将解耦的实时服务器与标准网络服务器一起扩展

Scaling a decoupled realtime server alongside a standard webserver

假设我有一个典型的网络服务器,它为客户端提供标准 HTML 页面,还有一个 websocket 服务器 运行 用于实时更新(聊天、通知等)。

我的一般工作流程是当主服务器上发生某些事情触发对实时消息的需求时,主服务器将该消息发送到实时服务器(通过消息队列),实时服务器将其分发到任何相关的连接。

我担心的是,如果我想扩大规模并添加另一个实时服务器,看来我唯一的选择是:

  1. 让主服务器跟踪客户端的实时服务器 连接到。当该客户收到 notification/chat 消息,主服务器只将该消息转发给 客户端连接到的实时服务器。这里的缺点是 代码复杂,因为主服务器必须做一些额外的书 保持。
  2. 或者让主服务器简单地传递该消息 连同每个实时服务器;只有客户端的服务器 连接到实际上会用它做任何事情。这会导致 在大量被传递的浪费消息中。

我是不是漏掉了另一个选项?我只是想确保我不会在其中一条道路上走得太远,然后意识到我做的事情完全错了。

对于一些实时服务器,您可以想象只在主服务器中保留它们的列表,然后循环使用它们。

另一种方法是使用 load balancer

基本上,您将拥有一个专用节点来接收来自主服务器的请求,然后让该负载平衡器节点负责选择将请求转发到哪个 websocket/realtime 服务器。

当然,这只是将代码的复杂性从主服务器转移到一个新的组件上,但从概念上讲,我认为它更好,更解耦。

更改了答案,因为回复表明 "main" 和 "realtime" 服务器已经是负载平衡的集群,而不是单独的主机。

核心的可扩展性问题似乎是:

My general workflow is when something occurs on the main server that triggers the need for a realtime message, the main server sends that message to the realtime server (via a message queue) and the realtime server distributes it to any related connection.

强调这个词"related"。假设您有 10 "main" 个服务器和 50 个 "realtime" 服务器,主服务器 #5 上发生了一个事件:哪些 websocket 会被认为与此事件相关?

最坏的情况是任何 "main" 服务器上的任何事件都需要传播到所有 websockets。这是一个 O(N^2) 复杂度,算作严重的可伸缩性障碍。

只有将 相关 连接分组到不随簇大小或总 nr 增长的组中,才能避免这种 O(N^2) 复杂性。的连接。分组需要状态内存来存储连接属于哪个组。

请记住,有 3 种存储状态的方法:

  1. 全局内存(memcached / redis / DB,...)
  2. 粘性路由(负载均衡器配置)
  3. 客户端内存(cookie、浏览器本地存储、link/redirect URL)

选项 3 被认为是最具可扩展性的选项,因为它省略了中央状态存储。

为了将消息从 "main" 传递到 "realtime" 服务器,根据定义,该流量应该比流向客户端的流量小得多。还有有效的框架来推动 pub/sub 流量。

您描述的问题是 SignalR 中使用的常见 "message backplane",也与消息体系结构中的 "fanout message exchange" 相关。当有背板或扇出时,每条消息都会转发到每个消息节点服务器,因此客户端可以连接到任何服务器并获取消息。当您必须同时支持长轮询和 websockets 时,这种方法是一种合理的痛苦。但是,正如您所注意到的,这是一种流量和资源的浪费。

您需要使用具有智能路由的消息基础架构,例如 RabbitMQ。看看主题和 header 交换:https://www.rabbitmq.com/tutorials/amqp-concepts.html

How Topic Exchanges Route Messages

RabbitMQ for Windows: Exchange Types

有大量不同的队列框架。选择你喜欢的一个,但要确保你可以有更多的交换模式,而不仅仅是直接或扇出;)最后,WebSocket 只是连接到消息基础设施的端点。所以如果你想横向扩展,它归结为你拥有的后端:)

如果场景是

a) 主 Web 服务器在执行操作时发出消息(假设插入了一条记录) b ) 他通知相应的实时服务器

您可以通过使用中间 pub/sub 架构将消息转发给指定的收件人来分离这两个步骤。

一个实现是

1) 你有一个 redis 发布-订阅通道,当客户端连接到实时套接字时,你就开始在该通道中监听

2) 当主应用想通过实时服务器通知用户时,它向频道推送一条消息,实时服务器获取并转发给目标用户。

这样,您就可以将实时通知与主应用分离,并且您不必跟踪用户的位置。