web-dev-qa-db-ja.com

node-websocket-server:単一のnode.jsプロセスに対して複数の別個の「ブロードキャスト」を使用できますか?

同じ node-websocket-server アプリインスタンスから実行されている異なるwebsocket "接続"でブロードキャストできるかどうかを知りたいのですが。単一のnode.jsサーバープロセスで、各ルームに固有の参加者にのみメッセージをブロードキャストする、複数のルームを持つチャットルームサーバーを想像してください。プロセスごとに1チャットルームのソリューションを実装できましたが、次のレベルに上げたいと思います。

57

おそらくPush-itを試してみたいと思います。 http://github.com/aaronblohowiak/Push-It Socket.IOの上に構築されています。設計はバイユープロトコルに準拠しています。

ただし、redis pubsubを使用するものが必要な場合は、 http://github.com/shripadk/Socket.IO-PubSub を確認できます

具体的に質問に答える:websocketサーバーに接続されているすべてのクライアントの配列を維持できます。そして、おそらくちょうどそれらのクライアントのサブセットに放送?ブロードキャスト方式は、基本的にそれを内部で実行します。 node-websocket-server/Socket.IOは、接続されたすべてのクライアントの配列を維持し、すべてのクライアントをループして、各クライアントにメッセージを「送信」します。コードの要点:

// considering you storing all your clients in an array, should be doing this on connection:
clients.Push(client)

// loop through that array to send to each client
Client.prototype.broadcast = function(msg, except) {
      for(var i in clients) {
          if(clients[i].sessionId !== except) {
             clients[i].send({message: msg});
          }
      }
}

したがって、特定のチャネルのみにメッセージを中継する場合は、クライアントがサブスクライブしているすべてのチャネルのリストを維持するだけです。ここでは簡単な例は、(ちょうどあなたが始めるに)次のとおりです。

clients.Push(client);


Client.prototype.subscribe = function(channel) {
      this.channel = channel;
}

Client.prototype.unsubscribe = function(channel) {
     this.channel = null;
}

Client.prototype.publish = function(channel, msg) {
      for(var i in clients) {
         if(clients[i].channel === channel) {
            clients[i].send({message: msg});
         }
      }
}

さらに簡単にするには、EventEmittersを使用します。したがって、node-websocket-server/Socket.IOでメッセージの受信場所を確認し、メッセージを解析してタイプを確認し(サブスクライブ/サブスクライブ/パブリッシュ)、タイプに応じてメッセージとともにイベントを発行します。例:

Client.prototype._onMessage = function(message) {
       switch(message.type) {
         case 'subscribe':
             this.emit('subscribe', message.channel);
         case 'unsubscribe':
             this.emit('unsubscribe', message.channel);
         case 'publish':
             this.emit('publish', message.channel, message.data);
         default:

       }
}

(「接続」)上でのアプリの中に放出されたイベントに耳を傾けます:

client.on('subscribe', function(channel) {
     // do some checks here if u like
     client.subscribe(channel);
});
client.on('unsubscribe', function(channel) {
     client.unsubscribe(channel);
});
client.on('publish', function(channel, message) {
     client.publish(channel, message);
});

お役に立てれば。

53
Shripad Krishna

rooms が他の回答が作成されたときの機能であったかどうかはわかりませんが、ドキュメントでは、まさにあなたが探している機能を備えています。そのリンクに移動して、roomsを検索してください。

ここにサイトからの例があります:

var io = require('socket.io').listen(80);

io.sockets.on('connection', function (socket) {
  socket.join('justin bieber fans');
  socket.broadcast.to('justin bieber fans').emit('new fan');
  io.sockets.in('rammstein fans').emit('new non-fan');
});

他の回答に基づいて、スケーリングに重点を置いていましたが、組み込みのバージョンが提案された回答と同様にスケーリングする場合、いくつかの洞察が欲しいです。

24
Shawn Mclean

Shripad Kの答えは非常によく構成されています。よくやった。

ただし、このソリューションにはスケーリングの問題があると思います。

500のチャットルームに10,000の同時ユーザーがいる場合、ユーザーがメッセージを送信するたびに、10,000のすべてのクライアントをループする必要があります。 redisの構造内の特定の部屋にクライアントのリストを保存し、このリストを取得してそれらのクライアントに送信する方が高速だと思います。

1)それが実際に速いかどうかわからない。 2)redisに保存できるものが不明なため、クライアントを参照できるようになります。サーバー内のすべてのクライアントのハッシュが一意のIDであり、redisで、チャットルームごとにユーザーIDのセットを保存することもできますか?

