6

Question based on this answer: https://stackoverflow.com/a/18650183/4478897

I tried to find this solution but nothing seems to work in the way that I need.

Clustering expressjs and socket.io we can share sessions using redis and send io messages inside io world (io.sockets.on('connection',...). The problem is if we want to send the message (or use a simple socket.join/leave) inside the expressjs world (route.get/post).

If we are not using clusters we can atach the client socket object to the express request object (or simply export the io object) and then use it at any time on any GET/POST route.

At the other hand, if we are clustering and use the mentioned method to get the socket object inside the expressjs world, sometimes the socket object is undefined because the socket object for this client is initialized at other worker.

Some example flow:

  • Client connects to http://localhost and worker 1 handles this request.
  • After the page is loaded, the client connects to socket.io. Worker 2 handles this connection.
  • Client do a POST and again worker 1 or worker X handles this request.

In this case when the client do the POST, only the worker 2 knows the socket object for this client. So this will get an undefined socket object.

So, the question:

How can we get the client socket object from any worker to reuse it on expressjs request object.

Maybe my code is wrong but is almost like the link to the answer mentioned above.


NOTEs

  • Don't want to use some kind of proxy.
  • Don't want to migrate to other libraries (expressio, sockjs...)
  • Sorry for my English :)

Using last nodejs, socket.io, expressjs, socket.io-redis, redis... versions

Don't hesitate to ask something!


UPDATE 1

Possible solution but still need to test it. Dont know if this is a really good: solution.

  • UPDATE 3: Working code on my own answer

UPDATE 2

Like update 1 but using https://nodejs.org/dist/latest-v5.x/docs/api/cluster.html#cluster_event_message

Community
  • 1
  • 1
nada
  • 972
  • 5
  • 22

2 Answers2

1

remoteJoin and remoteLeave methods were added in socket.io-redis 3.0.0:

io.adapter.remoteJoin('<my-id>', 'room1', function (err) {
  if (err) { /* unknown id */ }
  // success
});

io.adapter.remoteLeave('<my-id>', 'room1', function (err) {
  if (err) { /* unknown id */ }
  // success
});

Note: The implementation looks a lot (hopefully?) like the answer above.

Community
  • 1
  • 1
darrachequesne
  • 742
  • 7
  • 18
-1

Well finally tried the code and it works (with some misspells modifications and other things) but i'm sure that needs to be a better code somewhere. So i'm open to more answers!

This code is part of my socket.io module when authorize the client socket and some other stuff...

  var redis = require("redis");
  var redisPub = redis.createClient();
  var redisSub = redis.createClient();
  var PubSubChannel = "clusterChannel";

  // Function that checks if this worker knows the socket object of this socketId.
  // If not, publish the message to all the other sockets (workers)
  io.socketDo = function (type, socketId, roomName) {
    if (typeof io.sockets.connected[socketId] != "undefined") {
      if (type === "join") {
        return io.sockets.connected[socketId].join(roomName);
      }
      if (type === "leave") {
        return io.sockets.connected[socketId].leave(roomName);
      }
    } else {
      redisPub.publish(
        PubSubChannel,
        JSON.stringify({
          type: type,
          socketId: '' + socketId,
          roomName: roomName
        })
      );
    }
  };

  // Subscribe to some channel
  redisSub.subscribe(PubSubChannel);

  // When this worker receive a message from channel "PubSubChannel" checks
  // if it have the socket object for this socketId and do the operation
  redisSub.on("message", function (channel, data) {
    data = JSON.parse(data);
    var type = data.type;
    var socketId = data.socketId;
    var roomName = data.roomName;
    if ((type === "join" || type === "leave") && channel == PubSubChannel){
      if (typeof io.sockets.connected[socketId] != "undefined") {
        if (type === "join") {
          return io.sockets.connected[socketId].join(roomName);
        }
        if (type === "leave") {
          return io.sockets.connected[socketId].leave(roomName);
        }
      }
    }
  });

Then just simply export the module and attach it to your expressjs request => req.io = io

// req.session.socketId value is fetched on "io.sockets.on('connection', function(socket) {" 
// by express to socket.io using redis shared sessions
app.get('/', function (req, res) {
    req.io.socketDo('join', req.session.socketId, 'someRoomToJoin');

    // IT WORKS!
    req.io.sockets.in('someRoomToJoin').emit('text'); 

    req.io.socketDo('leave', req.session.socketId, 'someRoomToLeave');
    res.send('Hello World!');
});
nada
  • 972
  • 5
  • 22