node.js 我应该使用什么?Socket.io 房间还是 Redis 发布订阅?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/14929700/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
What should I be using? Socket.io rooms or Redis pub-sub?
提问by Josh Mc
Pretty simple question. I am building a realtime game using nodejs as my backend and I am wondering if there is any information available on which one is more reliable and which one is more efficient? I am heavily using both Redis and Socket.io throughout my code. So I want to know whether I should be utilizing Socket.io's Roomsor I would be better off using redis' pub-sub?
很简单的问题。我正在使用 nodejs 作为我的后端构建一个实时游戏,我想知道是否有任何可用的信息来说明哪个更可靠,哪个更有效?我在整个代码中大量使用 Redis 和 Socket.io。所以我想知道我是应该使用 Socket.io 的Rooms还是使用 redis 的pub-sub会更好?
Update:Just realized there is a very important reason why you may want to use redis pub/sub over socket.io rooms. With Socket.io rooms when you publish to listeners, the (browser)clients recieve the message, with redis it is actually the (redis~on server)clients who recieve messages. For this reason, if you want to inform all (server)clients of information specific to each client and maybe do some processing before passing on to browser clients, you are better off using redis. Using redis you can just fire off an event to generate each users individual data, where as with socket.io you have to actually generate all the users unique data at once, then loop through them and send them their individual data, which almost defeats the purpose of rooms, at least for me.
更新:刚刚意识到您可能想要在 socket.io 房间上使用 redis pub/sub 的一个非常重要的原因。使用 Socket.io 房间,当您发布到侦听器时,(浏览器)客户端接收消息,而使用 redis,实际上是(redis~on 服务器)客户端接收消息。出于这个原因,如果您想通知所有(服务器)客户端特定于每个客户端的信息,并且可能在传递给浏览器客户端之前进行一些处理,那么最好使用 redis。使用 redis,您可以触发一个事件来生成每个用户的个人数据,而与 socket.io 一样,您必须一次实际生成所有用户的唯一数据,然后遍历它们并向他们发送他们的个人数据,这几乎打败了房间的目的,至少对我来说。
Unfortunately for my purposes I am stuck with redis for now.
不幸的是,出于我的目的,我现在坚持使用 redis。
Update 2:Ended up developing a plugin to use only 2 redis connections but still allow for individual client processing, see answer below....
更新 2:最终开发了一个插件以仅使用 2 个 redis 连接,但仍允许单个客户端处理,请参阅下面的答案....
回答by Pascal Belloncle
Redis pub/sub is great in case all clients have direct access to redis. If you have multiple node servers, one can push a message to the others.
Redis pub/sub 非常适合所有客户端都可以直接访问 redis 的情况。如果您有多个节点服务器,则可以将消息推送给其他节点。
But if you also have clients in the browser, you need something else to push data from a server to a client, and in this case, socket.io is great.
但是如果浏览器中也有客户端,则需要其他东西将数据从服务器推送到客户端,在这种情况下,socket.io 很棒。
Now, if you use socket.io with the Redis store, socket.io will use Redis pub/sub under the hood to propagate messages between servers, and servers will propagate messages to clients.
现在,如果您将 socket.io 与 Redis 存储一起使用,socket.io 将在后台使用 Redis pub/sub 在服务器之间传播消息,而服务器将向客户端传播消息。
So using socket.io rooms with socket.io configured with the Redis store is probably the simplest for you.
因此,使用带有 Redis 存储配置的 socket.io 的 socket.io 房间对您来说可能是最简单的。
回答by Josh Mc
I ended up writing a node plugin to allow for many pub-sub clients but only require 2 redis connections instead of a new one on every single socketio connection, it should work in general, figured someone else may find use for it.
我最终编写了一个节点插件来允许许多发布订阅客户端,但只需要 2 个 redis 连接而不是每个 socketio 连接上的一个新连接,它应该可以正常工作,认为其他人可能会发现它有用。
This code assumed you have socket.io running and setup, basically in this example any number of socket.io clients can connect and it will always still only use 2 redis connections, but all clients can subscribe to their own channels. In this example, all clients get a message 'sweet message!' after 10 seconds.
这段代码假设你已经运行和设置了 socket.io,基本上在这个例子中,任何数量的 socket.io 客户端都可以连接,并且它始终只使用 2 个 redis 连接,但所有客户端都可以订阅他们自己的频道。在这个例子中,所有客户端都会收到一条消息“甜蜜的消息!” 10 秒后。
Example with socket.io (utilizing redis pub-sub):
socket.io 示例(使用 redis pub-sub):
var
RPubSubFactory = require('rpss.js');
var
redOne = redis.createClient(port, host),
redTwo = redis.createClient(port, host);
var pSCFactory = new RPubSubFactory(redOne);
io.sockets.on('connection', function(socket){
var cps = pSCFactory.createClient();
cps.onMessage(function(channel, message){
socket.emit('message', message);
});
io.sockets.on('disconnect', function(socket){
// Dont actually need to unsub, because end() will cleanup all subs,
// but if you need to sometime during the connection lifetime, you can.
cps.unsubscribe('cool_channel');
cps.end();
});
cps.subscribe('cool_channel')
});
setTimeout(function(){
redTwo.publish('cool_channel', 'sweet message!');
},10000);
Actual plugin code:
实际插件代码:
var RPubSubFactory = function(){
var
len,indx,tarr;
var
dbcom = false,
rPubSubIdCounter = 1,
clientLookup = {},
globalSubscriptions = {};
// public
this.createClient = function()
{
return new RPubSupClient();
}
// private
var constructor = function(tdbcom)
{
dbcom = tdbcom;
dbcom.on("message", incommingMessage);
}
var incommingMessage = function(rawchannel, strMessage)
{
len = globalSubscriptions[rawchannel].length;
for(var i=0;i<len;i++){
//console.log(globalSubscriptions[rawchannel][i]+' incomming on channel '+rawchannel);
clientLookup[globalSubscriptions[rawchannel][i]]._incommingMessage(rawchannel, strMessage);
}
}
// class
var RPubSupClient = function()
{
var
id = -1,
localSubscriptions = [];
this.id = -1;
this._incommingMessage = function(){};
this.subscribe = function(channel)
{
//console.log('client '+id+' subscribing to '+channel);
if(!(channel in globalSubscriptions)){
globalSubscriptions[channel] = [id];
dbcom.subscribe(channel);
}
else if(globalSubscriptions[channel].indexOf(id) == -1){
globalSubscriptions[channel].push(id);
}
if(localSubscriptions.indexOf(channel) == -1){
localSubscriptions.push(channel);
}
}
this.unsubscribe = function(channel)
{
//console.log('client '+id+' unsubscribing to '+channel);
if(channel in globalSubscriptions)
{
indx = globalSubscriptions[channel].indexOf(id);
if(indx != -1){
globalSubscriptions[channel].splice(indx, 1);
if(globalSubscriptions[channel].length == 0){
delete globalSubscriptions[channel];
dbcom.unsubscribe(channel);
}
}
}
indx = localSubscriptions.indexOf(channel);
if(indx != -1){
localSubscriptions.splice(indx, 1);
}
}
this.onMessage = function(msgFn)
{
this._incommingMessage = msgFn;
}
this.end = function()
{
//console.log('end client id = '+id+' closing subscriptions='+localSubscriptions.join(','));
tarr = localSubscriptions.slice(0);
len = tarr.length;
for(var i=0;i<len;i++){
this.unsubscribe(tarr[i]);
}
localSubscriptions = [];
delete clientLookup[id];
}
var constructor = function(){
this.id = id = rPubSubIdCounter++;
clientLookup[id] = this;
//console.log('new client id = '+id);
}
constructor.apply(this, arguments);
}
constructor.apply(this, arguments);
};
module.exports = RPubSubFactory;
I mucked around and tried to improve the efficiency as much as I could, but after doing some different speed tests, I concluded this was the fastest I could get it.
我四处游荡并尽可能地提高效率,但在进行了一些不同的速度测试后,我得出结论,这是我能得到的最快的速度。
For up-to-date version: https://github.com/Jezternz/node-redis-pubsub

