netty(十八)Netty提升 - 连接假死如何处理? 一、连接假死出现原因 二、如何解决连接假死?

一、连接假死出现原因

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

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

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秒没有读取到数据
... ...

其实还有后续的操作,就是,当我们检测到在一定时间内没有发生数据写入或读取,应该怎么做?

可以制定一些策略,如连续多少次检测到空闲等。最终我们要做的其实就是释放资源:

ctx.channel().close();

上述的代码实际并不友好,其实比较好的处理方式,需要结合业务区考虑,没有消息读取并不代表连接存在问题,可以使用下面的方式,心跳检测

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

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


关于空闲检测的内容,就介绍这么多,有用的话点个赞再走吧!!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章