websocket stomp Error: Session c

2023-10-19  本文已影响0人  哈哈11122

问题描述

所见即所得,上图片。浏览器为chrome
使用stomp-js做客户端,spring框架的stomp做服务端。建立了连接,然后订阅了一个topic。过了一段时间后,client端收到了server端发来的消息:Error message:Session closed(连接被关闭了)。

stomp连接断开.jpg
console里打印日志:Connection closed to ws://xx.xx.xx

代码

不贴代码就巴拉巴拉的那些小魔仙们,不觉得自己在耍流氓么?
js代码:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>ActiveMq推送:消费端.</title>
    <!--<link rel="stylesheet" type="text/css" href="default.css">-->
    <!--<link rel="stylesheet" type="text/css" href="jquery.notify.jcss">-->
    <script type="text/javascript" src="stomp.umd.js"></script>
    <script type="text/javascript" src="jquery-3.7.1.min.js"></script>
</head>
<script type="text/javascript">
    $(document).ready(function () {
        //创建客户端
        const client = new StompJs.Client({
            brokerURL: 'ws://localhost:8080/websocket/caltta-cm/',
            connectHeaders: {
                login: ' ',
                passcode: ' ',
            },
            debug: function (str) {
                console.log(str);
            },
            reconnectDelay: 5000,
            heartbeatIncoming: 5000,
            heartbeatOutgoing: 5000,
        });

    var connect = function (frame) {
            //订阅主题的消息
            client.subscribe("/topic/cm_ne_status_topic", function (message) {
                //弹出业务消息提醒
                if (message.body) {
                  console.log("got message with body " + message.body)
                } else {
                  alert("got empty message");
                }
            });
    };
    client.onConnect = connect;

client.onStompError = function (frame) {
  // Will be invoked in case of error encountered at Broker
  // Bad login/passcode typically will cause an error
  // Complaint brokers will set `message` header with a brief message. Body may contain details.
  // Compliant brokers will terminate the connection after any error
  console.log('Broker reported error: ' + frame.headers['message']);
  console.log('Additional details: ' + frame.body);
};

client.activate();
        
    });
</script>
<body>
<div id="body"></div>
</body>
</html>

java代码:

@Configuration
@EnableWebSocketMessageBroker
public class BrokerWebSocketConfig implements WebSocketMessageBrokerConfigurer {
    public static final String SUBSCRIBE_PATH = "/websocket/";
    public static final String TOPIC_PATH = "/topic/";

    private TaskScheduler messageBrokerTaskScheduler;
    @Value("${spring.application.name}")
    private String applicationName;

    @Autowired
    public void setMessageBrokerTaskScheduler(@Lazy TaskScheduler taskScheduler) {
        this.messageBrokerTaskScheduler = taskScheduler;
    }

    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        registry.addEndpoint(SUBSCRIBE_PATH + applicationName + "/").setAllowedOrigins("*");
    }

    @Override
    public void configureMessageBroker(MessageBrokerRegistry registry) {
        registry.setApplicationDestinationPrefixes("/app");
        registry.enableSimpleBroker(TOPIC_PATH)
                .setHeartbeatValue(new long[]{5000, 5000})
                .setTaskScheduler(this.messageBrokerTaskScheduler);
    }
}

问题分析

this._pinger = setInterval(() => {
                    console.log("***发送心跳前,判断状态StompSocketState。***")
                    if (this._webSocket.readyState === exports.StompSocketState.OPEN) {
                        this._webSocket.send(BYTE.LF);
                        this.debug('>>> PING');
                    }else{
                        console.log("this._webSocket.readyState状态关闭了,所以没有发送心跳(PING)")
                    }
                }, ttl);

上面是stomp.js发送心跳的定时器,我设置的是5秒发送一次。但是,从截图中发现,client端并没有按时发送心跳给server端,导致server端认定client端挂掉了(server端判断client端挂掉的逻辑在SimpleBrokerMessageHandler类中,我这个例子中就是超过5*3=15秒不发送心跳就判定client挂了),所以断开了连接。追根溯源,client端不发送心跳给server端的原因是什么?答案就是计时器没有按时执行,这篇帖子里给出了答案JS定时器执行不可靠的原因及解决方案

解决方案1(上厕)

计时器没有按时间执行,因为可能当时在忙着上厕所,谁还没有个急事呢。解决这个问题很简单,把心跳检测的时间参数(heartbeatIncoming和heartbeatOutgoing)设定的大一些就行了,等计时器上完厕所再发心跳给server端。

const client = new StompJs.Client({
            brokerURL: 'ws://localhost:8080/websocket/caltta-cm/',
            connectHeaders: {
                login: ' ',
                passcode: ' ',
            },
            debug: function (str) {
                console.log(str);
            },
            reconnectDelay: 5000,
            heartbeatIncoming: 120000,
            heartbeatOutgoing: 120000,
        });

解决方案2(下厕)

修改stomp.js里发送心跳的定时器,换成webworker(代码自行百度)。这种方法的缺点就是麻烦。优点是心跳检测可以精确到秒,而不会报错。

this._pinger = window["reliableSetInterval"](() => {
                    if (this._webSocket.readyState === exports.StompSocketState.OPEN) {
                        this._webSocket.send(BYTE.LF);
                        //this.debug('>>> PING');
                    }
                }, ttl);

补充说明

即使改了参数,难保不挂掉。stomp帮你加了重新机制,但重连接后还需要重新订阅topic哦,切记!
了解原理很重要,附上stomp协议

上一篇 下一篇

猜你喜欢

热点阅读