首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >Node.js、Socket.io、Redis发布/订阅高容量、低延迟困难

Node.js、Socket.io、Redis发布/订阅高容量、低延迟困难
EN

Stack Overflow用户
提问于 2012-05-12 03:58:53
回答 1查看 28.5K关注 0票数 68

当连接socket.io/node.js和redis pub/sub试图创建一个由服务器事件驱动的实时web广播系统时,可以处理多个传输,似乎有三种方法:

  1. 'createClient‘一个redis连接并订阅频道。在socket.io客户端连接上,将客户端加入socket.io房间。在redis.on("message",...)event,调用io.sockets.in(房间).emit(“event”,data)下发给对应房间内的所有客户端。就像How to reuse redis connection in socket.io?
  2. 'createClient‘一样,它与redis有联系。在socket.io客户端连接上,将客户端加入到socket.io房间,并订阅相关的redis频道。包含消息(“redis.on”,...)在客户端连接闭包内部,并在收到消息时调用client.emit(" event ",data)以在特定客户端上引发事件。就像Examples in using RedisStore in socket.io
  3. Use中的答案一样,RedisStore被烘焙到socket.io中,并在Redis中从单一的“调度”频道‘广播’,遵循套接字规范协议。

编号1允许为所有客户端处理一次Redis子事件和相关事件。#2提供了一个到Redis pub/sub的更直接的钩子。第三种方法更简单,但对消息传递事件几乎没有提供控制。

然而,在我的测试中,所有的都表现出出乎意料的低性能,超过1个连接的客户端。正在讨论的服务器事件是将1000条消息尽快发布到redis通道,以便尽快分发。性能是通过连接的客户端上的时间来衡量的(基于Socket.IO的客户端,将时间戳记录到Redis列表中进行分析)。

我猜测,在选项1中,服务器接收消息,然后按顺序将其写入所有连接的客户端。在选项2中,服务器多次接收每条消息(每个客户端订阅一次),并将其写入相关客户端。在任何一种情况下,服务器都不会到达第二个消息事件,直到它被传递给所有连接的客户端。这种情况显然随着并发性的增加而加剧。

这似乎与人们对堆栈功能的认知不符。我想相信,但我在挣扎。

这种情况(大量消息的低延迟分布)不是这些工具的一个选择吗(还没有?),还是我错过了一个技巧?

EN

回答 1

Stack Overflow用户

发布于 2012-06-14 05:31:51

我认为这是一个合理的问题,并在一段时间前对其进行了简短的研究。我花了一点时间寻找一些例子,也许你能从中学到一些有用的技巧。

示例

我喜欢从直截了当的例子开始:

简单的示例是一个页面(请注意,您需要将redis-node-client替换为Matt Ranney的node_redis

代码语言:javascript
复制
/*
 * 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在这种类型的堆栈上变化很快,所以您必须权衡每个文档的时间相关性。

相关问题

只有几个相关的问题,这是堆栈上的一个热门话题:

注意事项(ymmv)

关闭或优化套接字池,使用有效的绑定,监控延迟,并确保您没有重复工作(即不需要向所有侦听器发布两次)。

票数 30
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/10557826

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档