Websocket(一)~ Spring Websocket Session共享解决思路
Spring Websocket Session共享解决思路(背景)
在很久以前,我采用Nodejs
的方式,使用socket.io
实现了一把在线聊天。现在我需要实现一些功能,用户签到、抽奖还有抽奖、签到、任务信息完成播报等功能。
而这些任务完成,都要有一个特性,就是实时性,所以想用 Websocket 来尝试一下。接着就发现了一个问题, Session共享 的问题。
在这里,我不得再次批判一下中国的互联网问题。百度的问题。
- 你搜索的未必是你想要的,一大堆标题党。
- 你搜索的10篇结果,可能10篇都是一样的。
- 爬虫、复制过来的比原创排名要好,如XX之家,推酷等恶瘤。
Websocket Session 共享解决思路
好吧,言归正传。Websocket Session 共享?好多人想都不想,直接上来丢 Redis 、 Memcache 等等?常用的HttpSession
共享如下图
都是采用序列化和反序列化来实现,而Websocket Session
当你存储的时候会发现出现这样的问题。
java.io.NotSerializableException: org.springframework.web.socket.adapter.standard.StandardWebSocketSession
在知乎上看到讨论这个问题,摘抄一下。
Websocket的长连接通道一但关闭了websocket session就失效了。不像http协议每次用就去新建一个连接。你是可以知道是哪个用户,也知道要发哪些数据,但是问题在于。连接关闭后你发不过去。这也是websocketsession不能序列化的原因。自己构建的session不能解决长连接的问题。
而我的观点。
我觉得 Websocket session 不能序列化的原因不应该在此,关闭失效,我们有失效调用的close方法,这个时候我从共享池(cache)中remove即可,只要cache里有,那么我就可以针对用户发送请求,而且还有一个很现实的问题,我几个节点运行的时候,轮询的时候,从A点切换到B点的时候,用户就不认识了。而且每个节点只知道每个节点的websocketSession,这设计太扯了。
事实证明我的观点还是有遗漏,问题出在,上面引用的描述,它不像Http协议每次用就去新建一个 Session 。而是前端 JS 创建 Websocket 创建链接,就一个Websocket Session
的生命周期开启。当一段时间不使用的时候,Websocket会主动关闭。如下图。
再次使用会出现“WebSocket is already in CLOSING or CLOSED state.”
。所以在再次使用的时候,需要重新开启一个Websocket Session
,而这个开启,是有前端 JS
来完成的。
所以这个处理很简单,需要增加判断,或者try...catch
来完成重新创建。
Websocket Session 共享解决(方案一)
既然Websocket Session
不能直接序列化然后存储,而且如果用户请求的时候,开始与集群中的A
节点创建链接,就算把这个 Session 拿到B
节点去再给用户Push
消息,应该也是不通(没测试)的。
所以我的解决方案采用订阅式队列去触发 Websocket 反推的方式来实现看上去是 Session共享 的,思路如下图:
Websocket Session
共享 思路图解释:
模块一:这里没有什么好说明的,因为每一个用户只创建以一个 Session ,当用户再次链接到第二个节点的时候,第一个节点的 Session 就会关闭和销毁。而每个节点采用静态(static
)的成员变量去存储(Map
)当前节点的所有Websocket Session
。
模块二:因为每个节点存储的是每个节点的所有Websocket Session
,那么只需要判断下当前节点是否存在当前用户的Websocket Session
,存在则反推消息。
模块三:当有消息需要通过 Websocket 推送给对应的用户的时候,而又不知道这个用户在哪个节点,那么通过任务调度模块,把消息以订阅MQ
的方式放到 MQ 中,这样每个节点都可以收到消息,从而参照 “模块二” 判断当前用户是否存在当前节点。然后推送。
总结:目前我采用这种方式实现,没什么问题,但是是不是觉得太复杂了。是的,太重了,所以不推荐这样去解决。
Websocket Session 共享解决(方案二)
其实我们之所以要做共享,其实就是因为在集群环境中,在HA/Nginx等方案中,做轮询请求来减轻节点压力,这样导致用户可能分配在多个节点,那么如果我们采用IP等的Hash值规则,或者其他可以参考的规则。解决一个用户只会分配到一个节点上,那么就不存在Session共享了。这样的中间件好多。如我们的“伞神”提出的 采用阿里的 “Tengine
的session sticky
组件” 就可以解决。其他的也很多。
我要吃饭了... ...
版权所属:SO JSON在线解析
原文地址:https://www.sojson.com/blog/238.html
转载时必须以链接形式注明原始出处及本声明。
如果本文对你有帮助,那么请你赞助我,让我更有激情的写下去,帮助更多的人。