美文网首页JAVA进阶JavaJava 进阶
netty(十八)Netty提升 - 连接假死如何处理?

netty(十八)Netty提升 - 连接假死如何处理?

作者: 我犟不过你 | 来源:发表于2021-11-17 15:06 被阅读0次

    一、连接假死出现原因

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

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

    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。


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

    相关文章

      网友评论

        本文标题:netty(十八)Netty提升 - 连接假死如何处理?

        本文链接:https://www.haomeiwen.com/subject/ajmltrtx.html