web-dev-qa-db-ja.com

何を使うべきですか? Socket.ioルームまたはRedis pub-sub?

かなり簡単な質問です。 nodejsをバックエンドとして使用してリアルタイムゲームを構築していますが、信頼性の高いものと効率的なものに関する情報があるかどうか疑問に思っています。私はコード全体でRedisとSocket.ioの両方を頻繁に使用しています。したがって、Socket.ioの Rooms を使用する必要があるのか​​、またはredis ' pub-sub を使用する方がよいのかを知りたいのですが。

更新:socket.ioルームでredis pub/subを使用したい場合がある非常に重要な理由があることに気づきました。 Socket.ioルームを使用してリスナーにパブリッシュすると、(ブラウザー)クライアントはメッセージを受信しますが、redisでは実際に(再サーバー上の)クライアントがメッセージを受信します。このため、各クライアントに固有の情報をすべての(サーバー)クライアントに通知し、ブラウザークライアントに渡す前に何らかの処理を実行する場合は、redisを使用する方がよいでしょう。 redisを使用すると、イベントを発火して各ユーザーの個別データを生成できます。socket.ioの場合と同様に、実際にすべてのユーザーの一意のデータを一度に生成し、それらをループして個別のデータを送信する必要があります。部屋の目的、少なくとも私にとって。

残念ながら、私は今のところredisに悩まされています。

Update 2:最終的に、2つのredis接続のみを使用するプラグインを開発しましたが、個々のクライアントの処理が可能です。以下の回答を参照してください。

29
Josh Mc

Redis pub/subは、すべてのクライアントがredisに直接アクセスできる場合に最適です。複数のノードサーバーがある場合、1つは他にメッセージをプッシュできます。

ただし、ブラウザにクライアントもある場合は、サーバーからクライアントにデータをプッシュするために別のものが必要です。この場合、socket.ioが最適です。

これで、redisストアでsocket.ioを使用すると、socket.ioは内部でRedis pub/subを使用してサーバー間でメッセージを伝播し、サーバーはメッセージをクライアントに伝播します。

したがって、redisストアで構成されたsocket.ioでsocket.ioルームを使用するのがおそらく最も簡単です。

33

私は多くのpub-subクライアントを許可するノードプラグインを作成することになりましたが、すべての単一のsocketio接続で新しい接続ではなく2つのredis接続のみを必要とします。

このコードは、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

6
Josh Mc