socket.io + redis + expressjs集群 – 在expressjs请求中获取套接字对象
基于这个答案的问题: https : //stackoverflow.com/a/18650183/4478897
我试图find这个解决scheme,但似乎没有任何工作,我需要的方式。
expressjs
和socket.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.io
。Worker 2
处理这个连接。 - 客户端执行一个POST,并且
worker 1
或worker 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
中添加了remoteJoin
和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 });
注意:执行看起来很多(希望?)就像上面的答案。
那么终于尝试了代码,它的工作原理(一些拼写错误修改和其他事情),但我敢肯定,需要是一个更好的代码的地方。 所以我打开更多的答案!
此代码是授权客户端套接字和其他一些东西时,我的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!'); });