Netty心跳检测机制

### Netty心跳检测机制
#### 概念
  所谓心跳, 即在 TCP 长连接中, 客户端和服务器之间定期发送的一种特殊的数据包, 通知对方自己还在线, 以确保 TCP 连接的有效性. 

在 Netty 中, 实现心跳机制的关键是 IdleStateHandler, 看下它的构造器:

这里解释下三个参数的含义:
    - readerIdleTimeSeconds: 读超时. 即当在指定的时间间隔内没有从 Channel 读取到数据时, 会触发一个 READER_IDLE 的 IdleStateEvent 事件.
    - writerIdleTimeSeconds: 写超时. 即当在指定的时间间隔内没有数据写入到 Channel 时, 会触发一个 WRITER_IDLE 的 IdleStateEvent 事件.
    - allIdleTimeSeconds: 读/写超时. 即当在指定的时间间隔内没有读或写操作时, 会触发一个 ALL_IDLE 的 IdleStateEvent 事件.


#### 源码分析

 注:这三个参数默认的时间单位是秒。若需要指定其他时间单位,可以使用另一个构造方法:

```
IdleStateHandler(booleanobserveOutput,longreaderIdleTime,longwriterIdleTime,longallIdleTime,TimeUnitunit) 
```
要实现Netty服务端心跳检测机制需要在服务器端的ChannelInitializer中加入如下的代码:
```
pipeline.addLast(new IdleStateHandler(3, 0, 0, TimeUnit.SECONDS));
```

1、先看下IdleStateHandler中的channelRead方法: 

```
//通道读取接收数据
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
    if (readerIdleTimeNanos > 0 || allIdleTimeNanos > 0) {
        reading = true;
        firstReaderIdleEvent = firstAllIdleEvent = true;
    }
    ctx.fireChannelRead(msg); //只是进行透传,不做任何业务逻辑处理,让chanelpiple中的下一个handler处理channelRead方法
}
```

2、在看下IdleStateHandler中的channelActive方法:

//通道激活通道
```
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
    // This method will be invoked only if this handler was added
    // before channelActive() event is fired.  If a user adds this handler
    // after the channelActive() event, initialize() will be called by beforeAdd().
    initialize(ctx);  //initialize的方法,这是IdleStateHandler的精髓
    super.channelActive(ctx);
}
```
3、初始化通道连接 initialize 方法:

```
private void initialize(ChannelHandlerContext ctx) {
    // Avoid the case where destroy() is called before scheduling timeouts.
    // See: https://github.com/netty/netty/issues/143
    switch (state) {
    case 1:
    case 2:
        return;
    }

    state = 1;
    initOutputChanged(ctx);

    lastReadTime = lastWriteTime = ticksInNanos();
    if (readerIdleTimeNanos > 0) { //读超时大于0
        //这边会触发一个Task,ReaderIdleTimeoutTask
        readerIdleTimeout = schedule(ctx, new ReaderIdleTimeoutTask(ctx),
                readerIdleTimeNanos, TimeUnit.NANOSECONDS);
    }
    if (writerIdleTimeNanos > 0) {
        writerIdleTimeout = schedule(ctx, new WriterIdleTimeoutTask(ctx),
                writerIdleTimeNanos, TimeUnit.NANOSECONDS);
    }
    if (allIdleTimeNanos > 0) {
        allIdleTimeout = schedule(ctx, new AllIdleTimeoutTask(ctx),
                allIdleTimeNanos, TimeUnit.NANOSECONDS);
    }
}
```

4、触发一个task,ReaderIdleTimeoutTask -> run方法如下:

```
@Override
protected void run(ChannelHandlerContext ctx) {
    long nextDelay = readerIdleTimeNanos; // IdleStateHandler 中设置的读超时时间
    if (!reading) {
        nextDelay -= ticksInNanos() - lastReadTime; //表示当前时间减去最后一次调用channelRead方法时间,读超时时间再减去这个时间
    }

    if (nextDelay <= 0) {  //nextDelay<=0,说明上一次条用channelRead方法时间超过了readerIdleTimeNanos(读超时时间),称为读空闲
        // Reader is idle - set a new timeout and notify the callback.
        readerIdleTimeout = schedule(ctx, this, readerIdleTimeNanos, TimeUnit.NANOSECONDS);

        boolean first = firstReaderIdleEvent;
        firstReaderIdleEvent = false;

        try {
            IdleStateEvent event = newIdleStateEvent(IdleState.READER_IDLE, first);
            channelIdle(ctx, event);  //此方法会触发下一个handler的 userEventTriggered方法:
        } catch (Throwable t) {
            ctx.fireExceptionCaught(t);
        }
    } else {
        // Read occurred before the timeout - set a new timeout with shorter delay.
        readerIdleTimeout = schedule(ctx, this, nextDelay, TimeUnit.NANOSECONDS);
    }
}
```

5、下一个handler的 userEventTriggered方法: 用此方法来处理超时后发生的动作,比如断开连接

```
/**
 * Is called when an {@link IdleStateEvent} should be fired. This implementation calls
 * {@link ChannelHandlerContext#fireUserEventTriggered(Object)}.
 */
protected void channelIdle(ChannelHandlerContext ctx, IdleStateEvent evt) throws Exception {
    ctx.fireUserEventTriggered(evt);
}
```

你可能感兴趣的:(java)