Tiago Fabre Tiago Fabre - 1 month ago 21
Javascript Question

How can I Handle Socket.IO rooms with cluster?

I have a server working with cluster and to make that work with socke.IO I am using sticky-session, but I have a problem with my rooms (I don't know if the way I did is the best option): The cluster is instantiating processes and each process has a specific number of rooms.


  • Server


    • Process 1


      • Room1

      • Room2

      • Room N


    • Process 2


      • Room1

      • Room2

      • Room N





The way I did to connect some user to the rooms (with only one process) is using the route, where the user access a page and when he tries to make a connection with Socket.io I check the URL and with that information I insert him in a room.

My problem is implementing this server with cluster I can not insert the user in specific rooms because there is some rooms that only exist in specific processes and sticky session put him in another process. How can I put an user in a room that is in another process ? Also The use can only to see the routes of the process he is in the server and I would like to show every rooms in the page.

I already has read about Redis-Adapter but I didn't find solutions on github using Socket.io + Cluster(Sticky-session + redis-adapter) + rooms.

Follow my code to share what I have done:

//Cluster.Master with simplified Code
if (cluster.isMaster) {

var workers = [];
// Spawn workers.
for (var i = 0; i < num_processes; i++) {
spawn(i);
}

// Create the outside facing server listening on our port.
var server = net.createServer({
pauseOnConnect: true
}, function(connection) {
// We received a connection and need to pass it to the appropriate
// worker. Get the worker for this connection's source IP and pass
// it the connection.
var worker = workers[worker_index(connection.remoteAddress, num_processes)];
worker.send('sticky-session:connection', connection);
}).listen(process.env.PORT);
} else {
console.log('I am worker #' + cluster.worker.id);
var app = new express();

//view engine
app.set('views', './views');
app.set('view engine', 'pug');
//statics
app.use(express.static(path.join(__dirname, 'public')));
//rooms
app.use('/', rooms);
var server = app.listen(0, 'localhost'),
io = sio(server);
io.adapter(sio_redis({ host: 'localhost', port: 6379 }));

//This File has the socket events (socket.on('messageX', function(){}))
// And there I am
var realtime = require('./realtime/socketIOEvents.js')(io);

// Listen to messages sent from the master. Ignore everything else.
process.on('message', function(message, connection) {
if (message !== 'sticky-session:connection') {
return;
}
// Emulate a connection event on the server by emitting the
// event with the connection the master sent us.
server.emit('connection', connection);
connection.resume();
});
}

Answer

socketio-redis is a right thing to do. Every sserver/process listens to a topic in redis queue. To understand in short socketio-redis just publishes the event to every other server/process in cluster. So as far as the Rooms are concern they are just an abstraction to a group of sockets interested in listening to messages in the room.

Even if sockets are distributed to different servers/processes they can be a part of same room. As every message comes in, every server knows about it and passes on the to required sockets.

As far as correctness is concern, Your architecture is right also correct as your proxy decides also selectively forwards the message but it is increasing hops in a lifecycle of a message. You really do not need this proxy to handle socket routing.