炫技亮点 Websocket集群解决方案汇总

问题

假设我们有一个聊天应用程序,其中客户端通过WebSocket与服务器进行实时通信。在单机环境下,所有的WebSocket连接都由单个服务器处理。

单机场景

用户A、用户B和web服务器建立连接之后,用户A发送一条消息到服务器,服务器再推送给用户B,在单机系统上所有的用户都和同一个服务器建立连接,所有的session都存储在同一个服务器中。

但随着用户数量的增加,我们需要将应用程序扩展为WebSocket集群,以提供更好的性能和可伸缩性。

集群场景

当演变为集群环境后,用户A与节点1建立连接,用户B与节点2建立连接,用户A发送一条消息到服务器,服务器无法再推送用户B

在WebSocket集群中,一个常见的问题是连接状态同步。当一个节点接收到客户端的连接请求并与其建立WebSocket连接时,其他节点也需要了解这个连接的存在。这样,当其他节点收到消息时,它们可以将消息正确地推送给与该客户端相关联的节点。

然而,如果连接状态同步存在问题,可能会导致消息发送错误或丢失。例如,在一个WebSocket集群中,当一个客户端连接到节点A时,而其他节点(B、C)不知道这个连接存在,当节点B收到一条消息并尝试将其推送给该客户端时,它会失败,因为它不知道该客户端连接到了节点A。

这种情况下,客户端可能会错过重要的消息或者体验不一致的消息推送。此外,如果客户端断开连接时,各个节点的连接状态同步也是至关重要的。如果某个节点仍然认为客户端处于活动状态,而实际上该客户端已经断开连接,节点可能会继续尝试将消息推送给它,从而浪费资源和带宽。

为了解决这个问题,需要在集群中实现连接状态的一致性和同步机制,以确保消息能够正确地传递和推送给客户端。

方案

方案一:Session共享(不可行)

在WebSocket集群中,共享Session的方法并不适用于解决连接状态同步的问题。虽然在HTTP中,可以使用共享Session来解决集群问题,例如将Session信息存储在Redis数据库中,但是在WebSocket中这种方法是不可行的。

WebSocket的Session与HTTP的Session有所不同。WebSocket的Session是与连接相关的状态,而不是像HTTP的Session那样与请求相关。WebSocket的连接在不同的服务器之间是无法共享的,因此无法将WebSocket的Session存储在共享的存储中。

换句话说Http短连接是无状态的,websocket是长链接的有状态,连接在哪个节点就在哪个节点了。

方案二:负载均衡器(状态路由)

在WebSocket集群中,使用负载均衡器进行状态路由是一种常见的解决方案。负载均衡器可以将客户端的连接请求分发到集群中的不同节点上,以实现负载均衡和高可用性。

用户A、用户B与节点1建立连接,用户C与节点2建立连接,用户A发送一条消息到服务器,服务器再推送给用户B

固定参数哈希(Fixed Parameter Hashing): 这种策略是根据请求中的特定参数(例如会议ID)进行哈希计算,然后将相同哈希结果的请求路由到同一节点上。这样可以确保同一会话或同一组相关连接始终被路由到同一个节点上。这种方法的优点是保持了连接的一致性,缺点会导致节点负载不均衡,因为某些参数值可能会导致较大的连接集中在某些节点上。

下面是一个示例的Nginx配置,展示了如何使用固定参数哈希进行WebSocket连接的负载均衡:

http {
    upstream websocket_backend {
        hash $arg_meeting_id consistent;
        server backend1.example.com:8080;
        server backend2.example.com:8080;
        server backend3.example.com:8080;
    }
    
    server {
        listen 80;
        
        location /websocket {
            proxy_pass http://websocket_backend;
            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection "Upgrade";
        }
    }
}

方案三:广播机制(异步方式 - 建议)

在集群中的每个服务节点上发送群发消息,以确保消息能够覆盖到所有连接的客户端。可以通过使用消息队列或广播协议来实现该机制。然而,需要注意的是广播机制可能会增加网络负载和处理成本

为了解决发送方和接收方必须在同一个服务器下才能发送消息的限制,可以采用消息广播的方式将消息通知给所有的服务器。这可以通过使用消息中间件的发布订阅模式来实现。通过将消息发送到中间件,然后再将消息广播给订阅的服务器,类似于广播的方式,只要订阅了消息,就能接收到消息的通知。

具体实现方式可以使用以下两种方式之一:

  1. Redis的发布订阅(Pub/Sub):使用Redis作为消息中间件,发布者将消息发布到特定的频道,而订阅者会订阅该频道,从而接收到消息的通知。
  2. 消息队列的广播:使用支持广播功能的消息队列,如RabbitMQ或Apache Kafka。发布者将消息发送到消息队列的特定主题(topic),而所有订阅了该主题的消费者都能接收到消息。

以上方案都将消息的发送和接收解耦,并通过消息中间件来实现跨服务器的消息广播。这样,无论连接的客户端分布在哪个服务器上,都能够接收到广播的消息。

方案四:路由转发(同步方式)

在WebSocket集群中,可以采用路由转发的方式来处理群发消息。该方案的核心思想是在集群的每个服务节点上维护一个路由表(查不到时去redis查),记录连接的客户端和对应的服务节点。当发消息时,根据路由表将消息转发到对应的服务节点,由该节点负责将消息发送给连接的客户端。

具体实现该方案需要以下步骤:

  1. 连接路由表:每个服务节点维护一个连接路由表,用于记录客户端连接的信息,包括客户端标识(如客户端ID)和对应的服务节点(查不到时去redis查)。
  2. 路由协议:实现一个路由协议,用于在集群中的各个服务节点之间传递路由信息和消息(用Http协议或者用websocket协议模拟一个客户端都行)。
  3. 路由转发:当需要发消息时,消息发送方的服务节点根据路由表确定每个连接的客户端所在的服务节点,并将消息转发给对应的服务节点。
  4. 消息发送:接收到转发的消息的服务节点负责将消息发送给连接的客户端,确保消息能够到达每个客户端。

通过路由转发方案,可以将群发消息的负载分散到各个服务节点上,避免了单个节点处理大量消息的压力。同时,该方案也能够确保消息能够正确路由和转发到每个连接的客户端。

方案五:高可用(多活冗余)

在WebSocket集群中,可以采用高可用多活冗余方案来提高系统的可用性和容错性。该方案的核心思想是将多台服务器同时工作,并允许客户端同时连接到多台服务器。当需要发送消息时,每台服务器都将消息发送给连接到它的客户端。

具体实现该方案的步骤如下:

  1. 多台服务器:配置多台服务器,每台服务器都运行相同的应用程序,形成一个集群。这些服务器可以位于不同的物理位置或云服务商上。
  2. 客户端连接多台服务器,每个客户端就可以同时与多台服务器建立连接。
  3. 消息发送:当需要发送消息时,每个服务器都将消息发送给连接到它的客户端。这意味着每个服务器都需要维护自己的连接列表,并向连接的客户端发送消息。
  4. 容错处理:如果某台服务器出现故障或断开连接,其他服务器仍然可以继续发送消息给客户端。

猜你喜欢

转载自blog.csdn.net/abu935009066/article/details/131402043