Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Socket.io and multiple Dyno's on Heroku Node.js app. WebSocket is closed before the connection is established

Tags:

I'm building an App deployed to Heroku which uses Websockets.

The websockets connection is working properly when I use only 1 dyno, but when I scale to >1, I get the following errors

POST http://****.herokuapp.com/socket.io/?EIO=2&transport=polling&t=1412600135378-1&sid=zQzJJ8oPo5p3yiwIAAAC 400 (Bad Request) socket.io-1.0.4.js:2

WebSocket connection to 'ws://****.herokuapp.com/socket.io/?EIO=2&transport=websocket&sid=zQzJJ8oPo5p3yiwIAAAC' failed: WebSocket is closed before the connection is established. socket.io-1.0.4.js:2

I am using the Redis adaptor to enable multiple web processes

var io = socket.listen(server); var redisAdapter = require('socket.io-redis'); var redis = require('redis');  var pub = redis.createClient(18049, '[URI]', {auth_pass:"[PASS]"}); var sub = redis.createClient(18049, '[URI]', {detect_buffers: true, auth_pass:"[PASS]"} );  io.adapter( redisAdapter({pubClient: pub, subClient: sub}) ); 

This is working on localhost (which I am using foreman to run, as Heroku does, and I am launching 2 web processes, same as on Heroku).

Before I implemented the redis adaptor I got a web-sockets handshake error, so the adaptor has had some effect. Also it is working occasionally now, I assume when the sockets match the same web dyno.

I have also tried to enable sticky sessions, but then it never works.

var sticky = require('sticky-session'); sticky(1, server).listen(port, function (err) {   if (err) {     console.error(err);     return process.exit(1);   }   console.log('Worker listening on %s', port); }); 
like image 994
Jack Wild Avatar asked Oct 06 '14 13:10

Jack Wild


2 Answers

I'm the Node.js Platform Owner at Heroku.

WebSockets works on Heroku out-of-the-box across multiple dynos; socket.io (and other realtime libs) use fallbacks to stateless processes like xhr polling that break without session affinity.

To scale up socket.io apps, first follow all the instructions from socket.io:

  • http://socket.io/docs/using-multiple-nodes/

Then, enable session affinity on your app (this is a free feature):

  • https://devcenter.heroku.com/articles/session-affinity
like image 79
hunterloftis Avatar answered Sep 30 '22 20:09

hunterloftis


I spent a while trying to make socket.io work in multi-server architecture, first on Heroku and then on Openshift as many suggest.

The only way to make it work on both PAAS is disabling xhr-polling and setting transports: ['websocket'] on both client and server.

On Openshift, you must explicitly set the port of the server to 8000 (for ws – 8443 for wss on socket.io client initialization, using the *.rhcloud.com server, as explained in this post: http://tamas.io/deploying-a-node-jssocket-io-app-to-openshift/.

Polling strategy doesn't work on Heroku because it does not support sticky sessions (https://github.com/Automattic/engine.io/issues/261), and on Openshift it fails because of this issue: https://github.com/Automattic/engine.io/issues/279, that will hopefully be fixed soon.

So, the only solution I found so far, is disabling polling and use websocket transport only.

In order to do that, with socket.io > 1.0 server-side:

var app = express(); var server = require('http').createServer(app);  var socketio = require('socket.io')(server, {   path: '/socket.io-client' }); socketio.set('transports', ['websocket']); 

client-side:

var ioSocket = io('<your-openshift-app>.rhcloud.com:8000' || '<your-heroku-app>.herokuapp.com', {     path: '/socket.io-client'     transports: ['websocket'] }) 

Hope this will help.

like image 28
qubird Avatar answered Sep 30 '22 20:09

qubird