2013-02-18 43 views
28

很简单的问题。我正在使用nodejs作为后端构建一个实时游戏,我想知道是否有任何信息可用于哪个更可靠,哪个更有效? 我在我的代码中大量使用了Redis和Socket.io。所以我想知道我是否应该使用Socket.io的Rooms或者我会更好使用redis'pub-sub我应该使用什么? Socket.io客房或Redis酒吧?

更新: 刚刚意识到有一个非常重要的原因,您可能想要使用redis pub/sub over socket.io房间。在发布给侦听器的情况下,使用Socket.io客房时,(浏览器)客户端会收到消息,其中redis实际上是接收消息的客户端(redis服务器)。因此,如果要通知所有(服务器)客户端特定于每个客户端的信息,并且在传递给浏览器客户端之前可能需要执行一些处理,则最好使用redis。使用redis,你可以触发一个事件来生成每个用户的个人数据,就像使用socket.io一样,你必须一次生成所有用户的唯一数据,然后循环遍历它们并发送它们的个人数据,这几乎会打败房间的目的,至少对我来说。

不幸的是,为了我的目的,我现在坚持使用redis。

更新2:端开发了一个插件,只使用2 Redis的连接,但仍然允许个别客户端处理,请参阅下面回答....

+2

有趣的问题,我也想知道。大概这个职位会有一些帮助:http://stackoverflow.com/questions/10167206/redis-pub-sub-or-socket-ios-broadcast – yuwang 2013-02-18 05:57:01

+0

感谢您的链接,最后一篇文章是一个很好的观点。由于(进程)范围可能更有限,因此可能无法使用socket.io进行扩展。 – 2013-02-18 06:22:11

+1

任何人都可以解释不同之处吗?细节会很好。 – user568109 2013-02-18 06:43:00

回答

29

Redis的发布/订阅的情况下,所有的客户都很大直接访问redis。如果您有多个节点服务器,则可以将消息推送给其他服务器。

但是,如果您在浏览器中也有客户端,则需要其他的东西将数据从服务器推送到客户端,在这种情况下,socket.io非常棒。

现在,如果您在Redis存储中使用socket.io,则socket.io将使用Redis pub/sub在服务器之间传播消息,并且服务器会将消息传播到客户端。

因此,使用带有Redis存储配置的socket.io的socket.io客房对您来说可能是最简单的。

+0

在我当前的设置中,我使用redisStore作为socket.io和redis pub-sub,但由于每个通过socket.io连接的客户端都需要创建相应的redis连接。如果我切换到Socket.io房间,它是否仍然为每个用​​户使用单独的redis连接? (我认为它会?) – 2013-02-20 00:14:17

+2

RedisStore每个节点实例总共使用3个连接(客户端)。它不会为每个客户端连接或每个房间创建新的连接,并将其分派到正确的房间本身。 – 2013-02-20 00:20:02

+0

没错,所以在我的情况下使用房间会大大提高效率,因为我目前为每个客户端使用一个redis客户端(在socket.io onconnection回调中)? – 2013-02-20 00:40:26

6

我最终编写了一个节点插件,允许多个pub-sub客户端,但只需要2个redis连接,而不是每个socketio连接上的新连接,它应该工作在一般情况下,其他人可能会找到它。

这段代码假设你有socket.io的运行和设置,基本上在这个例子中,任何数量的socket.io客户端都可以连接,并且它总是只能使用2个redis连接,但是所有的客户端都可以订阅自己的通道。在这个例子中,所有的客户都会收到一条消息“甜蜜的消息!” 10秒后。

实施例与socket.io(利用redis的发布 - 订阅):

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); 

实际插件代码:

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; 

我弄脏周围,并试图改进像我可以在效率,但是在做了一些不同的速度测试之后,我认为这是我能够获得的最快速度。

For latest version:https://github.com/Jezternz/node-redis-pubsub