node-websocket-server:单个 node.js 进程可以有多个单独的“广播”吗?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/4445883/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
node-websocket-server: possible to have multiple, separate "broadcasts" for a single node.js process?
提问by S M
I'd like to know if it's possible to broadcast on different websocket "connections" running from the same node-websocket-serverapp instance. Imagine a chatroom server with multiple rooms, only broadcasting messages to the participants specific to each room, on a single node.js server process. I've successfully implemented a one-chatroom-per-process solution, but I want to take it to the next level.
我想知道是否可以在从同一个node-websocket-server应用程序实例运行的不同 websocket“连接”上进行广播。想象一个有多个房间的聊天室服务器,在单个 node.js 服务器进程上只向特定于每个房间的参与者广播消息。我已经成功实施了一个聊天室每个进程的解决方案,但我想把它提升到一个新的水平。
采纳答案by Shripad Krishna
You would probably like to try Push-it: http://github.com/aaronblohowiak/Push-Itwhich is built on top of Socket.IO. Design adheres to the Bayeux Protocol.
您可能想尝试 Push-it:http: //github.com/aaronblohowiak/Push-It,它建立在 Socket.IO 之上。设计遵循贝叶协议。
However, if you need something that uses redis pubsub you can check http://github.com/shripadk/Socket.IO-PubSub
但是,如果您需要使用 redis pubsub 的东西,您可以检查http://github.com/shripadk/Socket.IO-PubSub
Specifically answering your question: You can maintain an array of all the clients connected to the websocket server. And probably just broadcast to a subset of those clients? The broadcast method does essentially that under the hood. node-websocket-server/Socket.IO maintains an array of all the clients connected and just loops through all of them "send"ing a message to each of the clients. Gist of the code:
具体回答您的问题:您可以维护连接到 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});
}
}
}
So if you want to relay messages only to specific channels, just maintain a list of all the channels subscribed by the client. Here is a simple example (to just get you started) :
因此,如果您只想将消息中继到特定频道,只需维护客户端订阅的所有频道的列表。这是一个简单的例子(只是让你开始):
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});
}
}
}
To make it even easier use EventEmitters. So in node-websocket-server/Socket.IO see where the messages are being received and parse the message to check the type (subscribe/unsubscribe/publish) and emit the event with the message depending on the type. Example:
为了使它更容易使用 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:
}
}
Listen to the events emitted in your app's on('connection') :
侦听在您的应用程序的 on('connection') 中发出的事件:
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);
});
Hope this helps.
希望这可以帮助。
回答by Shawn Mclean
I'm not sure if roomswere a feature when the other answers were created, but in the documentation, they have a feature exactly what you are looking for. So go to that link and search for rooms.
我不确定在创建其他答案时房间是否是一项功能,但在文档中,它们具有您正在寻找的功能。因此,转到该链接并搜索rooms.
Here is an example from the 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');
});
Based on the other answers, it was more focused on scaling, I would love some insight if the built in version scales well as the proposed answers.
根据其他答案,它更侧重于缩放,如果内置版本的缩放比例与建议的答案一致,我会喜欢一些见解。
回答by Sean Colombo
Shripad K's answer is very well structured. Good job.
Shripad K 的回答结构非常好。做得好。
I think that solution will have some scaling issues though.
不过,我认为该解决方案会有一些扩展问题。
If you had 10,000 concurrent users in 500 chat rooms, then every time any user sent a message, you'd have to loop through all 10,000 clients. I suspect that it would be faster to store the list of clients in a given room in a structure in redis and just grab this list and send to those clients.
如果您在 500 个聊天室中有 10,000 个并发用户,那么每次任何用户发送消息时,您都必须遍历所有 10,000 个客户端。我怀疑将给定房间中的客户端列表存储在 redis 中的结构中并获取此列表并发送给这些客户端会更快。
1) Not sure if that's actually faster. 2) Not sure what could be stored in redis that would then allow us to reference clients. Maybe there could be a hash of all clients in the server, by a unique id and in redis, we could just store a set of the user id's per chat room?
1)不确定这是否真的更快。2) 不确定可以在 redis 中存储什么,然后我们才能引用客户端。也许服务器中所有客户端的哈希值都是唯一的,而在 redis 中,我们可以只存储每个聊天室的一组用户 ID?
Does this seem any more scalable?
这似乎更具可扩展性吗?
I've written a node chat server based on fzysqr's and need to make it scalable for multiple chats before we roll it out widely.
我已经编写了一个基于 fzysqr 的节点聊天服务器,在我们广泛推广它之前,需要使其可扩展用于多个聊天。
回答by Volodymyr Krupach
With rooms my simple test chat looks like
chat.js:
对于房间,我的简单测试聊天看起来像
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);
});
});
and chat.html:
和 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>

