长连接的心跳及重连设计(上)

简介: 什么场景下需要心跳呢?目前我们接触到的大多是一些基于长连接的应用需要心跳来“保活”。由于在长连接的场景下,客户端和服务端并不是一直处于通信状态,如果双方长期没有沟通则双方都不清楚对方目前的状态;所以需要发送一段很小的报文告诉对方“我还活着”。同时还有另外几个目的:服务端检测到某个客户端迟迟没有心跳过来可以主动关闭通道,让它下线。客户端检测到某个服务端迟迟没有响应心跳也能重连获取一个新的连接。

心跳实现方式


心跳其实有两种实现方式:


  • TCP 协议实现(keepalive 机制)。


  • 应用层自己实现。


由于 TCP 协议过于底层,对于开发者来说维护性、灵活度都比较差同时还依赖于操作系统。


所以我们这里所讨论的都是应用层的实现。



如上图所示,在应用层通常是由客户端发送一个心跳包 ping 到服务端,服务端收到后响应一个 pong 表明双方都活得好好的。


一旦其中一端延迟 N 个时间窗口没有收到消息则进行不同的处理。


客户端自动重连


先拿客户端来说吧,每隔一段时间客户端向服务端发送一个心跳包,同时收到服务端的响应。


常规的实现应当是:


  • 开启一个定时任务,定期发送心跳包。


  • 收到服务端响应后更新本地时间。


  • 再有一个定时任务定期检测这个“本地时间”是否超过阈值。


  • 超过后则认为服务端出现故障,需要重连。


这样确实也能实现心跳,但并不友好。


在正常的客户端和服务端通信的情况下,定时任务依然会发送心跳包;这样就显得没有意义,有些多余。


所以理想的情况应当是客户端收到的写消息空闲时才发送这个心跳包去确认服务端是否健在。


好消息是 Netty 已经为我们考虑到了这点,自带了一个开箱即用的 IdleStateHandler 专门用于心跳处理。


来看看 cim 中的实现:



pipeline 中加入了一个 10秒没有收到写消息的 IdleStateHandler,到时他会回调 ChannelInboundHandler 中的 userEventTriggered 方法。



所以一旦写超时就立马向服务端发送一个心跳(做的更完善应当在心跳发送失败后有一定的重试次数);


这样也就只有在空闲时候才会发送心跳包。


但一旦间隔许久没有收到服务端响应进行重连的逻辑应当写在哪里呢?


先来看这个示例:


当收到服务端响应的 pong 消息时,就在当前 Channel 上记录一个时间,也就是说后续可以在定时任务中取出这个时间和当前时间的差额来判断是否超过阈值。


超过则重连。



同时在每次心跳时候都用当前时间和之前服务端响应绑定到 Channel 上的时间相减判断是否需要重连即可。


也就是  heartBeatHandler.process(ctx); 的执行逻辑。


伪代码如下:


@Override
public void process(ChannelHandlerContext ctx) throws Exception {
    long heartBeatTime = appConfiguration.getHeartBeatTime() * 1000;
    Long lastReadTime = NettyAttrUtil.getReaderTime(ctx.channel());
    long now = System.currentTimeMillis();
    if (lastReadTime != null && now - lastReadTime > heartBeatTime){
        reconnect();
    }
}


IdleStateHandler 误区


一切看起来也没毛病,但实际上却没有这样实现重连逻辑。


最主要的问题还是对 IdleStateHandler 理解有误。


我们假设下面的场景:


  1. 客户端通过登录连上了服务端并保持长连接,一切正常的情况下双方各发心跳包保持连接。


  1. 这时服务端突入出现 down 机,那么理想情况下应当是客户端迟迟没有收到服务端的响应从而 userEventTriggered 执行定时任务。


  1. 判断当前时间 - UpdateWriteTime > 阈值 时进行重连。


但却事与愿违,并不会执行 2、3两步。


因为一旦服务端 down 机、或者是与客户端的网络断开则会回调客户端的 channelInactive 事件。


IdleStateHandler 作为一个 ChannelInbound 也重写了 channelInactive() 方法。



这里的 destroy() 方法会把之前开启的定时任务都给取消掉。


所以就不会再有任何的定时任务执行了,也就不会有机会执行这个重连业务


相关文章
|
8月前
|
前端开发
websocket的心跳机制
websocket的心跳机制
395 3
|
Java Windows
JavaWebSocket心跳机制详解
WebSocket是一种在Web浏览器和服务器之间进行全双工通信的协议,它提供了一种简单而强大的方式来实现实时数据传输。在使用WebSocket时,心跳机制是非常关键的,它能够保持连接的稳定性并及时发现连接的异常。本文将详细解释JavaWebSocket心跳机制的实现原理和步骤。
540 0
|
5月前
|
网络协议 Java
JAVA实现心跳检测【长连接】
这篇文章介绍了Java中实现心跳检测机制的方法,包括心跳机制的简介、实现方式、客户端和服务端的代码实现,以及具体的测试结果。文中详细阐述了如何通过自定义心跳包和超时检测来维持长连接,并提供了完整的客户端和服务端示例代码。
JAVA实现心跳检测【长连接】
|
6月前
WebSocket 心跳机制如何实现
WebSocket 心跳机制如何实现
77 0
|
8月前
MQTT的心跳保活机制是通过`setKeepAliveInterval()`方法设置的
MQTT的心跳保活机制是通过`setKeepAliveInterval()`方法设置的
1124 2
|
8月前
MQTT协议本身是支持心跳保活机制的
MQTT协议本身是支持心跳保活机制的
726 3
|
8月前
MQTT的心跳保活机制是通过`setKeepAliveInterval()`方法设置的,
MQTT的心跳保活机制是通过`setKeepAliveInterval()`方法设置的,
620 1
|
tengine 网络协议 Linux
关于长连接服务器和客户端之间要加入心跳的一些讨论
关于长连接服务器和客户端之间要加入心跳的一些讨论
|
8月前
|
移动开发 HTML5
WebSocket心跳机制
WebSocket心跳机制
90 0
|
网络协议
选择长连接 or 短连接,大量 Timewait 的产生时如何处理?
网络通讯中,常见的两个连接类型分别是长连接和短连接。长连接指在一定时间内保持连接不断开,而短连接则指每次连接只进行一次通信,通信结束后即时断开连接。在实际应用中,不同类型的连接有着不同的应用场景和优缺点,而且在网络通讯中可能会遇到大量 Timewait 的产生,这就需要针对不同情况选择不同的处理方案。
123 1