4

We have the following setup for Google HTTPS load balancer.

Two Frontends: 1. HTTP traffic to static IP 2. HTTPS traffic to the same static IP(DNS configured to a domain name)

Host and Path rules All going to backend

One Backend: With HTTP protocol with session affinity set to client IP

The backend instance has a MEAN app running on port 3000.

From our client side application we are able to get through to backend app using loadbalancer domain name. But we also have a chat feature with socket.io

For socket connection we were not able to use loadbalancer domain name. It throws 400 error.

If we try to use the backend IP directly for socket connection, it works but the if the client is on HTTPS, it creates another problem because backend is http.

Google documentation says loadbalancer supports websockets by default. So not sure what is going on. All other examples I see are relatively old and not relevant I think. Any help is appreciated. Thanks.

Nunna Suma
  • 41
  • 3
  • Because socket.io starts out the connection with a couple http polling requests, the connection will regularly fail if you don't have sticky load balancing. So, you can either enable sticky load balancing in the load balancer (if that's a supported option) or configure the socket.io client to connect directly with webSocket and not use http polling at the start of the connection. – jfriend00 Apr 20 '18 at 04:31
  • 1
    Like I tried to explain, Google documentation clearly says HTTP(S) load balancing supports websockets without additional support. And I don't think there is a sticky option available on GCE. However there is session affinity and I tried that. – Nunna Suma Apr 20 '18 at 04:58
  • 1
    You apparently didn't understand my comment. A socket.io connection starts with several http requests before it switches to a webSocket connection. If those http requests aren't sticky and go to the same host every time, then the socket.io connection will fail. You can force the client to connect directly with a webSocket as the transport to avoid that or you can configure the load balancer to be sticky. You NEED one of those for a socket.io connection to work through a load balancer, even one that supports webSockets. socket.io is NOT plain webSocket, it's got stuff on top of that. – jfriend00 Apr 20 '18 at 05:00
  • See [Socket.io to use webSocket only](https://stackoverflow.com/questions/28238628/socket-io-1-x-use-websockets-only/28240802#28240802) for how to force webSocket transport from the beginning. – jfriend00 Apr 20 '18 at 05:02
  • Further to above comment, please note that as per [GCP documentation](https://cloud.google.com/compute/docs/load-balancing/http/#websocket_proxy_support), If you have configured either client IP or generated cookie session affinity for your HTTP(S) LB, all WebSocket connections from a client are sent to the same backend instance, provided the instance continues to pass health checks. – D Saini Apr 20 '18 at 23:28

2 Answers2

2

For WebSockets over an HTTP(S) load balancer, the Backend Service (response) timeout is a connection lifetime limit (WebSockets connections are killed after the configured response timeout). Hence, the timeout should be set to the maximum amount of time a WebSocket connection will remain open. The appropriate response timeout value is dependent on the application you use.

You will need to do some experiments to find an appropriate response timeout to avoid the connection timeout closures (increase its value slightly and retry; for example, if 30 seconds is insufficient, try 40sec,50 sec etc.).

Found on StackOverflow.

1

Herro, I have struggled with debugging this issue for 2 weeks.

@jfriend00 's comment provided providence for my plebian mind.

On nodejs deployment endpoint I have to pass the order of transports in the following:

  app = express()
  server = require('http').Server(app)# {key: tlskey, cert: tlscert},app)
  io = require('socket.io')(server, { transports: ['websocket', 'polling'], cookie:true, secure: true })
  app.use bodyParser.urlencoded(extended: true)

What is happening? I specify to socket.io to explicitly use the websocket transport. If it fails, please fallback to polling. Because the HTTPS GCE Ingress sets some headers (does some header upgrades to wss), I need the secure:true flag in order to match them on my backend. Without the above an error 400 occurs on the client side.

The service runs as a NodePort, with a Generate_Cookie.

If it still fails, try just transports: ['polling'], this is the most basic protocol.

Here is the link for the socket.io doc

MFC
  • 11
  • 2