Netty入门 - Netty心跳机制源码剖析

Netty入门 - Netty心跳机制源码剖析

Netty 心跳机制是保持长连接稳定的一种方法,主要用于检测连接是否仍然活跃,避免因为长时间无数据传输导致连接被关闭。Netty 提供了一个专门的处理器 IdleStateHandler 来实现心跳机制。本文将对 Netty 心跳机制的源码进行剖析。

1. IdleStateHandler

IdleStateHandler 是 Netty 提供的一个处理器,用于检测连接的空闲状态。当一个连接超过指定的时间没有读、写、或读写操作时,会触发相应的事件。

1.1 IdleStateHandler 类
public class IdleStateHandler extends ChannelDuplexHandler {

    private final long readerIdleTimeNanos;
    private final long writerIdleTimeNanos;
    private final long allIdleTimeNanos;

    // 省略了构造函数和其他代码

    @Override
    public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
        if (ctx.channel().isActive() && ctx.channel().isRegistered()) {
            initialize(ctx);
        }
    }

    @Override
    public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
        destroy();
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        initialize(ctx);
        super.channelActive(ctx);
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        destroy();
        super.channelInactive(ctx);
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        readTime = System.nanoTime();
        firstReaderIdleEvent = firstAllIdleEvent = true;
        ctx.fireChannelRead(msg);
    }

    @Override
    public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
        promise.addListener(writeListener);
        ctx.write(msg, promise);
    }

    @Override
    public void flush(ChannelHandlerContext ctx) throws Exception {
        writeTime = System.nanoTime();
        firstWriterIdleEvent = firstAllIdleEvent = true;
        ctx.flush();
    }

    private void initialize(ChannelHandlerContext ctx) {
        switch (state) {
            case 1:
            case 2:
                return;
        }

        state = 1;

        initOutputChanged(ctx);
        lastReadTime = lastWriteTime = System.nanoTime();
        schedule(ctx);
    }

    private void destroy() {
        state = 2;
        if (readerIdleTimeout != null) {
            readerIdleTimeout.cancel(false);
            readerIdleTimeout = null;
        }
        if (writerIdleTimeout != null) {
            writerIdleTimeout.cancel(false);
            writerIdleTimeout = null;
        }
        if (allIdleTimeout != null) {
            allIdleTimeout.cancel(false);
            allIdleTimeout = null;
        }
    }

    private void schedule(final ChannelHandlerContext ctx) {
        if (readerIdleTimeNanos > 0) {
            readerIdleTimeout = ctx.executor().schedule(new ReaderIdleTimeoutTask(ctx), readerIdleTimeNanos, TimeUnit.NANOSECONDS);
        }
        if (writerIdleTimeNanos > 0) {
            writerIdleTimeout = ctx.executor().schedule(new WriterIdleTimeoutTask(ctx), writerIdleTimeNanos, TimeUnit.NANOSECONDS);
        }
        if (allIdleTimeNanos > 0) {
            allIdleTimeout = ctx.executor().schedule(new AllIdleTimeoutTask(ctx), allIdleTimeNanos, TimeUnit.NANOSECONDS);
        }
    }

    private final class ReaderIdleTimeoutTask implements Runnable {
        private final ChannelHandlerContext ctx;

        ReaderIdleTimeoutTask(ChannelHandlerContext ctx) {
            this.ctx = ctx;
        }

        @Override
        public void run() {
            if (!ctx.channel().isOpen()) {
                return;
            }

            long nextDelay = readerIdleTimeNanos;
            nextDelay -= System.nanoTime() - lastReadTime;
            if (nextDelay <= 0) {
                readerIdleTimeout = ctx.executor().schedule(this, readerIdleTimeNanos, TimeUnit.NANOSECONDS);
                boolean first = firstReaderIdleEvent;
                firstReaderIdleEvent = false;

                try {
                    IdleStateEvent event = newIdleStateEvent(IdleState.READER_IDLE, first);
                    channelIdle(ctx, event);
                } catch (Throwable t) {
                    ctx.fireExceptionCaught(t);
                }
            } else {
                readerIdleTimeout = ctx.executor().schedule(this, nextDelay, TimeUnit.NANOSECONDS);
            }
        }
    }

    // WriterIdleTimeoutTask and AllIdleTimeoutTask are similar
}
1.2 IdleStateEvent 类

IdleStateEvent 是 Netty 提供的一个事件类,用于表示连接的空闲状态。

public class IdleStateEvent {
    private final IdleState state;
    private final boolean first;

    public IdleStateEvent(IdleState state, boolean first) {
        this.state = state;
        this.first = first;
    }

    public IdleState state() {
        return state;
    }

    public boolean isFirst() {
        return first;
    }
}
1.3 IdleState 枚举

IdleState 枚举类表示连接的三种空闲状态。

public enum IdleState {
    READER_IDLE,
    WRITER_IDLE,
    ALL_IDLE
}
2. 实现心跳机制

要实现心跳机制,我们可以自定义一个处理器,继承 ChannelInboundHandlerAdapter,在连接空闲时发送心跳包。

2.1 HeartbeatHandler 类
public class HeartbeatHandler extends ChannelInboundHandlerAdapter {
    private static final Logger logger = LoggerFactory.getLogger(HeartbeatHandler.class);

    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
        if (evt instanceof IdleStateEvent) {
            IdleStateEvent event = (IdleStateEvent) evt;
            if (event.state() == IdleState.READER_IDLE) {
                logger.info("READER_IDLE 读超时");
            } else if (event.state() == IdleState.WRITER_IDLE) {
                logger.info("WRITER_IDLE 写超时");
            } else if (event.state() == IdleState.ALL_IDLE) {
                logger.info("ALL_IDLE 读写超时");
                ctx.writeAndFlush(new PingMessage());
            }
        } else {
            super.userEventTriggered(ctx, evt);
        }
    }
}
2.2 配置心跳检测

ChannelPipeline 中添加 IdleStateHandlerHeartbeatHandler

public class MyChannelInitializer extends ChannelInitializer<SocketChannel> {
    @Override
    protected void initChannel(SocketChannel ch) throws Exception {
        ChannelPipeline pipeline = ch.pipeline();
        pipeline.addLast(new IdleStateHandler(60, 30, 60, TimeUnit.SECONDS));
        pipeline.addLast(new HeartbeatHandler());
    }
}

总结

Netty 的心跳机制通过 IdleStateHandler 来实现,IdleStateHandler 负责检测连接的空闲状态,并触发相应的事件。我们可以自定义处理器 HeartbeatHandler,在连接空闲时发送心跳包,确保连接的活跃性。通过源码剖析,我们可以深入理解 Netty 的心跳机制,实现高性能的长连接管理。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值