Netty的心跳机制

    一、引入

    在 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_IDLEIdleStateEvent 事件.
    • writerIdleTimeSeconds: 写超时. 即当在指定的时间间隔内没有数据写入到 Channel 时, 会触发一个 WRITER_IDLEIdleStateEvent 事件.
    • allIdleTimeSeconds: 读/写超时. 即当在指定的时间间隔内没有读或写操作时, 会触发一个 ALL_IDLEIdleStateEvent 事件.

    **注意:**这三个参数默认的时间单位是秒。

    心跳处理类: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这个类会根据你设置的超时参数的类型和值,循环去检测channelReadwrite方法多久没有被调用了,如果这个时间超过了你设置的值,那么就会触发对应的事件,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协议进行许可。转载请署名作者且注明文章出处。

    posted @   Java技术债务  阅读(321)  评论(0编辑  收藏  举报
    💬
    评论
    📌
    收藏
    💗
    关注
    👍
    推荐
    🚀
    回顶
    收起
    点击右上角即可分享
    微信分享提示