一、引入
在 TCP 保持长连接的过程中,可能会出现断网等网络异常出现,异常发生的时候, client 与 server 之间如果没有交互的话,它们是无法发现对方已经掉线。
二、工作原理
在 client 与 server 之间在一定时间内没有数据交互时, 即处于 idle 状态时, 客户端或服务器就会发送一个特殊的数据包给对方, 当接收方收到这个数据报文后, 也立即发送一个特殊的数据报文, 回应发送方, 此即一个 PING-PONG 交互。所以, 当某一端收到心跳消息后, 就知道了对方仍然在线, 这就确保 TCP 连接的有效性。
TCP 实际上自带的就有长连接选项,本身是也有心跳包机制,也就是 TCP 的选项:SO_KEEPALIVE。但是,TCP 协议层面的长连接灵活性不够。所以,一般情况下我们都是在应用层协议上实现自定义心跳机制的,也就是在 Netty 层面通过编码实现。通过 Netty 实现心跳机制的话,核心类是 IdleStateHandler 。
三、实现
在
Netty
中, 实现心跳机制的关键是
IdleStateHandler
public IdleStateHandler(int readerIdleTimeSeconds, int writerIdleTimeSeconds, int allIdleTimeSeconds) {
this((long)readerIdleTimeSeconds, (long)writerIdleTimeSeconds, (long)allIdleTimeSeconds, TimeUnit.SECONDS);
}
参数的含义:
-
readerIdleTimeSeconds
: 读超时. 即当在指定的时间间隔内没有从
Channel
读取到数据时, 会触发一个
READER_IDLE
的
IdleStateEvent
事件. -
writerIdleTimeSeconds
: 写超时. 即当在指定的时间间隔内没有数据写入到
Channel
时, 会触发一个
WRITER_IDLE
的
IdleStateEvent
事件. -
allIdleTimeSeconds
: 读/写超时. 即当在指定的时间间隔内没有读或写操作时, 会触发一个
ALL_IDLE
的
IdleStateEvent
事件.
**注意:**这三个参数默认的时间单位是秒。
心跳处理类:
ClientIdleStateTrigger
/**
* <p>
* 用于捕获{@link IdleState#WRITER_IDLE}事件(未在指定时间内向服务器发送数据),然后向<code>Server</code>端发送一个心跳包。
* </p>
*/
public class ClientIdleStateTrigger extends ChannelInboundHandlerAdapter {
public static final String HEART_BEAT = "heart beat!";
@Override
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
if (evt instanceof IdleStateEvent) {
IdleState state = ((IdleStateEvent) evt).state();
if (state == IdleState.WRITER_IDLE) {
// write heartbeat to server
ctx.writeAndFlush(HEART_BEAT);
}
} else {
super.userEventTriggered(ctx, evt);
}
}
}
四、源码剖析
第254行代码其实表示该方法只是进行了透传,不做任何业务逻辑处理,让channelPipe中的下一个
handler
处理
channelRead
方法,但是记录了一下这里的调用时间
channelActive方法
initialize方法
这边会触发一个Task,ReaderIdleTimeoutTask,这个task是部分源码
341行是这样的,用当前时间减去最后一次
channelRead
方法调用的时间,假如这个结果是6s,说明最后一次调用
channelRead
已经是6s之前的事情了,你设置的是5s,那么nextDelay则为-1,说明超时了,那么354行则会触发
userEventTriggered
方法,如果没有超时则不触发userEventTriggered方法。
五、总结
IdleStateHandler
这个类会根据你设置的超时参数的类型和值,循环去检测
channelRead
和
write
方法多久没有被调用了,如果这个时间超过了你设置的值,那么就会触发对应的事件,read触发read,write触发write,all触发all
-
如果超时了,则会调用
userEventTriggered
方法,且会告诉你超时的类型 -
如果没有超时,则会循环定时检测,除非你将
IdleStateHandler
移除
Pipeline
JVM内存泄漏和内存溢出的原因
JVM常用监控工具解释以及使用
Redis 常见面试题(一)
ClickHouse之MaterializeMySQL引擎(十)
三种实现分布式锁的实现与区别
线程池的理解以及使用
号外!号外!
最近面试BAT,整理一份面试资料,覆盖了Java核心技术、JVM、Java并发、SSM、微服务、数据库、数据结构等等。想获取吗?如果你想提升自己,并且想和优秀的人一起进步,感兴趣的朋友,可以在扫码关注下方公众号。资料在公众号里静静的躺着呢。。。
-
喜欢就收藏
-
认同就点赞
-
支持就关注
-
疑问就评论
一键四连,你的offer也四连
————————————————————————————————————————————————————————————————
本文作者:Java技术债务
原文链接:
https://www.cuizb.top/myblog/article/1645434283
版权声明: 本博客所有文章除特别声明外,均采用 CC BY 3.0 CN协议进行许可。转载请署名作者且注明文章出处。