Ⅰ 聊聊 分布式 WebSocket 集群解决方案
WebSocketSession与HttpSession的对比
在Spring集成的WebSocket中,每个WebSocket连接都有一个对应的会话:WebSocketSession。在建立连接后,通过WebSocketSession进行与客户端的通信。然而,WebSocketSession无法序列化到Redis,因此在集群中无法实现会话共享。相反,HttpSession的Redis共享解决方案已存在。
尝试将关键信息缓存到Redis并重构WebSocket会话的方法并不可行。现在,让我们看看WebSocket会话与Http会话之间的区别。总的来说,Http会话共享有解决方案,且简单,通过引入spring-session-data-redis和spring-boot-starter-redis依赖即可实现。
Netty与Spring WebSocket的对比
最初,使用Netty实现WebSocket服务端搭建。Netty使用NIO模型,提供极高的并发量。然而,单独使用Netty可能会遇到一些问题。相比之下,Spring WebSocket被Spring Boot很好地集成,使用起来非常方便。实现WebSocket服务只需简单几步:添加依赖、配置类和实现消息监听类。为了更好地与Spring Cloud家族兼容,最终采用了Spring WebSocket。
应用服务架构与WebSocket集群解决方案
应用既负责RESTful服务,也负责WebSocket服务。未将WebSocket服务模块拆分是为了简化服务间调用。要实现WebSocket集群,从Zuul转型至Spring Cloud Gateway是必要的。Zuul 1.0不支持WebSocket转发,Zuul 2.0开始支持WebSocket,并且开源了。为了实现SSL认证和动态路由负载均衡,需要在YAML文件中配置某些项。还需配置一个filter以避免HTTP请求错误。至此,基本框架搭建完毕。
WebSocket集群通讯解决方案
最简单的解决方案是session广播,适用于并发需求不高的情况。此方法易于实现,但存在计算力浪费问题。为了解决此问题,可以使用一致性哈希算法。
一致性哈希算法应用到WebSocket集群,需要解决服务UP/DOWN问题。当服务器DOWN时,会自动关闭连接,影响哈希环映射。更新哈希环,避免转发至DOWN状态的服务器。当服务UP时,需要重新配置哈希环,或断开特定session连接。哈希环的实现细节较为复杂,需要根据实际情况设计算法。
网关如何根据WebSocket请求转发至指定服务器?
通过负载均衡实现。Spring Cloud Gateway或Zuul默认集成了Ribbon,需要根据客户端发来的UserID重写Ribbon负载均衡算法。将请求转发至哈希环上找到的IP地址。最后,用户沟通时,通过ID进行哈希计算,获取对应服务器上的会话。
Spring Cloud Finchley.RELEASE版本中Ribbon的不完善之处
在实际操作中发现Ribbon存在两个不完善之处。虽然暂时无法在Ribbon上实现一致性哈希算法,但可以通过客户端发起两次请求(一次HTTP,一次WebSocket)来间接实现。期待Ribbon更新以提供更优雅的WebSocket集群解决方案。