Spring Websocket Session共享解决思路

 Spring Websocket Session共享解决思路(背景)

 

Websocket Session 共享?好多人想都不想,直接上来丢  Redis  、  Memcache  等等?常用的HttpSession 共享如下图

都是采用序列化和反序列化来实现,而Websocket Session  当你存储的时候会发现出现这样的问题。

 
  1. 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共享了。这样的中间件好多。如我们的“伞神”提出的 采用阿里的 Tenginesession sticky 组件” 就可以解决。其他的也很多。

你可能感兴趣的:(webSocket)