WebSocket集群方案实践

WebSocket集群方案实践

单服务节点 ws 可以很好的结合业务服务处理后主动推送到前端,从而实时的将数据同步给前端。

相比于Http的无状态通信,服务端主动推送是有状态协议的,客户端连接服务器时只和集群中一个节点连接,数据传输过程中也只与这一节点通信,在集群多台服务器环境下,我们就出现了服务端部分消息推送丢失的现象。

image-20231022142429805

问题分析和整体思路

客户端和服务端每次建立连接时候,会创建有状态的会话Session,服务器的保存维持连接的Session。客户端每次只能和集群服务器其中的一个服务器连接,后续也是和该服务器进行数据传输。决集群的问题,应该考虑Session的问题,客户端成功连接服务器之后,其他服务器也知道客户端连接成功。

可以使用Nginx负载均衡的ip hash算法,客户端每次都是请求同一个服务器,客户端的session都保存在服务器上,而后续请求都是请求该服务器,都能获取到session,就不存在分布式session问题了。websocket相对http来说,可以由服务端主动推动消息给客户端,如果接收消息的服务端和发送消息消息的服务端不是同一个服务端,发送消息的服务端无法找到接收消息对应的session,即两个session不处于同一个服务端,也就无法推送消息。 解决问题的方法是将所有消息的发送方和接收方都处于同一个服务器下,而消息发送方和接收方都是不确定的,显然是无法实现的。将消息的发送方和接收方都处于同一个服务器下才能发送消息,那么可以转换一下思路,可以将消息以消息广播的方式通知给所有的服务器,可以使用消息中间件发布订阅模式,消息脱离了服务器的限制,通过发送到中间件,再发送给订阅的服务器,类似广播一样,只要订阅了消息,都能接收到消息的通知。

解决方案

WebSocket是有状态的,无法像直接HTTP以集群方式实现负载均衡,长连接建立后即与服务端某个节点保持着会话,因此集群下想要得知会话属于哪个节点,有两种方案,

方案 优点 缺点
注册中心 会话映射关系清晰,集群规模大时更合适 实现复杂、依赖注册中心,需自行实现,额外开发、运营成本
事件广播 实现简单、更多轻量 节点较多时,所有节点都会广播,资源浪费较多

事件广播

综合考虑实现成本与集群规模,选择了轻量级的事件广播方案。实现广播可以选择基于RocketMQ的消息广播、基于Redis的Publish/Subscribe、基于服务的通知等方案,其优缺点对比如表所示。从实时性、实现难易等方面考虑,同时对于持久化高可靠级别并没有太高要求,最终选择了Redis

方案 优点 缺点
基于 RocketMQ 吞吐高、高可用、高可靠 实时性不如 redis
基于 Redis 实时性高、实现简单 不保证可靠
基于 Zookeeper 实现简单 写入性能较差,不适合频繁写入场景

改造后架构图如下

image-20231022144735259

核心实现

性能测试

若有收获,就点个赞吧

上次更新时间: 2024/5/7 05:59:02