2013-02-18 77 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