Socket.io + redis + expressjs cluster - get socket object in expressjs expression

Question based on this answer: stack overflow

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

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

If we do not use clusters, we can attack the client socket object for the express request object (or simply the export io object), and then use it at any time on any GET / POST route.

On the other hand, if we are clusters 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 in another worker .

Stream example:

  • The client connects to http: // localhost and worker 1 processes this request.
  • After loading the page, the client connects to socket.io . Worker 2 processes this connection.
  • The client performs a POST, and then worker 1 or worker X processes this request.

In this case, when the client performs POST, only Worker 2 knows the socket object for this client. This way it will get an undefined socket object.

So the question is:

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

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


Notes

  • Do not want to use some kind of proxy.
  • Do not want to switch to other libraries (expressio, sockjs ...)
  • Sorry for my English:)

Using the latest nodejs, socket.io, expressjs, socket.io-redis, redis ... versions

Feel free to ask something!


UPDATE 1

Possible solution, but still need to test it. I don't know if this is really good: a solution.

  • UPDATE 3 : working code on own answer

UPDATE 2

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

+5
source share
2 answers

Well, finally, I tried the code, and it works (with some error modifications and other things), but I'm sure there should be a better code somewhere. Therefore, I am open to receiving additional answers!

This code is part of my socket.io module when authorizing a client socket and some other ...

  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 just 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!'); }); 
-1
source
Methods have been added to .io-redis 3.0.0 socket

remoteJoin and remoteLeave :

 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)? as the answer above .

+1
source

Source: https://habr.com/ru/post/1242695/


All Articles