2013-02-18 9 views
28

Ziemlich einfache Frage. Ich baue ein Echtzeitspiel mit nodejs als Backend und frage mich, ob es Informationen darüber gibt, welche zuverlässiger und welche effizienter ist. Ich benutze Redis und Socket.io in meinem Code. Also ich möchte wissen, ob ich Socket.io Rooms verwenden sollte oder ich wäre besser dran mit redis 'pub-sub?Was soll ich verwenden? Socket.io Zimmer oder Redis Pub-Sub?

Update: Nur realisiert, es gibt einen sehr wichtigen Grund, warum Sie Redis Pub/Sub über Socket.io Zimmer verwenden möchten. Bei Socket.io-Räumen empfangen die (Browser-) Clients beim Veröffentlichen in Listenern die Nachricht, bei Redis sind es tatsächlich die Clients, die Nachrichten empfangen. Wenn Sie also alle (Server-) Clients mit Informationen versorgen möchten, die für jeden Client spezifisch sind, und vielleicht einige Verarbeitungen durchführen, bevor Sie sie an Browser-Clients weitergeben, sollten Sie redis besser verwenden. Mit redis können Sie einfach ein Ereignis auslösen, um die individuellen Daten jedes Benutzers zu generieren. Dabei müssen Sie wie bei socket.io alle eindeutigen Daten des Benutzers auf einmal erzeugen, diese durchlaufen und ihnen ihre individuellen Daten senden, die die Daten fast vereiteln Zweck von Räumen, zumindest für mich.

Leider für meine Zwecke bin ich mit Redis für jetzt fest.

Update 2: Ended ein Plugin Entwicklung nur bis zu 2 redis Verbindungen zu verwenden, aber immer noch für einzelne Client-Verarbeitung erlauben, siehe weiter unten beantworten ....

+2

interessante Frage, ich würde auch gerne wissen. wahrscheinlich würde dieser Beitrag eine Hilfe sein: http://StackOverflow.com/Questions/10167206/Redis-Pub-Sub-Or-Socket-IOS-broadcast – yuwang

+0

Danke für den Link, der letzte Beitrag macht einen guten Punkt. Möglicherweise weniger skalierbar für die Verwendung von socket.io, da der (Prozess-) Umfang eingeschränkt sein könnte. –

+1

Kann jemand die Unterschiede erklären? Details wären nett. – user568109

Antwort

29

Redis pub/sub ist groß, falls alle Clients Direkter Zugang zu Redis. Wenn Sie mehrere Knotenserver haben, können Sie eine Nachricht an die anderen senden.

Aber wenn Sie auch Clients im Browser haben, brauchen Sie etwas anderes, um Daten von einem Server auf einen Client zu übertragen, und in diesem Fall ist socket.io großartig.

Wenn Sie nun socket.io mit dem Redis-Speicher verwenden, verwendet socket.io Redis pub/sub unter der Haube, um Nachrichten zwischen Servern zu verteilen, und Server werden Nachrichten an Clients weitergeben.

Die Verwendung von socket.io-Räumen mit socket.io, die mit dem Redis-Speicher konfiguriert wurden, ist wahrscheinlich die einfachste für Sie.

+0

In meinem aktuellen Setup verwende ich redisStore für socket.io und redisiere pub-sub, aber als Ergebnis jeden Client, der über socket.io verbindet, muss ich eine entsprechende Redis-Verbindung erstellen. Wenn ich in Socket.io-Räume umschalte, wird es immer noch eine separate Redis-Verbindung für jeden Benutzer unter der Haube verwenden? (Ich nehme an, wird es?) –

+2

RedisStore verwendet insgesamt 3 Verbindungen (Clients) pro Knoteninstanz. Es erstellt keine neuen Verbindungen pro Clientverbindung oder pro Raum und sendet diese an den richtigen Raum selbst. –

+0

Richtig, also würde die Verwendung von Räumen in meinem Fall die Effizienz drastisch verbessern, da ich derzeit einen Redis Client für jeden Client verwende (innerhalb des Socket.io onconnection Callback)? –

6

Ich schrieb ein Knoten-Plugin für viele Pub-Sub-Clients, aber nur 2 Redis-Verbindungen statt einer neuen auf jeder einzelnen Socket-Verbindung erforderlich, sollte es im Allgemeinen funktionieren, dachte jemand anders kann dafür Verwendung finden .

Dieser Code ging davon aus, dass Sie socket.io ausführen und einrichten, im Grunde können in diesem Beispiel beliebig viele socket.io Clients eine Verbindung herstellen und es werden immer nur 2 redis-Verbindungen verwendet, aber alle Clients können ihre eigenen Kanäle abonnieren. In diesem Beispiel erhalten alle Clients eine Nachricht "süße Nachricht!" nach 10 Sekunden.

Beispiel mit socket.io (redis pub-sub Nutzung):

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; 

ich muckte um und versuchte, die Effizienz so viel wie ich konnte zu verbessern, aber nachdem ich verschiedene Geschwindigkeitstests durchgeführt hatte, kam ich zu dem Schluss, dass dies der schnellste war, den ich bekommen konnte.

Für up-to-date-Version: https://github.com/Jezternz/node-redis-pubsub

Verwandte Themen