在水平扩展的 WebSocket 服务器上,如何平衡套接字负载?

24

每隔几个月,当考虑涉及套接字的个人项目时,我总是有一个问题:"在动态水平扩展WebSocket服务器上如何正确地负载均衡套接字?"

我理解水平扩展WebSockets背后的理论,并使用发布/订阅模型将数据传递到保存特定用户套接字连接的正确服务器。我认为我了解有效地识别具有最少当前套接字连接的服务器的方法,我想路由新的套接字连接到它。 但我不知道如何有效地将新的套接字连接路由到已选择的低套接字计数服务器。

我不认为这个答案会和特定的服务器实现有关,而是可以应用于大多数服务器。我可以轻松地使用vert.x,node.js,甚至perfect来实现这一点。


附注:如何有效地将新的套接字连接路由到您选择的低套接字计数服务器 - 这不是唯一的指标...也许一个服务器有很多懒惰的客户端,而另一个服务器有很多活跃的客户端 - 客户端数量并不是唯一有效的测试。此外,当一个懒惰的客户端变得超级活跃时会发生什么?...这一切都是不可能猜测的。我同样有兴趣发现答案,但我猜轮询同时调整上次响应时间测试(针对已知的预设查询)将是一个不错的启发式应用。 - Myst
如果您对答案感兴趣,请点赞问题 :-) 轮询正是我的问题试图避免的。如果您的服务器负载过高,因此需要启动一个新服务器,则轮询将无法很好地填充该新服务器上的套接字。而且,一旦您可以将套接字路由到特定服务器,您就可以让客户端重新连接并重新分布负载。 - spierce7
3个回答

31

首先,你需要定义你所询问的问题的范围。如果你真正谈论动态水平扩展,即根据总负载启动和关闭服务器,那么这比仅仅确定如何路由最新传入的新套接字连接更加复杂。

要解决这个问题,你必须有一种方法来“移动”一个套接字从一个主机到另一个主机,以便你可以清除你想要关闭的主机上的连接(我在这里假设真正的动态缩放既可以升又可以降)。通常我见过的方式是通过使用一个合作的客户端来实现,告诉客户端重新连接,当它重新连接时,它会被负载均衡到不同的服务器上,因此可以清除你想要关闭的服务器。如果你的客户端已经具有自动重新连接逻辑(像socket.io一样),你可以让服务器关闭连接,客户端将自动重新连接。

至于负载均衡传入的客户端连接,你必须决定要使用哪种负载度量。最终,你需要为每个服务器进程得出一个得分,以告诉你其有多“繁忙”,以便将新连接放置在最不繁忙的服务器上。一个基本的得分只是当前连接数。如果你每个服务器进程有大量连接(数万个),并且在你的应用程序中没有特定的原因使一些连接比其他连接更繁忙,那么大数定律可能平均了负载,因此你可以只考虑每台服务器有多少连接。如果使用连接不是那么公平或均匀,那么你还必须考虑一些时间移动CPU负载的平均值,以及总连接数。

如果你将负载分配到多台物理服务器上,则需要一个负载均衡器或代理服务,每个人最初连接到该服务,该代理可以查看池中所有当前运行的服务器的度量,并将连接分配给具有最低当前得分的服务器。这可以通过代理方案或(更可扩展的)重定向来实现,因此代理在初始分配后离开了。

你还可以设置一个定期检查所有服务器集群中的负载得分(无论你选择如何计算)的进程,决定何时启动一个新服务器或关闭一个服务器,或当某台服务器的负载过高且需要将其从多个连接中踢出时,告诉该服务器强制它们重新平衡。
“我不明白的是如何将新的套接字连接有效地路由到选定的低套接字计数服务器。” 如上所述,您可以使用代理方案或重定向方案。在连接时略微增加成本,我更喜欢重定向方案,因为它在运行时更具可扩展性,并为现有连接创建较少的故障点。所有客户端都连接到您的入站连接网关服务器,该服务器负责了解农场中每个服务器的当前负载得分,并基于此将传入的连接分配给具有最低得分的主机,然后重定向此新连接以重新连接到农场中的特定服务器之一。
我还看到过完全通过自定义DNS实现的负载平衡。客户端请求farm.somedomain.com的IP地址,而该自定义DNS服务器会给他们分配要分配的主机的IP地址。查询farm.somedomain.com的每个客户端可能会获得不同的IP地址。通过添加或删除它们的自定义DNS服务器来启动或关闭主机,并且该自定义DNS服务器必须包含了解所有运行主机的负载平衡逻辑和当前负载得分的逻辑。

我非常感谢您的回答。它很有道理,但并没有真正回答我的问题。我仍然不明白如何将客户端套接字连接路由到特定的服务器。我不明白如何路由套接字连接请求。也许我提出的问题太基础了,您没有预料到。如果我收到一个套接字连接请求,然后使用类似hazelcast或redis来识别负载最低的服务器,我该如何将请求发送到该服务器,并使客户端直接创建一个套接字连接到该服务器? - spierce7
1
@spierce7 - 你可以使用代理模型或重定向模型。在代理模型中,它的工作方式类似于NGINX负载均衡。客户端连接到代理,代理然后连接到适当的主机并充当中间人转发数据包。在这种情况下,我更喜欢重定向模型。客户端连接到负载均衡器,然后被重定向到一个新的IP地址,以便他们建立新的连接。 - jfriend00
1
@spierce7 - 重定向可以在初始WebSocket连接时使用303或307进行,也可以在应用程序级别上完成,在这个级别上,WebSocket连接启动到负载均衡器,然后负载均衡器通过WebSocket向客户端发送一个应用程序消息,告诉他们重新连接到新主机。我认为有些问题是不是所有的WebSocket客户端都支持3xx重定向,所以在应用程序级别上做可能更可靠。 - jfriend00
@spierce7 - 307重定向会返回一个新的主机名,供客户端连接。通过重定向方案,负载均衡器在初始负载平衡后离开,这对于可扩展性和可靠性来说都是一件好事(我认为)。 - jfriend00
是的,这就是我想要避免的原因 :-) 再次感谢你的来往! - spierce7
显示剩余6条评论

5

将WebSocket请求路由到一个负载均衡器,由其决定将连接发送到哪里。

例如,HAProxy 有一种用于长时间连接的 leastconn 方法,会选择最近最少使用的服务器和具有最低连接计数的服务器。

HAProxy 后端服务器权重也可以通过外部输入进行修改,@jfriend00 在其答案中详细介绍了权重的技术细节


0
我找到了一个可能有用的项目: https://github.com/apundir/wsbalancer 这是来自描述的片段:
Websocket balancer是用于websockets的有状态反向代理。它将传入的websockets分布在多个可用的后端之间。除了负载平衡,该负载均衡器还会在发生中期异常故障时透明地从一个后端切换到另一个后端。 在此故障转移期间,远程客户端连接保持不变,因此远程客户端甚至看不到此故障转移。在此故障转移期间,将尽一切努力确保不会丢失任何消息。
关于你的问题:如果配置为这样做,那么新的连接将由负载均衡器路由。
正如@Matt所提到的,例如使用HAProxy使用leastconn选项。

1
这样做会遇到扩展问题,因为它代理了所有的Web套接字,你觉得呢? - spierce7
每个IP有64k客户端端口限制,我猜这个负载均衡器设置将无法扩展到32k以上,因为它会消耗每个入站连接的2个Websockets。 - Ashwin Prabhu

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接