谈分布式环境下WebSocket消息共享问题
技术分析
我们在开发时会遇到需要使用即时通讯的场景,当然,实现方式很多,Socket、MQTT、Netty....等等。
具体用哪种就在于业务的需求了,去选择合理的方式实现。
今天小简要聊的场景便是分布式环境下,WebSocket的消息共享问题。
分布式环境下,业务方面往往最需要解决的是数据同步共享这类问题。
此时出现了一个场景,后端存在一个分布式服务,我需要两个服务都能收到WebSocket的消息,如何去实现?
或者说,服务端项目存在多个负载均衡实例,实例均在不同的实例上,这样当一次请求负载到A服务器实例时,socket的session在A服务器线程上,第二次请求负载到另一台B服务器的实例,此时B服务器并不存在A服务器的Session(即Socket的会话消息)。
思考解决
思路一(失败)
我们首先思考,改如何解决这个问题呢?要实现同步,根据上面的需求,我们可以直接定位到Socket的Session不能共享问题,只要可以共享会话对象,那就可以解决当前问题。
没错,小简也是这样想的,但是,实际上是错误的,请看下文。
我们首先联想到,分布式下,我们的分布式锁、分布式状态信息,都是可以通过Redis去实现一个共享的,那我们直接给Socket的Session通过Redis共享不就可以。
思路确实是对的,但是使用Redis共享对象是有条件的,要去实现Serializable接口,才可以被序列化。
![](https://i-blog.csdnimg.cn/blog_migrate/b172ec600dcac595ea571c75a3a9d12f.png)
我们查看源码就会发现,Socket的Session是不能被序列化的,那自然不能去使用Redis来实现Session对象的共享了。
为什么HttpSession可以使用Redis共享?
/**
* @author JanYork
* @date 2023/3/14 11:36
*/
org.apache.catalina.session.StandardManager
org.apache.catalina.session.PersistentManager
复制代码
WEB的中的HttpSession主要是通过上面的两个管理器实现序列化的。
StandardManager是Tomcat默认使用的,在web应用程序关闭时,对内存中的所有HttpSession对象进行持久化,把他们保存到文件系统中。
默认的存储文件为:<tomcat安装目录>/work/Catalina/<主机名>/<应用程序名>/sessions.ser
PersistentManager比StandardManager更为灵活,只要某个设备提供了实现org.apache.catalina.Store接口的驱动类,PersistentManager就可以将HttpSession对象保存到该设备。
![](https://i-blog.csdnimg.cn/blog_migrate/652427038160c2632ebb4141ef9238c7.png)
所以spring-session-redis解决分布场景下的session共享就是将session序列化到redis中,使用filter加装饰器模式解决分布式场景httpsession享问题。
注:此段参考自程序员DD大佬的文章。
思路二
既然不能共享对象,那我们共享消息不就可以,我们的目的是要其他实例也可以收到Socket的消息,那我们就是个1对n的消息模型,一对多消息那就简单了。
哪些方法?
首先我们会第一时间想到,MQ,也就是消息队列中间件。
其次我们也可以使用Redis的发布订阅功能实现。
MQ
使用MQ去实现一对多消息,相信也不需要我多说,MQ天然的广播、发布订阅、点对点、路由这些消息模式可以很方便的解决这个问题。
Redis
Redis实现有大佬已经写过了,请参考:
如何使用Redis解决WebSocket分布式场景下的Session共享问题: cloud.tencent.com/developer/a…
作者:JanYork_小简