websocket stomp连接一段时间后断开

作者: 朱端的一坨 | 来源:发表于2016-07-01 12:17 被阅读5716次

    背景概述

    因为项目中存在频繁的由服务器发起的数据交换,相比使用Ajax轮训的方式,websocket长连接和双向保持的特点能够较好的提升数据交换的性能。
    为了简便,直接使用spring boot + shiro + stomp和socketJs作为构建的工具。
    但是由于使用时,主要是由服务端进行数据的推送,通过stomp自行保持心跳,就会存在session过期导致连接断开的情况,而且由于stomp本身不会提示错误原因,导致排查起来比较麻烦,因此记录下整个纠错过程以备忘。

    原因排查

    1. 问题现象:

    在一开始打开时,一切正常,数据能推送成功,但是在大约10分钟左右(时长不定)能通过chrome的前端调试工具发现stomp提示连接断开。服务器端无任何异常提示。

    2. 原因排查:

    因为服务器端无任何异常,且断连时长不定,因此推测为可能是session导致的自动断链或者心跳丢失导致的。因为stomp本身不会报错误原因,因此想要找到具体的错误原因比较麻烦。后来发现可以通过断点到stomp的onclose时间就能够获取到具体的错误码,这就为我们定位问题提供了很好的帮助(具体断点位置如下图所示)


    error code.PNG
    3. 问题确定

    最后通过错误码发现,提示的错误码是1008,reason部分提示的是http session被关闭,因此问题就比较清楚了,是由于session释放导致的断链。这一点就很有意思,因为在使用过程中为了避免不必要的数据传输就一直没有发起websocket的send事件,只是一直在subscribe监听服务器的推送。因此导致了服务器的session一直没有被touch(),从而释放。(其实个人觉得这个可能也是一个比较有意思的问题,为什么心跳没有被处理会触发到session,具体没有试验是不是和我使用了shiro的session作为管理有关,有兴趣的同学们可以尝试一下)
    这里也顺便贴上对于错误码的描述和相关参考资料,便于大家解决其它的错误码问题:

    1. 首先关于1008 错误错误码的描述如下 Java WebSocket 规范

    如果用户退出了包含的web应用,或如果身份验证超时,或由于其他一些原因无效的。在这种情况下,websocket实现必须立即使用websocket关闭状态码1008关闭连接

    1. 关于错误码的具体原因 WebSocket Protocol
    all code.PNG
    4. 问题解决

    那么问题找到了响应的解决方案就比较好处理了,但是究竟哪种方案比较好,还是得看具体的业务需求和对利弊的取舍。

    1. 可以通过修改session的过期时间来控制连接的时长(但是可能没有那么精准),如果为了简便以可以设置为永远不过期(但是永远不过期有很多潜在问题)。笔者主要使用了shiro作为session的管理工具,设置session不过期的代码如下:
    Subject currentUser = SecurityUtils.getSubject(); 
    Session session = currentUser.getSession();
     //注意这里单位为ms,且会向s向下取整
    session.setTimeout(-1000); 
    
    1. 通过服务器端主动发送send前,手动调用一下session的touch()函数,但是这种方法感觉比较不合理,而且需要注意send和touch的顺序关系,不然容易报response已经commit后再重建session的异常
    Subject currentUser = SecurityUtils.getSubject(); 
    Session session = currentUser.getSession();
    session.touch();
    //注意顺序
    messagingTemplate.convertAndSend(desUrl, value);
    
    1. 周期性通过客户端发送一个send消息到服务器,维持session的不过期。这个方案实现比较简单,而且对服务器的影响较小,唯一不爽的一点是感觉有点违背了当时服务器推送的初衷(毕竟已经发送心跳保持激活了,还再发送send有点多此一举的感觉),不过综合考虑到实际处理中笔者可以通过send来帮助控制及时取消掉一些observer的订阅,因此最后选择了这个方案。
           function listenInfo(){
                var listenUrl = "/queue/xxx/" ;
                stompClient.subscribe(listenUrl, function (data) {  
                    setChangedAccount(data);                
                    var currentDate = new Date();
                    //50s send once(server expire is 60s)
                    if((currentDate.getTime()-lastSendDate.getTime())>=50000){      
                        sendInfo(userID);
                        lastSendDate = new Date();
                    }
                });    
            }
    

    结语

    问题本身倒是不复杂,就是对stomp不熟悉,确定具体的错误原因上花了很多时间,进行了很多白费的尝试,所以希望能记录下,做个以后的备忘,防止再走很多弯路。

    相关文章

      网友评论

      • 郎中_大橙子:大神我的是websocket 连接 stomp 一分钟就自动断开了 错误码是1000 有遇到过吗
        a0f8f3fa87d6:解决了么?我现在遇到和你一样问题咯
        郎中_大橙子:@朱端的一坨 应该是session过期的原因,写个定时器50秒往mq发消息就不断了,哪里可以设置rabbitmq超时时间吗
        朱端的一坨:@郎中_大成 没有呢
      • 艿頩o:我的问题是,需要在这个基础之上加一个断网重连机制!弄了好久,哪位大佬帮帮忙
      • 25079c021976:你好,我出现的错误码是1006。但是解释不懂呢。请问一下有什么解决的办法吗?
        朱端的一坨:您好,1006我也没遇到过,所以不太清楚了,具体调试下看看吧 :)
      • f1964b421f79:想问下你怎么把shirosession和websocketsession绑定在一起的 也是页面参数传递么?
        朱端的一坨:@我住隔壁我姓赵 我是配置起来就已经这样了,可能和我直接使用了spring boot自动配置的websocket接口有关。至于如何传递的,应该就是shiro自己的传递机制吧,sockjs的具体实现不太清楚,不过jsession应该就是带上字串过去的

      本文标题:websocket stomp连接一段时间后断开

      本文链接:https://www.haomeiwen.com/subject/fpmfjttx.html