web-dev-qa-db-fra.com

Que dois-je utiliser? Socket.io rooms 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 laquelle est la plus fiable et laquelle est la plus efficace? J'utilise beaucoup Redis et Socket.io tout au long de mon code. Donc je veux savoir si je devrais utiliser - Rooms de Socket.io 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 voudrez peut-être utiliser redis pub/sub sur les salles socket.io. Avec les salles Socket.io lorsque vous publiez sur des écouteurs, les clients (navigateur) reçoivent le message, avec redis ce sont en fait les clients (redis ~ on server) 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 passer aux clients du navigateur, il est préférable d'utiliser redis. En utilisant redis, vous pouvez simplement déclencher un événement pour générer les 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 bat presque le but des chambres, au 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 permettre toujours le traitement client individuel, voir la réponse ci-dessous ....

29
Josh Mc

Redis pub/sub est idéal si tous les clients ont un accès direct à redis. Si vous avez plusieurs serveurs de nœuds, l'un 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 est génial.

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

Donc, utiliser les salles socket.io avec socket.io configuré avec le magasin Redis est probablement le plus simple pour vous.

33
Pascal Belloncle

J'ai fini par écrire un plugin de nœud pour permettre de nombreux clients pub-sub mais ne nécessite que 2 connexions redis au lieu d'un nouveau sur chaque connexion socketio, cela devrait fonctionner en général, pensait que quelqu'un d'autre pourrait l'utiliser.

Ce code suppose que vous avez socket.io en cours d'exécution et de configuration, dans cet exemple, n'importe quel nombre de clients socket.io peut se connecter et il n'utilisera toujours que 2 connexions redis, mais tous les clients peuvent s'abonner à leurs propres canaux. Dans cet exemple, tous les clients reçoivent un message "message doux!" 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 plugin 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 autant que possible l'efficacité, mais après avoir fait différents tests de vitesse, j'ai conclu que c'était le plus rapide possible.

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

6
Josh Mc