我使用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.
答案 0 :(得分:1)
你没有读取超时的任何机制,会发生什么是10~(取决于路由器型号)分钟没有流量,路由器中的NAT表认为连接已完成,并关闭连接。
您有多种方法可以解决此问题:
ReadTimeoutHandler
ReadTimeoutHandler
将关闭频道并抛出ReadTimeoutException
。如果需要,您可以通过exceptionCaught
捕获此异常。根据您现有的逻辑,您不需要抓住这个。
此处理程序还可以与WriteTimeoutHandler
结合使用来编写&#34; ping&#34;消息到远程。但是,以下解决方案更适合此目的。
IdleStateHandler
您也可以使用IdleStateHandler
来实现此目的,此处理程序有3个参数,分别代表readerIdleTime
,writeIdleTime
和allIdleTime
。这个类的优点是它不会抛出异常并使用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());
}
}
}
}