Что я должен использовать? Комнаты Socket.io или паб-саб Redis?

Довольно простой вопрос. Я создаю игру в реальном времени, используя nodejs в качестве моего бэкэнда, и мне интересно, есть ли какая-либо информация, какая из которых более надежна, а какая более эффективна? Я интенсивно использую Redis и Socket.io в своем коде. Итак, я хочу знать, должен ли я использовать Комнаты Socket.io, или мне было бы лучше использовать Pubis Sub Redis?

Обновление: только что понял, что есть очень важная причина, по которой вы можете использовать redis pub / sub через комнаты socket.io. В комнатах Socket.io, когда вы публикуете для слушателей, клиенты (браузеры) получают сообщение, а с помощью redis на самом деле клиенты (перенаправляют на сервер) получают сообщения. По этой причине, если вы хотите сообщить всем (серверным) клиентам информацию, относящуюся к каждому клиенту, и, возможно, выполнить некоторую обработку перед передачей клиентам браузера, лучше использовать redis. Используя redis, вы можете просто запустить событие, чтобы сгенерировать индивидуальные данные для каждого пользователя, где, как и в случае с socket.io, вы должны фактически сгенерировать все уникальные данные всех пользователей одновременно, затем просмотреть их и отправить их индивидуальные данные, что почти побеждает назначение комнат, по крайней мере для меня.

К сожалению, для моих целей я застрял с Redis на данный момент.

Обновление 2: Закончилась разработка плагина для использования только 2-х подключений redis, но при этом допускается обработка отдельного клиента, см. Ответ ниже....

2 ответа

Решение

Redis pub/sub отлично подходит, если все клиенты имеют прямой доступ к redis. Если у вас есть несколько узловых серверов, один может отправить сообщение другим.

Но если у вас также есть клиенты в браузере, вам нужно что-то еще для передачи данных с сервера на клиент, и в этом случае socket.io отлично подходит.

Теперь, если вы используете socket.io с хранилищем Redis, socket.io будет использовать Redis pub/sub для защиты сообщений между серверами, а серверы будут передавать сообщения клиентам.

Так что использование комнат socket.io с socket.io, настроенным с хранилищем Redis, вероятно, самое простое для вас.

Я закончил тем, что написал плагин для узла, который позволял бы работать с многими клиентами pub-sub, но требовал только 2-х подключений redis вместо нового на каждом соединении socketio, он должен работать в общем, полагая, что кто-то другой может найти его применение.

В этом коде предполагается, что у вас запущен и настроен socket.io, в основном в этом примере может подключаться любое количество клиентов socket.io, и он всегда будет использовать только 2 соединения redis, но все клиенты могут подписаться на свои собственные каналы. В этом примере все клиенты получают сообщение "Сладкое сообщение!" через 10 секунд.

Пример с socket.io (используя 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);

Фактический код плагина:

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;

Я обшаривал все вокруг и пытался как можно больше повысить эффективность, но после нескольких различных тестов скорости я пришел к выводу, что это был самый быстрый способ получить результат.

Для актуальной версии: https://github.com/Jezternz/node-redis-pubsub

Другие вопросы по тегам