为什么netty客户端表现为僵尸?

时间:2016-01-22 08:52:44

标签: java network-programming netty

我使用netty作为套接字客户端:

public void run() {
    isRunning = true;
    EventLoopGroup group = new NioEventLoopGroup(EventLoopsPerGetter);
    Bootstrap b = new Bootstrap();
    b.group(group).channel(NioSocketChannel.class)
     .handler(new ChannelInitializer<SocketChannel>() {

        @Override
        protected void initChannel(SocketChannel ch) throws Exception {
            ChannelPipeline p = ch.pipeline();
            p.addLast(
                    new ProtobufVarint32FrameDecoder(),
                    ZlibCodecFactory.newZlibDecoder(ZlibWrapper.GZIP),
                    new ProtobufDecoder(Protocol.Packet.getDefaultInstance()),

                    new ProtobufVarint32LengthFieldPrepender(),
                    ZlibCodecFactory.newZlibEncoder(ZlibWrapper.GZIP),
                    new ProtobufEncoder(),

                    session
                    );
        }
    });
    try {
        while(isRunning) {
            try {
                 b.connect(host, port).sync().channel().closeFuture().sync();
            } catch(Exception e) {
                if (e instanceof InterruptedException) {
                    throw e;
                }
                retryLogger.warn("try to connect to " + host + " : " + port + " , but", e);
            }
            if(isRunning) {
                retryLogger.info("netty connection lost, retry!");
                Thread.sleep(RetryInterval);
            }
        }
    } catch (InterruptedException e) {
        Thread.currentThread().interrupt();
    } finally {
        group.shutdownGracefully();
    }
}

会话代码非常简单,将Get-packet发送到服务器,获取响应,写入文件,然后发送下一个Get-packet。

在这个程序中,我启动了两个netty客户端线程,但是在运行了几天之后,其中一个行为就像一个僵尸线程,也就是说,即使我杀死了netty服务器,僵尸客户端也没有打印任何日志而另一个客户端打印所需的日志。顺便说一下,jstack文件显示两个线程都是活动的,而不是死的。

我正在使用netty 5.

1 个答案:

答案 0 :(得分:1)

你没有读取超时的任何机制,会发生什么是10~(取决于路由器型号)分钟没有流量,路由器中的NAT表认为连接已完成,并关闭连接。

您有多种方法可以解决此问题:

使用ReadTimeoutHandler

如果检测到超时,

ReadTimeoutHandler将关闭频道并抛出ReadTimeoutException。如果需要,您可以通过exceptionCaught捕获此异常。根据您现有的逻辑,您不需要抓住这个。

此处理程序还可以与WriteTimeoutHandler结合使用来编写&#34; ping&#34;消息到远程。但是,以下解决方案更适合此目的。

使用IdleStateHandler

您也可以使用IdleStateHandler来实现此目的,此处理程序有3个参数,分别代表readerIdleTimewriteIdleTimeallIdleTime。这个类的优点是它不会抛出异常并使用Netty userEventTriggered来调度它的调用,这会使类更难用,你可以用它做更多的事情。

例如,如果协议支持ping消息,则可以使用此类发送以发送这些ping消息。它非常容易使用这个类,可以在处理程序中使用如下:

public class MyChannelInitializer extends ChannelInitializer<Channel> {
     @Override
     public void initChannel(Channel channel) {
         channel.pipeline().addLast("idleStateHandler", new IdleStateHandler(60, 30, 0));
         channel.pipeline().addLast("myHandler", new MyHandler());
     }
 }

 // Handler should handle the IdleStateEvent triggered by IdleStateHandler.
 public class MyHandler extends ChannelHandlerAdapter {
     @Override
     public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
         if (evt instanceof IdleStateEvent) {
             IdleStateEvent e = (IdleStateEvent) evt;
             if (e.state() == IdleState.READER_IDLE) {
                 ctx.close();
             } else if (e.state() == IdleState.WRITER_IDLE) {
                 ctx.writeAndFlush(new PingMessage());
             }
         }
     }
 }