javascript
Springboot整合Websocket遇到的坑_websocket session不支持序列化,无法存储至redis_Websocket相关问题总结(Session共享,用户多端登录等)
Springboot整合Websocket遇到的坑
一、使用Springboot內(nèi)嵌的tomcat啟動(dòng)websocket
1.添加ServerEndpointExporter配置bean
@Configuration public class WebSocketConfig {/*** 服務(wù)器節(jié)點(diǎn)** 如果使用獨(dú)立的servlet容器,而不是直接使用springboot的內(nèi)置容器,就不要注入ServerEndpointExporter,因?yàn)樗鼘⒂扇萜髯约禾峁┖凸芾? @return*/@Beanpublic ServerEndpointExporter serverEndpointExporter() {return new ServerEndpointExporter();}}2.在接收連接的類(lèi)加上@ServerEndpoint和@Component
@ServerEndpoint("/connect") @Component二、使用外部tomcat容器啟動(dòng)websocket
1.刪除ServerEndpointExporter配置bean
2.接收連接的類(lèi)刪除@Component
三、websocket關(guān)閉連接異常
如果客戶(hù)端關(guān)閉了websocket,但服務(wù)端沒(méi)有監(jiān)聽(tīng)到關(guān)閉事件,即onClose方法沒(méi)有調(diào)用,這是會(huì)發(fā)生的情況
此時(shí)如果服務(wù)端向客戶(hù)端推送消息,會(huì)出現(xiàn)異常告訴開(kāi)發(fā)者:關(guān)閉了一個(gè)連接,并重新調(diào)用onClose方法
websocket 分布式開(kāi)發(fā),websocket session不支持序列化,無(wú)法存儲(chǔ)至redis
單websocket服務(wù)器在面對(duì)并發(fā)量很大時(shí)壓力會(huì)很大,而且session儲(chǔ)存在Map中,內(nèi)存壓力也會(huì)很大。于是考慮分布式。
但是分布式存在websocket session共享問(wèn)題,于是考慮radis存儲(chǔ)session,但是遇到websocket session不支持序列化,無(wú)法存儲(chǔ)。
一番搜索后有了以下幾個(gè)方案
使用spring session自定義session.
既然無(wú)法序列化session,那還是存儲(chǔ)在Map中,各服務(wù)器通過(guò)發(fā)布訂閱變相實(shí)現(xiàn)共享websocket session.
Websocket相關(guān)問(wèn)題總結(jié)(Session共享,用戶(hù)多端登錄等)
我們?cè)谑褂脀ebsocket的時(shí)候其實(shí)主要面對(duì)的問(wèn)題就是session共享的問(wèn)題:
不管是基于Spring實(shí)現(xiàn)的Websocket的WebsocketSession
還是基于JDK實(shí)現(xiàn)的Session
亦或者基于netty實(shí)現(xiàn)的ChannelHandlerContext
用圖來(lái)描述下場(chǎng)景吧:
OK,大家看到這個(gè)圖了,差不多應(yīng)該明白了Session共享應(yīng)該怎么處理了。其實(shí)原理很簡(jiǎn)單:
1、我們知道nginx有IP保持的功能,其實(shí)這個(gè)功能就能解決大部分場(chǎng)景的Session共享問(wèn)題。 但是某些極限情況下還是會(huì)有問(wèn)題,比如在瀏覽器沒(méi)有關(guān)閉的情況下同一個(gè)用戶(hù)更換了網(wǎng)絡(luò)的情況導(dǎo)致IP變了,或者對(duì)于某些網(wǎng)絡(luò)的IP是變動(dòng)的情況下,就會(huì)出現(xiàn)Session找不到的情況。
2、基于上述nginx的原理我們可以進(jìn)行優(yōu)化,還是單例存儲(chǔ)。那么要操作的時(shí)候,我告訴所有的服務(wù)端,你們?nèi)フ疫@個(gè)用戶(hù)的Session,并把消息帶過(guò)去。那么相應(yīng)的節(jié)點(diǎn)根據(jù)用戶(hù)拿到Session了就可以進(jìn)行處理了。
上面2點(diǎn)大概簡(jiǎn)單的描述了下Session共享的原理,那么有這么個(gè)場(chǎng)景,文字可能不太好表達(dá),我們還是用圖來(lái)說(shuō)明:
一般出現(xiàn)多端情況也應(yīng)該就上面2種情況,要么允許,要么不允許。我這里簡(jiǎn)單的說(shuō)下不允許的處理流程。
建立連接的時(shí)候,先獲取老的Session
Session oldSession = SOCK_MAP.get(baseStudentInfo.getId());存在,則推送關(guān)閉消息,不存在告知其他節(jié)點(diǎn)去清楚。當(dāng)然本節(jié)點(diǎn)的的Server要排除在外,這里就通過(guò)IP判斷即可。
if(oldSession!=null) {oldSession.getBasicRemote().sendObject(close);}else{//關(guān)閉其他節(jié)點(diǎn)的的sessionauthService.pushCloseMessage(close);}//替換SOCK_MAP.put(baseStudentInfo.getId(),session);消息監(jiān)聽(tīng)
String serverIp = IPUtils.getLocalhostIp();logger.info("當(dāng)前IP:"+serverIp);logger.info("content的IP:"+wsMessage.getBody().getContent());//IP不相等,說(shuō)明不是當(dāng)前連接的服務(wù)端,關(guān)閉其他端口if(!serverIp.equals(wsMessage.getBody().getContent())){//關(guān)閉session,并返回給前端customerHandler.closeSession(wsMessage.getBody().getReceiver(), wsMessage);}關(guān)閉的方法:
/*** 關(guān)閉Session* @param studentId* @param closeMessage*/public void closeSession(Long studentId,WsMessage closeMessage){Session session = SOCK_MAP.get(studentId);if(session!=null) {try {session.getBasicRemote().sendObject(closeMessage);SOCK_MAP.remove(studentId);//清除redislogger.info("連接已關(guān)閉:" + studentId);} catch (Exception e) {e.printStackTrace();logger.error("關(guān)閉連接異常");}}}這樣基本就避免多端登錄的問(wèn)題,如果允許多端登錄的時(shí)候只需要更改存儲(chǔ),更改發(fā)送消息變成群發(fā)即可。
總結(jié)
以上是生活随笔為你收集整理的Springboot整合Websocket遇到的坑_websocket session不支持序列化,无法存储至redis_Websocket相关问题总结(Session共享,用户多端登录等)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 【深度学习】卷积越大性能越强!RepLK
- 下一篇: qml 鼠标点击_QML ListVie