微信小程序 关于websocket--心跳机制

332 阅读3分钟

「这是我参与2022首次更文挑战的第1天,活动详情查看:2022首次更文挑战

简述心跳机制

所谓心跳机制就是 定时发送一个自定义的心跳包(每隔固定时间间隔发一次,以此来告诉服务器,这个客户端还活着),让对方知道自己还活着,以确保连接有效性的机制。

为什么使用心跳检测

在长连接中,理论上客户端与服务端会保持连接,互相等待对方的消息。但在实际情况中,会出现一些意外情况,导致连接中断,而此时的客户端与服务端可能仍在等待对方发送消息。
我是在最近的一个小程序项目中使用websocket时遇到的这种问题,当时调日志时,发现报了以下错误:

java.io.EOFException: null at org.apache.tomcat.util.net.NioEndpoint$NioSocketWrapper.fillReadBuffer(NioEndpoint.java:1231) at org.apache.tomcat.util.net.NioEndpoint$NioSocketWrapper.read(NioEndpoint.java:1141) at org.apache.tomcat.websocket.server.WsFrameServer.onDataAvailable(WsFrameServer.java:75) at org.apache.tomcat.websocket.server.WsFrameServer.doOnDataAvailable(WsFrameServer.java:183) at org.apache.tomcat.websocket.server.WsFrameServer.notifyDataAvailable(WsFrameServer.java:162) at org.apache.tomcat.websocket.server.WsHttpUpgradeHandler.upgradeDispatch(WsHttpUpgradeHandler.java:156) at org.apache.coyote.http11.upgrade.UpgradeProcessorInternal.dispatch(UpgradeProcessorInternal.java:60) at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:59) at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:888) at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1597) at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128) at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628) at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) at java.base/java.lang.Thread.run(Thread.java:829)

代码中可以看出,是websocket出现了错误,但不知道具体原因,所以只能上网查了一下,很多人说可能是使用的 nginx代理服务器中 连接超时时间的配置问题: keapalive_timeout = 60; 在60s内若未使用websocket通信,就会自动断开连接,而不会通知客户端和后端。

所以有两种解决方案:
1. 调整 keapalive_timeout 的大小
2. 使用心跳机制

第一种方法我没有尝试, 因为我不怎么了解nginx,所以不敢妄动。
所以就先尝试了一下第二种方法:
根据项目的需求,我的解决思路是, 配置 心跳检测机制,当客户端或服务端在 指定时间内 未使用该连接时,就发送一次请求,以此来保证连接的有效性,避免被nginx自动断开。当websocket是主动断开时,就关闭心跳检测。

代码部分:

小程序:

heartBeat : { // 心跳对象 
    timeout : 50000, // 心跳频率 
    timeoutObj : null, // 心跳检测对象 
    socketObj: null, // socketTask对象 
    _reset: function() { // 重置心跳检测 
        clearTimeout(this.timeoutObj); 
        return this; 
    }, 
    _start: function() { // 开启心跳检测 
        this.timeoutObj = setTimeout(() => { 
            this.socketObj.send({ 
            data: 'heartbeat', 
            success:(res) => { 
                console.log("心跳检测发送成功"); 
            }, 
            fail:(err) => { 
                console.log("心跳检测发送失败"); 
                this._reset(); 
            } 
            }) 
        }, this.timeout) 
    }, 
}

而后端只需校验了一下发送过来的消息,然后再返回一条消息。这里就不贴出来了。\

在加入了心跳机制后,发现日志中再没有出现类似的问题,应该已经解决了。但还是有些问题仍未明白:
就是 websocket中 是否含有 关于心跳检测的配置,在其内置的话应该更方便一些吧。

以上就是本次出现的问题的解决过程, 在此记录下来,以免后续再碰到类似情况时不知所措。第一次写博客,如果能帮到你的话那就更好,若是哪些地方不对,有问题,也希望不吝批评指正。

本文参考:
百度百科--心跳机制
小程序关于封装心跳检测、断线重连等方法的类