「这是我参与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中 是否含有 关于心跳检测的配置,在其内置的话应该更方便一些吧。
以上就是本次出现的问题的解决过程, 在此记录下来,以免后续再碰到类似情况时不知所措。第一次写博客,如果能帮到你的话那就更好,若是哪些地方不对,有问题,也希望不吝批评指正。