socket.io+redis+expressjs集群-在expressjs请求中获取socket对象
socket.io+redis+expressjs cluster - get socket object in expressjs request
基于此答案的问题:
我试图找到这个解决方案,但似乎没有我需要的方法。
集群 expressjs
和 socket.io
我们可以使用 Redis 共享会话并在 io
世界(io.sockets.on('connection',
...)中发送 io
消息。问题是如果我们想在 expressjs
世界 (route.get/post
).
中发送消息(或使用简单的 socket.join/leave
)
如果我们不使用集群,我们可以将客户端 socket
对象附加到 express request
对象(或简单地 export
io
对象),然后使用随时在任何 GET/POST 路线上。
另一方面,如果我们正在聚类并使用上述方法在 expressjs
世界中获取 socket
对象,有时 socket
对象是未定义的,因为 socket
此客户端的对象在其他 worker
.
处初始化
一些示例流程:
- 客户端连接到 http://localhost 并且
worker 1
处理此请求。
- 页面加载后,客户端连接到
socket.io
。 Worker 2
处理此连接。
- 客户端执行 POST 并再次
worker 1
或 worker X
处理此请求。
在这种情况下,当客户端执行 POST 时,只有 worker 2
知道此客户端的 socket
对象。所以这将得到一个未定义的 socket
对象。
所以,问题:
我们如何从任何 worker
获取客户端 socket
对象以在 expressjs request
对象上重用它。
也许我的代码有误,但与上述答案的 link 差不多。
备注
- 不想使用某种代理。
- 不想迁移到其他库(expressio、sockjs...)
- 对不起我的英语:)
使用最后的nodejs,socket.io,expressjs,socket.io-redis,redis...版本
不要犹豫,问问题!
更新 1
可能的解决方案,但仍需测试。不知道这是否是一个很好的解决方案。
- 更新 3:我自己回答的工作代码
更新 2
与更新 1 类似,但使用 https://nodejs.org/dist/latest-v5.x/docs/api/cluster.html#cluster_event_message
好吧,终于尝试了代码并且它可以工作(有一些拼写错误的修改和其他东西)但我确信在某个地方需要更好的代码。所以我愿意接受更多答案!
此代码是我的 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!');
});
remoteJoin
和 remoteLeave
方法已添加到 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
});
注意:实施看起来很像(希望如此?)答案 。
基于此答案的问题:
我试图找到这个解决方案,但似乎没有我需要的方法。
集群 expressjs
和 socket.io
我们可以使用 Redis 共享会话并在 io
世界(io.sockets.on('connection',
...)中发送 io
消息。问题是如果我们想在 expressjs
世界 (route.get/post
).
socket.join/leave
)
如果我们不使用集群,我们可以将客户端 socket
对象附加到 express request
对象(或简单地 export
io
对象),然后使用随时在任何 GET/POST 路线上。
另一方面,如果我们正在聚类并使用上述方法在 expressjs
世界中获取 socket
对象,有时 socket
对象是未定义的,因为 socket
此客户端的对象在其他 worker
.
一些示例流程:
- 客户端连接到 http://localhost 并且
worker 1
处理此请求。 - 页面加载后,客户端连接到
socket.io
。Worker 2
处理此连接。 - 客户端执行 POST 并再次
worker 1
或worker X
处理此请求。
在这种情况下,当客户端执行 POST 时,只有 worker 2
知道此客户端的 socket
对象。所以这将得到一个未定义的 socket
对象。
所以,问题:
我们如何从任何 worker
获取客户端 socket
对象以在 expressjs request
对象上重用它。
也许我的代码有误,但与上述答案的 link 差不多。
备注
- 不想使用某种代理。
- 不想迁移到其他库(expressio、sockjs...)
- 对不起我的英语:)
使用最后的nodejs,socket.io,expressjs,socket.io-redis,redis...版本
不要犹豫,问问题!
更新 1
可能的解决方案,但仍需测试。不知道这是否是一个很好的解决方案。
- 更新 3:我自己回答的工作代码
更新 2
与更新 1 类似,但使用 https://nodejs.org/dist/latest-v5.x/docs/api/cluster.html#cluster_event_message
好吧,终于尝试了代码并且它可以工作(有一些拼写错误的修改和其他东西)但我确信在某个地方需要更好的代码。所以我愿意接受更多答案!
此代码是我的 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!');
});
remoteJoin
和 remoteLeave
方法已添加到 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
});
注意:实施看起来很像(希望如此?)答案