Node -websocket-server: возможно иметь несколько отдельных "трансляций" для одного процесса node.js?

Я хотел бы узнать, можно ли транслировать на разных "соединениях" веб-приложений, запущенных из того же приложения node-websocket-server. Представьте себе сервер чата с несколькими комнатами, только передавая сообщения участникам, специфичным для каждой комнаты, на одном сервере node.js. Я успешно реализовал одночасовое решение для каждого процесса, но хочу перейти на следующий уровень.

Ответ 1

Вам бы хотелось попробовать Push-it: http://github.com/aaronblohowiak/Push-It, который построен поверх Socket.IO. Конструкция придерживается протокола Bayeux.

Однако, если вам нужно что-то, что использует 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);
});

Надеюсь, что это поможет.

Ответ 2

Я не уверен, что комнаты были функцией, когда были созданы другие ответы, но в документации у них есть функция, ты ищешь. Поэтому перейдите по этой ссылке и выполните поиск 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');
});

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

Ответ 3

Ответ Shripad K очень хорошо структурирован. Хорошая работа.

Я думаю, что решение будет иметь проблемы с масштабированием.

Если у вас было 10 000 одновременных пользователей в 500 чатах, каждый раз, когда любой пользователь отправлял сообщение, вам нужно было бы перебрать все 10 000 клиентов. Я подозреваю, что быстрее будет хранить список клиентов в данной комнате в структуре redis и просто захватить этот список и отправить этим клиентам.

1) Не уверен, что это быстрее. 2) Не уверен, что можно было бы сохранить в redis, что позволило бы нам ссылаться на клиентов. Может быть, может быть хеш всех клиентов на сервере, с помощью уникального идентификатора и в redis, мы могли бы просто сохранить набор идентификаторов пользователя в чате?

Является ли это более масштабируемым?

Я написал чат-сервер node на основе fzysqr и должен сделать его масштабируемым для нескольких чатов, прежде чем выкладывать его широко.

Ответ 4

С комнатами мой простой тестовый чат выглядит

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>