基于“广播”或“房间”的 Websocket 策略

Posted

技术标签:

【中文标题】基于“广播”或“房间”的 Websocket 策略【英文标题】:"Broadcast" or "Room" based Websocket Strategy 【发布时间】:2020-05-14 05:30:57 【问题描述】:

基于“room”的WebSocket连接或WebSocket中的“广播”选项背后的技术或策略是什么?

我们是否将“用户的 WebSocket 连接 ID”存储在基于房间的聊天系统的特定列表中? 在发送时,我们是否使用 for 循环向每个参与者/WebSocket-Connection-ID “发送/接收”消息,或者我们是否有一个 API 可以在不使用循环的情况下一次性向所有组用户发送消息?

我正在使用“gorilla WebSocket”-https://github.com/gorilla/websocket

如果我们将 WS 连接 ID 存储在列表中,那么如果任何房间增加了 5000 个参与者,那么运行 for loop 为每个参与者发送/接收消息将变得非常麻烦。

请不要共享任何库,我想了解基于房间的聊天系统如何工作或在后端运行

【问题讨论】:

Telegram Chat 有 1 个群组的 5000 名成员限制,他们真的运行for loop 来向数百万个群组中的所有 5000 名成员发送消息吗? 服务最终对每个 websocket 连接执行写入操作。 websockets 或底层 TCP 层没有广播功能。 【参考方案1】:

我构建了一个 web-socket this 技术,但稍作修改以支持多房间。

我为每个房间创建了一个集线器,并将集线器的地址存储在地图中,其中键为字符串,以便更轻松地使用房间名称访问每个集线器。

现在,上面提供的链接是包含在 gorilla/websocket 库中的官方示例,他们还清楚地记录了它的运行和行为方式。我建议您阅读该链接以更好地理解该示例。

我会尽量简短地解释一下。

存储聊天室中人员列表的集线器作为常规运行。 每个用户将被分配 2 个 go 例程,一个用于接收传入消息,一个用于将消息发送回它的客户端。除此之外,每个用户都有几个无缓冲通道来存储已接收或要发送的数据。

假设我发送一条消息,负责读取的 go-routine 读取消息 并将其传递到它和集线器之间共享的通道。现在,集线器需要将消息广播给聊天室中的每个人。它简单地遍历列表用户,并使用一个通道(再次,但不同的通道,而不是之前的通道)将数据传递给每个负责发送消息的 go-routine。

请记住,我的解释只是一个概述,还有很多细节我没有写,因为除非您仔细阅读代码,否则我将难以理解,但我错过的事情是给出示例的文档,所以随意参考。

现在回到 5000 名参与者的问题。如果他们不同时发送消息,上述方法可以轻松处理,这不太可能。 我在负载测试期间遇到了类似的问题,并且很困惑为什么我的连接会崩溃。

事实证明,无缓冲通道是一个问题,将其更改为缓冲通道(缓冲区的大小取决于同时消息的数量)。

因此,即使有 5000 名参与者,集线器也只需要使用通道传递消息,而两个 go-routines 会完成繁重的工作。

PS:我想对消息或数据库操作进行任何检查,然后在 read go-routine 上进行,而不是在集线器中阻止其他消息。

【讨论】:

it simply iterates over the list users,这正是我不想要的 - 用户迭代并为每个连接 ID 一个接一个地发送消息。是否没有 WebSocket API 将消息发布到大量没有。 1 次发射中的用户数? 我确信电报聊天使用了一个非常不同的模型,电报聊天在每个组中提供 5000 个用户限制,并且通过电报在那个大组中发送消息,我怀疑for loop 有什么作用。 我不确定电报聊天是如何工作的。在 gorilla/websocket 的示例中,您遍历列表并将消息传递到通道,集线器不会直接传递它。集线器将消息传递给 writepump go-routine,然后将消息传递给客户端。迭代不会消耗太多时间,因为它没有被任何东西阻塞。实际传递消息的是 go-routine,而 Golang 负责调度 go-routine。 对于在 1 次发射中发布的 API,每个用户都有不同的连接(即不同的地址),所以除了迭代连接之外,我看不到任何其他方法,除非我错了还有更好的方法。不管怎样,看看这些:freecodecamp.org/news/million-websockets-and-go-cc58418460bbgithub.com/gobwas/ws【参考方案2】:

如果您使用uWebSockets,则有订阅/发布机制。我不确定这种机制是否适用于所有支持 WebSockets 的服务器端框架。

这个想法很简单。您将 WebSocket 订阅到某个“主题”,然后您可以向属于该特定主题的用户发布消息。

当您检查源代码时,它会在内部循环调用WebSocketContextData。当您从 TCP 套接字的角度考虑它时,这是有道理的。那里的每个协议都建立在 TCP 或 UDP 之上。

【讨论】:

【参考方案3】:

在 websockets 上没有向所有用户“广播”,您必须单独运行每个用户。每个库都是一样的,你可以看看'uWebSockets'可能是存在的最快的websocket代码,它也必须这样做。有些人可以通过共享内存缓冲区和零内存分配来做到这一点,并且运行效率极高。但是你还是得跑遍所有人。

【讨论】:

以上是关于基于“广播”或“房间”的 Websocket 策略的主要内容,如果未能解决你的问题,请参考以下文章

基于 geohashes 的 pubsub 主题划分的建议,以实现巧妙的 websocket 连接服务

socket io 广播、房间和确认功能

socket io 广播、房间和确认功能

最强辅助Jmeter,测试WebSocket协议应用

[Go WebSocket] 多房间的聊天室自动清理无人房间

Spring WebSocket 多房间聊天