当连接socket.io/node.js和redis pub / sub以尝试创建由可处理多个传输的服务器事件驱动的实时Web广播系统时,似乎有三种方法:
'createClient'是一个redis连接并订阅了频道。在socket.io客户端连接上,将客户端加入socket.io会议室。在redis.on(“message”,...)事件中,调用io.sockets.in(room).emit(“event”,data)以分发给相关房间中的所有客户端。与How to reuse redis connection in socket.io?
'createClient'是一个redis连接。在socket.io客户端连接上,将客户端加入socket.io会议室并订阅相关的redis频道。在客户端连接闭包内和收到消息调用client.emit(“event”,data)时包含redis.on(“message”,...)以在特定客户端上引发事件。与Examples in using RedisStore in socket.io
根据socketio-spec协议,使用Redis中的RedisStore和来自Redis中单个“dispatch”频道的'broadcast'。
Number 1允许为所有客户端处理一次Redis子和相关事件。 Number 2为Redis pub / sub提供了更直接的钩子。 3号更简单,但几乎无法控制消息传递事件。
但是,在我的测试中,所有连接的客户端都表现出意外的低性能。有问题的服务器事件是尽快发布到redis频道的1,000条消息,以便尽快分发。性能是通过连接客户端的时间来衡量的(socket.io-client基于该日志时间戳到Redis列表中进行分析)。
我猜测在选项1中,服务器接收消息,然后顺序将其写入所有连接的客户端。在选项2中,服务器多次接收每个消息(每个客户端订阅一次)并将其写入相关客户端。在任何一种情况下,服务器都不会进入第二个消息事件,直到它被传递给所有连接的客户端。随着并发性的上升,情况明显加剧。
这似乎与堆栈功能的感知智慧不一致。我想相信,但我在苦苦挣扎。
这种情况(大量消息的低延迟分布)是不是这些工具的选项(还是?),还是我错过了一招?
答案 0 :(得分:30)
我认为这是一个合理的问题,并且暂时对其进行了短暂的研究。我花了一点时间搜索一些例子,你可以从中获取一些有用的提示。
我喜欢从直接的例子开始:
光样本是单页(注意你要用Matt Ranney用node_redis替换redis-node-client:
/*
* Mclarens Bar: Redis based Instant Messaging
* Nikhil Marathe - 22/04/2010
* A simple example of an IM client implemented using
* Redis PUB/SUB commands so that all the communication
* is offloaded to Redis, and the node.js code only
* handles command interpretation,presentation and subscribing.
*
* Requires redis-node-client and a recent version of Redis
* http://code.google.com/p/redis
* http://github.com/fictorial/redis-node-client
*
* Start the server then telnet to port 8000
* Register with NICK <nick>, use WHO to see others
* Use TALKTO <nick> to initiate a chat. Send a message
* using MSG <nick> <msg>. Note its important to do a
* TALKTO so that both sides are listening. Use STOP <nick>
* to stop talking to someone, and QUIT to exit.
*
* This code is in the public domain.
*/
var redis = require('./redis-node-client/lib/redis-client');
var sys = require('sys');
var net = require('net');
var server = net.createServer(function(stream) {
var sub; // redis connection
var pub;
var registered = false;
var nick = "";
function channel(a,b) {
return [a,b].sort().join(':');
}
function shareTable(other) {
sys.debug(nick + ": Subscribing to "+channel(nick,other));
sub.subscribeTo(channel(nick,other), function(channel, message) {
var str = message.toString();
var sender = str.slice(0, str.indexOf(':'));
if( sender != nick )
stream.write("[" + sender + "] " + str.substr(str.indexOf(':')+1) + "\n");
});
}
function leaveTable(other) {
sub.unsubscribeFrom(channel(nick,other), function(err) {
stream.write("Stopped talking to " + other+ "\n");
});
}
stream.addListener("connect", function() {
sub = redis.createClient();
pub = redis.createClient();
});
stream.addListener("data", function(data) {
if( !registered ) {
var msg = data.toString().match(/^NICK (\w*)/);
if(msg) {
stream.write("SERVER: Hi " + msg[1] + "\n");
pub.sadd('mclarens:inside', msg[1], function(err) {
if(err) {
stream.end();
}
registered = true;
nick = msg[1];
// server messages
sub.subscribeTo( nick + ":info", function(nick, message) {
var m = message.toString().split(' ');
var cmd = m[0];
var who = m[1];
if( cmd == "start" ) {
stream.write( who + " is now talking to you\n");
shareTable(who);
}
else if( cmd == "stop" ) {
stream.write( who + " stopped talking to you\n");
leaveTable(who);
}
});
});
}
else {
stream.write("Please register with NICK <nickname>\n");
}
return;
}
var fragments = data.toString().replace('\r\n', '').split(' ');
switch(fragments[0]) {
case 'TALKTO':
pub.publish(fragments[1]+":info", "start " + nick, function(a,b) {
});
shareTable(fragments[1]);
break;
case 'MSG':
pub.publish(channel(nick, fragments[1]),
nick + ':' +fragments.slice(2).join(' '),
function(err, reply) {
if(err) {
stream.write("ERROR!");
}
});
break;
case 'WHO':
pub.smembers('mclarens:inside', function(err, users) {
stream.write("Online:\n" + users.join('\n') + "\n");
});
break;
case 'STOP':
leaveTable(fragments[1]);
pub.publish(fragments[1]+":info", "stop " + nick, function() {});
break;
case 'QUIT':
stream.end();
break;
}
});
stream.addListener("end", function() {
pub.publish(nick, nick + " is offline");
pub.srem('mclarens:inside', nick, function(err) {
if(err) {
sys.debug("Could not remove client");
}
});
});
});
server.listen(8000, "localhost");
那里有大量的文档,而且apis在这种类型的堆栈上正在快速变化,所以你必须权衡每个文档的时间相关性。
只是一些相关的问题,这是堆栈上的一个热门话题:
关闭或优化套接字池,使用高效绑定,监控延迟,并确保不重复工作(即无需向所有侦听器发布两次)。