Que dois-je utiliser? Socket.io chambres ou Redis pub-sub?

Question assez simple. Je construis un jeu en temps réel en utilisant nodejs comme backend et je me demande s'il y a des informations disponibles sur lequel est le plus fiable et lequel est le plus efficace? J'utilise fortement Redis et Socket.io tout au long de mon code. Donc, je veux savoir si je devrais utiliser Socket.io's Rooms ou je ferais mieux d'utiliser redis' pub-sub ?

Mise à Jour: Je viens de réaliser qu'il y a une raison très importante pour laquelle vous pouvez vous voulez utiliser Redis pub / sub sur socket.io chambres. Avec Socket.io chambres lorsque vous publiez aux auditeurs, les clients (navigateur)reçoivent le message, avec redis ce sont en fait les clients (redis~sur le serveur)qui reçoivent les messages. Pour cette raison, si vous souhaitez informer tous les clients (serveur) des informations spécifiques à chaque client et peut-être effectuer un traitement avant de les transmettre aux clients du navigateur, il vaut mieux utiliser redis. En utilisant redis vous pouvez simplement déclencher un événement pour générer des données individuelles de chaque utilisateur, où comme avec socket.io vous devez réellement générer toutes les données uniques des utilisateurs à la fois, puis les parcourir et leur envoyer leurs données individuelles, ce qui va presque à l'encontre du but des chambres, du moins pour moi.

Malheureusement, pour mes besoins, je suis coincé avec redis pour l'instant.

Mise à jour 2: a fini par développer un plugin pour utiliser seulement 2 connexions redis mais toujours permettre un traitement client individuel, voir la réponse ci-dessous....

28
demandé sur Josh Mc 2013-02-18 08:53:32

2 réponses

Redis pub / sub est génial dans le cas où tous les clients ont un accès direct à redis. Si vous avez plusieurs serveurs de nœuds, on peut envoyer un message aux autres.

Mais si vous avez également des clients dans le navigateur, vous avez besoin d'autre chose pour pousser les données d'un serveur vers un client, et dans ce cas, socket.io c'est génial.

Maintenant, si vous utilisez socket.io avec le magasin Redis, socket.io utilisera Redis pub / sub sous le capot pour propager des messages entre les serveurs, et les serveurs propageront des messages à client.

Donc en utilisant socket.io chambres avec socket.io configuré avec le magasin Redis est probablement le plus simple pour vous.

31
répondu Pascal Belloncle 2013-02-19 22:43:38

J'ai fini par écrire un plugin node pour permettre à de nombreux clients pub-sub mais ne nécessitent que 2 connexions redis au lieu d'une nouvelle sur chaque connexion socketio, cela devrait fonctionner en général, pensé que quelqu'un d'autre peut trouver l'utilisation pour cela.

Ce code suppose que vous avez socket.io exécution et configuration, essentiellement dans cet exemple n'importe quel nombre de socket.io les clients peuvent se connecter et ils n'utiliseront toujours que 2 connexions redis, mais tous les clients peuvent s'abonner à leurs propres canaux. Dans ce exemple, tous les clients reçoivent un message " doux message!"après 10 secondes.

Exemple avec socket.io (en utilisant Redis pub-sub):

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

Code du greffon réel:

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;

J'ai fait le tour et j'ai essayé d'améliorer l'efficacité autant que possible, mais après avoir fait des tests de vitesse différents, j'ai conclu que c'était le plus rapide que je pouvais obtenir.

Pour la version à jour: https://github.com/Jezternz/node-redis-pubsub

6
répondu Josh Mc 2015-06-18 22:18:01