一、连接假死出现原因

在网络编程的领域当中,很多问题都会莫名其妙的出现,让人措手不及。

其中一种,就是连接假死,那么连接假死是如何出现的呢?可能存在以下几种情况:

1)网络设备出现故障。例如网卡,机房等,底层的 TCP 连接已经断开了,但应用程序没有感知到,仍然占用着资源。

2)公网网络不稳定,出现丢包。如果连续出现丢包,这时现象就是客户端数据发不出去,服务端也一直收不到数据,进程一直占据资源,耗在这儿。

3)应用程序线程阻塞,无法进行数据读写。

连接假死会产生以下问题:

1)假死的连接占用的资源不能自动释放。

2)向假死的连接发送数据,得到的反馈是发送超时。

二、如何解决连接假死?

Netty提供了一个叫做IdleStateHandler的处理器,用来进行空闲检测。

其实现方式是:

当Channel有一段时间没有执行读、写或两者操作时触发IdleStateEvent。

有三个主要参数:

  • readerIdleTime:一个IdleStateEvent,其状态是IdleState.READER_IDLE时的指定时间段内,没有执行读操作将被触发。 指定0以禁用。

  • writerIdleTime:一个IdleStateEvent,其状态是IdleState.WRITER_IDLE时的指定时间段内,没有执行写操作将被触发。 指定0以禁用。

  • allIdleTime:一个IdleStateEvent,其状态是IdleState.ALL_IDLE时的指定时间段内,没有进行读取和写入都将被触发。 指定0以禁用

我们正常使用时,也使用这个三个参数的构造即可:

public IdleStateHandler(
        int readerIdleTimeSeconds,
        int writerIdleTimeSeconds,
        int allIdleTimeSeconds) {

    this(readerIdleTimeSeconds, writerIdleTimeSeconds, allIdleTimeSeconds,
         TimeUnit.SECONDS);
}

如上所示,其默认单位是秒,当然也有可以指定单位的构造,还能附带考虑buf处理数据时间的构造,这里就不多介绍了。

接下来我们看看如何使用,我们需要将这个处理器添加到pipeline当中,需要注意的是,我们需要自己去处理事件,IdleStateEvent。

关于这个事件的处理,我们不能使用常规的入站或者出站处理器了,此处需要学习一个新的处理器,专门用来处理特殊事件的,我通过下面的代码进行演示:

服务端接收数据,添加如下代码:

//空闲检测处理器,检测5秒内未读取
ch.pipeline().addLast(new IdleStateHandler(5, 0, 0));
//可以同时作为入站和出站处理器,此处用来处理特殊事件
ch.pipeline().addLast(new ChannelDuplexHandler() {
    //用户事件触发
    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
        IdleStateEvent stateEvent = (IdleStateEvent) evt;
        //我们的服务器端只进行数据接收
        if (stateEvent.state() ==  IdleState.READER_IDLE) {
            System.out.println("已经5秒没有接收到数据");
        }
    }
});

客户端发送数据,添加如下代码:

//空闲检测处理器,检测5秒未写入
ch.pipeline().addLast(new IdleStateHandler(0, 5, 0));
//可以同时作为入站和出站处理器,此处用来处理特殊事件
ch.pipeline().addLast(new ChannelDuplexHandler() {
    //用户事件触发
    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
        IdleStateEvent stateEvent = (IdleStateEvent) evt;
        //我们的服务器端只进行数据接收
        if (stateEvent.state() ==  IdleState.WRITER_IDLE) {
            System.out.println("已经5秒没有写入数据");
        }
    }
});

当客户端和服务端都启动并建立连接后,如果没有消息就会打印如下内容:

已经5秒没有写入数据
已经5秒没有写入数据
已经5秒没有写入数据
已经5秒没有写入数据
......
已经5秒没有读取到数据
已经5秒没有读取到数据
已经5秒没有读取到数据
已经5秒没有读取到数据
......

心跳检测

客户端:当检测到没有数据写入时,可以定时向服务端发送心跳,比如每5秒就向服务端写入一次数据。这个时间要小于服务器设置的检测时间。

服务端:设置一个定时读取的检测时间,比如8秒,这样在8秒内,一定会受到客户端的心跳,如果超过8秒没有心跳,则可以认为连接出现问题,可以关闭channel。

文章作者: 刘同学
本文链接:
版权声明: 本站所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 刘同学的小站
后端 Java
喜欢就支持一下吧