【问题标题】:Load balancing sockets on a horizontally scaling WebSocket server?水平扩展 WebSocket 服务器上的负载平衡套接字?
【发布时间】:2018-04-29 12:00:16
【问题描述】:

每隔几个月,在考虑一个涉及套接字的个人项目时,我都会发现自己有一个问题:“如何在动态水平扩展的 WebSocket 服务器上正确地对套接字进行负载平衡?”

我了解水平扩展 WebSocket 和使用 pub/sub 模型将数据获取到为特定用户保存套接字连接的正确服务器背后的理论。我想我了解有效识别具有最少当前套接字连接的服务器的方法,我也想路由一个新的套接字连接。 我不明白的是如何有效地将新的套接字连接路由到您选择的具有低套接字数的服务器。

我不认为这个答案会与特定的服务器实现相关联,而是可以应用于大多数服务器。我可以很容易地看到自己使用 vert.x、node.js 甚至完美的方式实现了这一点。

【问题讨论】:

  • 旁注:“如何有效地将新的套接字连接路由到您选择的具有低套接字数的服务器” - 这不是唯一的指标......也许是一个服务器有很多懒惰的客户端,而另一台服务器有很多活动的客户端——客户端的数量并不是唯一有效的测试。另外,当一个惰性客户端变得超级活跃时会发生什么? ......这一切都无法猜测。我对发现答案同样感兴趣,但我猜想在调整最后响应时间测试(对于已知的预设查询)时进行循环将是一个不错的启发式应用。
  • 如果您对答案感兴趣,请投票赞成这个问题 :-) 循环赛正是我的问题试图避免的。如果您的服务器负载不足,因此新服务器启动,轮询会在填充该新服务器上的套接字方面做得非常糟糕。此外,一旦您可以将套接字路由到特定服务器,您就可以让客户端重新连接并重新分配负载。

标签: node.js sockets websocket vert.x perfect


【解决方案1】:

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

要解决这个问题,您必须有一种方法可以将套接字从一个主机“移动”到另一个主机,这样您就可以清除来自您想要减速的主机的连接(我在这里假设真正的动态缩放上下)。我见过的通常方法是让一个合作的客户端参与进来,你告诉客户端重新连接,当它重新连接时,它会负载平衡到另一台服务器上,这样你就可以清除你想要关闭的服务器。如果您的客户端已经具有自动重新连接逻辑(如 socket.io 那样),您可以让服务器关闭连接,客户端将自动重新连接。

对于传入客户端连接的负载平衡,您必须确定要使用的负载指标。最终,您需要为每个服务器进程打一个分数,告诉您您认为它有多“忙”,以便您可以将新连接放在最不忙的服务器上。基本分数只是当前连接的数量。如果您的每个服务器进程有大量连接(数万个),并且在您的应用程序中没有特别的理由表明有些人可能比其他人更忙,那么大数定律可能会平均负载,这样您就可以侥幸逃脱每个服务器有多少连接。如果连接的使用不公平甚至不公平,那么您可能还必须考虑 CPU 负载的某种时间移动平均值以及连接总数。

如果您要跨多个物理服务器进行负载平衡,那么您将需要每个人最初都连接到的负载平衡器或代理服务,并且该代理可以查看池中所有当前正在运行的服务器的指标并分配连接到当前得分最低的那个。这可以通过代理方案或(更具可扩展性)通过重定向来完成,以便代理在初始分配后不碍事。

然后,您还可以有一个流程,定期检查集群中所有服务器上的负载分数(但您决定计算它),并决定何时启动新服务器或何时关闭或何时启动给定服务器上的平衡太差,需要告知该服务器关闭多个连接,迫使它们重新平衡。

我不明白的是如何有效地将新的套接字连接路由到您选择的具有低套接字数的服务器。

如上所述,您可以使用代理方案或重定向方案。在连接时成本稍高,我更喜欢重定向方案,因为它在运行时更具可扩展性,并且为现有连接创建的故障点更少。所有客户端都连接到您的传入连接网关服务器,该服务器负责了解场中每个服务器的当前负载分数,并基于此将传入连接分配给得分最低的主机,然后重定向此新连接重新连接到场中的特定服务器之一。


我还看到了纯粹由自定义 DNS 实现完成的负载平衡。客户端请求farm.somedomain.com 的 IP 地址,并且该自定义 DNS 服务器为它们提供了它希望分配给它们的主机的 IP 地址。每个查找farm.somedomain.com IP 地址的客户端可能会获得不同的 IP 地址。您可以通过在自定义 DNS 服务器中添加或删除主机来启动或关闭主机,而自定义 DNS 服务器必须包含用于了解负载平衡逻辑和所有正在运行的主机的当前负载分数的逻辑。

【讨论】:

  • 非常感谢您的回答。这是有道理的,但它并没有真正回答我的问题。我仍然不明白如何将客户端套接字连接路由到特定服务器。我不明白如何路由套接字连接请求。我要问的问题可能太基本了,以至于您没有预料到。如果我收到一个套接字连接请求,然后使用 hazelcast 或 redis 之类的东西来识别负载最低的服务器,我如何将请求发送到该服务器并让客户端直接创建一个到该服务器的套接字?
  • @spierce7 - 您可以使用代理模型或重定向模型。在代理模型中,它就像 NGINX 做负载均衡一样工作。客户端连接到代理,然后代理连接到适当的主机并充当双向转发数据包的中间人。在这种情况下,我更喜欢重定向模型。客户端连接到负载均衡器并重定向到一个新的 IP 地址,并建立新的连接。
  • @spierce7 - 重定向可以在初始 webSocket 连接上使用 303 或 307 完成,也可以在 webSocket 连接启动到负载均衡器然后是负载均衡器的应用程序级别完成通过 webSocket 向客户端发送一条应用程序消息,告诉他们重新连接到新主机。我认为是否所有 webSocket 客户端都支持 3xx 重定向存在一些问题,因此在应用程序级别执行它可能更可靠。
  • 所以你是说通过正确的重定向,客户端会直接连接到选定的服务器?我假设套接字总是通过这种方法通过负载均衡器。
  • 307 重定向如何工作?这会告诉客户端向资源发出另一个请求,还是服务器会正​​确地将请求路由到内部服务器?你有任何链接到这种方法的例子吗?
【解决方案2】:

将 websocket 请求路由到负载平衡器,该负载平衡器决定将连接发送到何处。

例如,HAProxy 有一个用于长连接的leastconn 方法,该方法选择最近最少使用且连接数最少的服务器。

HAProxy 后端server weightings can also be modified by external inputs、@jfriend00 详细介绍了weighting in their answer 的技术细节。

【讨论】:

    【解决方案3】:

    我发现这个项目可能有用: https://github.com/apundir/wsbalancer

    描述中的一个sn-p:

    Websocket 平衡器是 Websocket 的有状态反向代理。它在多个可用后端分发传入的 websocket。除了负载均衡外,均衡器还负责在会话中间异常失败的情况下透明地从一个后端切换到另一个后端。 在此故障转移期间,远程客户端连接保持原样,因此远程客户端甚至看不到此故障转移。已尽一切努力确保在此故障转移期间不会丢弃任何消息。

    关于您的问题:如果配置为新连接,负载均衡器将路由该新连接。

    正如@Matt 提到的,例如使用 HAProxy 使用 minimumconn 选项。

    【讨论】:

    • 由于它代理了所有的网络套接字,这不会受到缩放问题的影响吗?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-09-13
    • 2013-10-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-07-28
    • 2018-12-24
    相关资源
    最近更新 更多