これはよりスケーラブルに見えますか?

私はfzysqrに基づいてノードチャットサーバーを作成しました。これを広く展開する前に、複数のチャットに対してスケーラブルにする必要があります。

9
Sean Colombo

部屋では、私の簡単なテストチャットは次のようになります

chat.js:

var app = require('http').createServer(handler)
  , io = require('socket.io').listen(app)
  , fs = require('fs')

app.listen(80);

function handler (req, res) {
  fs.readFile(__dirname + '/chat.html',
  function (err, data) {
    if (err) {
      res.writeHead(500);
      return res.end('Error loading chat.html');
    }

    res.writeHead(200);
    res.end(data);
  });
}

io.sockets.on('connection', function (socket) {

    socket.on('join', function (room) {
        if (Array.isArray(room)) {
            var i;
            for (i = 0; i < room.length; ++i) {
                console.log('join room ' + room[i]);
                socket.join(room[i]);
            }
        } else if (typeof room === 'string') {
            console.log('join room ' + room);
            socket.join(room);
        }
    });

    socket.on('leave', function (room) {
        if (typeof room === 'string') {
            console.log('leave room ' + room);
            socket.leave(room);
        }
    });

    socket.on('post', function (data) {
        io.sockets.in(data.room).emit('publish', data);
    });

});


およびchat.html:

<html>
<head>
<title>Node js test</title>
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.1/jquery.min.js"></script>
<script src="http://127.0.0.1:80/socket.io/socket.io.js"></script>
</head>
<body>
<h2>Node js test</h2>

<div style="height:400px;">
    <div id="controls" style="height:400px; display: inline-block; width:20%; float:left; background-color:lightyellow;">
        <input id="room1_check" type="checkbox" value="room_1" checked /><label for="room1_check">Room 1</label><br/><br/>
        <input id="room2_check" type="checkbox" value="room_2" /><label for="room2_check">Room 2</label><br/><br/>
        <input id="room3_check" type="checkbox" value="room_3" checked /><label for="room3_check">Room 3</label><br/><br/>
        <input id="room4_check" type="checkbox" value="room_4" /><label for="room4_check">Room 4</label><br/><br/>
        <input id="room5_check" type="checkbox" value="room_5" /><label for="room5_check">Room 5</label><br/><br/>
    </div>

    <div id="stream" style="height:400px; display: inline-block; width:40%; background-color:white; overflow:auto;"></div>

    <div id="post" style="height:400px; display: inline-block; width:40%; float:right; background-color:yellow;">
        <label for="postRoom">Room: </label>
        <select id="postToRoom">
            <option value="room_1">Room 1</option>
            <option value="room_2">Room 2</option>
            <option value="room_3">Room 3</option>
            <option value="room_4">Room 4</option>
            <option value="room_5">Room 5</option>
        </select>
        <br/><br/>
        <label for="postBy">By: </label>
        <select id="postBy">
            <option value="User 1">User 1</option>
            <option value="User 2">User 2</option>
            <option value="User 3">User 3</option>
            <option value="User 4">User 4</option>
            <option value="User 5">User 5</option>
        </select>
        <br/><br/>
        <label for="postMessage">Message:</label><br/>
        <textarea id="postMessage" style="width:80%; height:100px;" ></textarea>
        <br/><br/>
        <input id="postBtn" type="button" value="post message" />
    </div>

</div>


<script>
    var socket = io.connect('http://127.0.0.1:80');

    var checkedRooms = [];
    $('#controls :checked').each(function() {
        checkedRooms.Push($(this).val());
    });
    socket.emit('join', checkedRooms);

    socket.on('publish', function (post) {
        //console.log(data);
        $("#stream").html($("#stream").html() + "room: " + post.room + "<br/>");
        $("#stream").html($("#stream").html() + "by: " + post.by + "<br/>");
        $("#stream").html($("#stream").html() + "on: " + post.on + "<br/>");
        $("#stream").html($("#stream").html() + "message: " + unescape(post.message) + "<br/>");
        $("#stream").html($("#stream").html() + "=============================================<br/>");
    });

    $('#controls :checkbox').change(function () {
        socket.emit(this.checked ? 'join' : 'leave', $(this).val());
     });

    $("#postBtn").click(function() {
        socket.emit('post', {room: $("#postToRoom").val(), message: escape($("#postMessage").val()), by: $("#postBy").val(), on: (new Date() + "") });
    });

</script>

</body>
</html>
1