web-dev-qa-db-fra.com

node-websocket-server: possible d'avoir plusieurs "diffusions" séparées pour un seul processus node.js?

Je voudrais savoir s'il est possible de diffuser sur différentes "connexions" Websocket à partir de la même instance d'application node-websocket-server . Imaginez un serveur de chat avec plusieurs salles, diffusant uniquement des messages aux participants spécifiques à chaque salle, sur un seul processus de serveur node.js. J'ai réussi à implémenter une solution à une salle de discussion par processus, mais je veux la faire passer au niveau supérieur.

57
Sebastian Motraghi

Vous aimeriez probablement essayer Push-it: http://github.com/aaronblohowiak/Push-It qui est construit sur Socket.IO. La conception est conforme au protocole de Bayeux.

Cependant, si vous avez besoin de quelque chose qui utilise redis pubsub, vous pouvez vérifier http://github.com/shripadk/Socket.IO-PubSub

Répondre spécifiquement à votre question: vous pouvez gérer un tableau de tous les clients connectés au serveur websocket. Et probablement diffusé à un sous-ensemble de ces clients? La méthode de diffusion fait essentiellement cela sous le capot. node-websocket-server/Socket.IO maintient un tableau de tous les clients connectés et effectue une boucle à travers chacun d'eux "envoyant" un message à chacun des clients. Contenu du code:

// 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});
          }
      }
}

Donc, si vous souhaitez relayer des messages uniquement vers des canaux spécifiques, il vous suffit de conserver une liste de tous les canaux abonnés par le client. Voici un exemple simple (pour commencer):

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});
         }
      }
}

Pour le rendre encore plus facile, utilisez EventEmitters. Donc, dans node-websocket-server/Socket.IO, voyez où les messages sont reçus et analysez le message pour vérifier le type (abonnement/désabonnement/publication) et émettez l'événement avec le message en fonction du type. Exemple:

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:

       }
}

Écoutez les événements émis dans votre application sur ("connexion"):

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);
});

J'espère que cela t'aides.

53
Shripad Krishna

Je ne sais pas si rooms était une fonctionnalité lorsque les autres réponses ont été créées, mais dans la documentation, elles ont exactement une fonctionnalité que vous recherchez. Allez donc sur ce lien et recherchez rooms.

Voici un exemple du site:

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');
});

Sur la base des autres réponses, il était plus axé sur la mise à l'échelle, j'aimerais avoir un aperçu si la version intégrée est bien adaptée aux réponses proposées.

24
Shawn Mclean

La réponse de Shripad K est très bien structurée. Bon travail.

Je pense que cette solution aura cependant quelques problèmes de mise à l'échelle.

Si vous aviez 10 000 utilisateurs simultanés dans 500 salles de discussion, chaque fois qu'un utilisateur enverrait un message, vous devriez parcourir les 10 000 clients. Je soupçonne qu'il serait plus rapide de stocker la liste des clients dans une pièce donnée dans une structure en redis et de simplement saisir cette liste et l'envoyer à ces clients.

1) Je ne sais pas si c'est vraiment plus rapide. 2) Je ne sais pas ce qui pourrait être stocké dans redis qui nous permettrait ensuite de référencer les clients. Peut-être qu'il pourrait y avoir un hachage de tous les clients sur le serveur, par un identifiant unique et en redis, nous pourrions simplement stocker un ensemble d'identifiants utilisateur par salle de discussion?

Cela semble-t-il plus évolutif?

J'ai écrit un serveur de discussion de nœud basé sur fzysqr et j'ai besoin de le rendre évolutif pour plusieurs conversations avant de le déployer largement.

9
Sean Colombo

Avec les chambres, mon simple chat de test ressemble

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);
    });

});


et 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
Volodymyr Krupach