socket.io + redis + expressjs集群 – 在expressjs请求中获取套接字对象

基于这个答案的问题: https : //stackoverflow.com/a/18650183/4478897

我试图find这个解决scheme,但似乎没有任何工作,我需要的方式。

expressjssocket.io我们可以使用redis共享会话,并在io世界里发送io消息( io.sockets.on('connection', …)。问题是如果我们想发送消息(或者使用简单的socket.join/leave )在expressjs世界( route.get/post )中。

如果我们不使用集群,我们可以将客户端socket对象连接到表示request对象(或简单地export io对象),然后在任何GET / POST路由上随时使用它。

另一方面,如果我们正在使用上面提到的方法获取expressjs世界中的socket对象,有时socket对象是未定义的,因为此客户端的socket对象是在其他worker初始化的。

一些示例stream程:

  • 客户端连接到http:// localhost , worker 1处理这个请求。
  • 页面加载后,客户端连接到socket.ioWorker 2处理这个连接。
  • 客户端执行一个POST,并且worker 1worker X处理这个请求。

在这种情况下,当客户端执行POST时,只有worker 2知道该客户端的socket对象。 所以这将得到一个未定义的socket对象。

所以,这个问题:

我们如何从任何worker获取客户端socket对象来在expressjs request对象上重用它。

也许我的代码是错误的,但几乎就像上面提到的答案的链接。


笔记

  • 不想使用某种代理。
  • 不想迁移到其他库(expressio,sockjs …)
  • 对不起我的英语不好 :)

使用最后一个nodejs,socket.io,expressjs,socket.io-redis,redis …版本

不要犹豫,问问题!


更新1

可能的解决scheme,但仍然需要testing它。 不知道这是否是一个非常好的解决scheme。

  • 更新3 :工作代码在我自己的答案

更新2

像更新1,但使用https://nodejs.org/dist/latest-v5.x/docs/api/cluster.html#cluster_event_message

在socket.io-redis 3.0.0中添加了remoteJoinremoteLeave方法:

 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 }); 

注意:执行看起来很多(希望?)就像上面的答案。

那么终于尝试了代码,它的工作原理(一些拼写错误修改和其他事情),但我敢肯定,需要是一个更好的代码的地方。 所以我打开更多的答案!

此代码是授权客户端套接字和其他一些东西时,我的socket.io模块的一部分…

  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); } } } }); 

然后,只需简单地导出模块并将其附加到您的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!'); });