基于spring-boot、spring-cloud的websocket服务器多点负载均衡改造

时间:2021-03-17 16:11:34

背景

为应对更多用户使用socket的场景,准备对存放websocket的服务器进行多点搭建并配置负载均衡。

问题

服务器上了多点负载均衡以后,基于socket的部分功能发生了有规律的失效,查看后台日志发现了原因。

基于socket的功能使用的session存放在其他负载均衡的服务器上,所以在当前服务器无法实现相应操作。

举个实例,有两台加了负载的socket服务器分别为A、B。服务器A拥有用户1的socket连接即session句柄,服务器B拥有用户2的session句柄,

此时client端发送给用户1一条消息,消息内容是“kill程序员祭天消息”,由于socket服务器的负载均衡策略,该消息可能被发送到A或B服务器。

当A服务器接收到该请求时,A服务器可以完成给用户1发送消息的任务,因为A服务器拥有可以给用户1发送消息的句柄。但是服务器B接收到此消息的时候,B尝试着寻找用户1的句柄但是在列表当中没有找到该句柄,所以无法将消息发送出去,此时消息丢失了。换言服务器只能与当前建立连接的用户进行通信。

解决方案的尝试

不可行的方案

redis集中管理连接,这种方式是最开始想到的一种方式,这种方法的好处在于redis集中管理用户连接。该方法预期将用户的连接信息存放在redis服务器,当服务器接收到发送消息请求时,去redis获取与用户的连接,然后发送消息。

然而这种方法夭折了~~,问题在于session管理着与用户的长连接,该数据无法存入redis。笔者尝试着将session以HashMap、Object形式存入redis,此时redis报错。另外尝试将session以Json形式存入redis,但是在session转为Json的时候发现session是不能被序列化的,自然不能转为JSON格式了。

可行的方案

使用kafka广播形式,将消息广播到各服务器。首先将client端的请求吐入kafka,并且配置各接收服务器使用广播方式接收,即使用不同群组接收同一个topic,这样每个服务器都能接收到该请求,但是只有一个服务器可以将消息发出。该方法存在缺点但确实解决了此问题。该方法缺点在于,虽然保证的socket的负载均衡,但是服务器down掉以后,该服务器与用户的长连接就消失了。

哪路神仙有更好的方案,欢迎在评论区回复~~