11
votes

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();
});
}
3
The redis connector for socket.io is designed to solve this problem.jfriend00
Do you have a better example how to use that? The documentation of SocketIO and socketio-redis are very bad :/Tiago Fabre
I already saw the documentation and it is not helping me since it the redis examples are just sharing events between servers... As I have a room management should I replicate the logic (user joining in the room X, userleft, user sent a message X,Y,Z ) in each server ?Tiago Fabre
If you use socket.io rooms, then you can add to a room and send messages to a room and the socket.io/redis adapter handles the multi-server thing for you automatically. It stores in redis which server each user is one and keeps the room list in redis so when you want to broadcast to a room, it goes to redis, gets the list of users in the room and then tells the server for each user to send to them. I think it's all solved for you already. If not, then you haven't described why the built in solution is not what you want or need.jfriend00

3 Answers

2
votes

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.

1
votes

Here is the example code which will worked based on the room joining with.

http://www.html5gamedevs.com/topic/12321-create-a-cluster-server-with-nodejs-and-socketio/

Let me know if you have question in this post.

1
votes

After a lot of tries I did this with a Proxy server in the top of the workers and every workers registering rooms in another server (Redis). When I connect to the server with the URL of a room, my proxy server detects which worker I am trying to connect and route my web socket connection to the right worker.

                      Proxy
           /            |          \
worker(5rooms)   worker(5rooms)  worker(5rooms)
           \            |          /
                      Redis

That solves my problem but it is not using socket.io-redis. How can we solve that problem with socket.io-redis? Add every users, sockets and variables in redis to make just the processing in the workers is a valid approach ?