首页 文章

将分离的实时服务器与标准Web服务器一起扩展

提问于
浏览
2

假设我有一个典型的Web服务器为客户端提供标准HTML页面,并且与它一起运行的websocket服务器用于实时更新(聊天,通知等) .

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

我担心的是,如果我想扩展一点,并添加另一个实时服务器,似乎我唯一的选择是:

  • 让主服务器跟踪客户端连接到哪个实时服务器 . 当该客户端收到通知/聊天消息时,主服务器将该消息转发到仅客户端连接的实时服务器 . 这里的缺点是代码复杂性,因为主服务器必须做一些额外的簿记 .

  • 或者让主服务器简单地将该消息传递给每个实时服务器;只有客户端连接的服务器实际上会对它做任何事情 . 这将导致传递大量浪费的消息 .

我在这里错过了另一种选择吗?我只是想确保我不要走太远这些路径,并意识到我做错了 .

4 回答

  • 0

    如果方案是

    a)主Web服务器在动作时引发消息(假设插入记录)b)他通知相应的实时服务器

    您可以通过使用中间pub / sub体系结构将这两个步骤解耦,以便将消息转发给入口的收件人 .

    实施将是

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

    2)当主应用程序想要通过实时服务器通知用户时,它会向通道发送消息,实时服务器获取它并将其转发给目标用户 .

    这样,您可以将实时通知与主应用程序分离,而无需跟踪用户的位置 .

  • 1

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

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

    How Topic Exchanges Route Messages

    RabbitMQ for Windows: Exchange Types

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

  • 1

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

    另一种方法是使用load balancer .

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

    当然,这只是将代码复杂性从主服务器转移到新组件,但从概念上讲,我认为它更好,更分离 .

  • 0

    更改了答案,因为答复表明“主”和“实时”服务器是负载均衡的集群,而不是单个主机 .

    中心可扩展性问题似乎是:

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

    强调“相关”一词 . 假设您有10个“主”服务器和50个“实时”服务器,主服务器#5上会发生一个事件:哪个websockets被认为与此事件有关?

    最糟糕的情况是任何“主”服务器上的任何事件都需要传播到所有websockets . 这是O(N ^ 2)的复杂性,其被视为严重的可扩展性损害 .

    这种O(N ^ 2)复杂度只能是如果您可以在不以群集大小或总nr增长的组中对相关连接进行分组,则会阻止 . 连接 . 分组需要状态存储器来存储连接所属的组 .

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

    • 全局内存(memcached / redis / DB,...)

    • 粘性路由(负载均衡器配置)

    • 客户端内存(cookie,浏览器本地存储,链接/重定向URL)

    选项3算作最具扩展性的选项,因为它省略了中央状态存储 .

    为了将消息从“主”服务器传递到“实时”服务器,根据定义,该流量应远小于客户端的流量 . 还有有效的框架来推动发布/子流量 .

相关问题