我正在尝试关闭与其打开连接的Netty服务器,它只是挂起。这就是我的工作。
我在关闭ChannelGroup的服务器上注册了一个关闭钩子,并在ServerBootstrap上调用了releaseExternalResources(实际上我正在使用protobuf-pro-duplex库的DuplexTcpServerBootstrap来实现这一点)。无论如何,在关闭时正确调用shutdown钩子,但它永远不会返回。当我对正在发生的事情进行线程转储时,我可以看到两个有趣的堆栈:
java.lang.Thread.State: TIMED_WAITING (parking)
at sun.misc.Unsafe.park(Native Method)
- parking to wait for <0x00000006b0890950> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
at java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:226)
at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.awaitNanos(AbstractQueuedSynchronizer.java:2082)
at java.util.concurrent.ThreadPoolExecutor.awaitTermination(ThreadPoolExecutor.java:1433)
at org.jboss.netty.util.internal.ExecutorUtil.terminate(ExecutorUtil.java:103)
at org.jboss.netty.channel.socket.nio.AbstractNioWorkerPool.releaseExternalResources(AbstractNioWorkerPool.java:80)
at org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory.releaseExternalResources(NioServerSocketChannelFactory.java:162)
at org.jboss.netty.bootstrap.Bootstrap.releaseExternalResources(Bootstrap.java:319)
at com.googlecode.protobuf.pro.duplex.server.DuplexTcpServerBootstrap.releaseExternalResources(DuplexTcpServerBootstrap.java:132)
at com.xxx.yyy.node.NodeServer$2.run(NodeServer.java:104)
at java.lang.Thread.run(Thread.java:722)
所以这是永远不会返回的关闭钩子线程。下面是另一个似乎在频道上等待的线程:
java.lang.Thread.State: RUNNABLE
at sun.nio.ch.EPollArrayWrapper.interrupt(Native Method)
at sun.nio.ch.EPollArrayWrapper.interrupt(EPollArrayWrapper.java:274)
at sun.nio.ch.EPollSelectorImpl.wakeup(EPollSelectorImpl.java:193)
- locked <0x00000006b0896660> (a java.lang.Object)
at java.nio.channels.spi.AbstractSelector$1.interrupt(AbstractSelector.java:210)
at java.nio.channels.spi.AbstractSelector.begin(AbstractSelector.java:216)
at sun.nio.ch.EPollSelectorImpl.doSelect(EPollSelectorImpl.java:80)
at sun.nio.ch.SelectorImpl.lockAndDoSelect(SelectorImpl.java:87)
- locked <0x00000006b08964a8> (a sun.nio.ch.Util$2)
- locked <0x00000006b0896498> (a java.util.Collections$UnmodifiableSet)
- locked <0x00000006b0890d20> (a sun.nio.ch.EPollSelectorImpl)
at sun.nio.ch.SelectorImpl.select(SelectorImpl.java:98)
at org.jboss.netty.channel.socket.nio.SelectorUtil.select(SelectorUtil.java:52)
at org.jboss.netty.channel.socket.nio.AbstractNioWorker.run(AbstractNioWorker.java:208)
at org.jboss.netty.channel.socket.nio.NioWorker.run(NioWorker.java:38)
at org.jboss.netty.util.ThreadRenamingRunnable.run(ThreadRenamingRunnable.java:102)
at org.jboss.netty.util.internal.DeadLockProofWorker$1.run(DeadLockProofWorker.java:42)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1110)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:603)
at java.lang.Thread.run(Thread.java:722)
我在Linux上使用Netty 3.4.6.Final和Java 7.04。谢谢!
BR, 坦率。
答案 0 :(得分:4)
与裸Netty客户端/服务器也存在相同的“问题”。
事情是,关闭服务器通道不要关闭为接受的客户端连接创建的开放通道。必须明确地跟踪服务器中的客户端通道。这可以通过通道组和将该客户端通道添加到该组的处理程序来完成。 关闭服务器时,应该以类似批处理的方式关闭组中的所有通道,而不是仅关闭一个服务器通道(也可以放入通道组)。
用户指南中有很好的文档(9.关闭应用程序):http://static.netty.io/3.5/guide/和ChannelGroup API文档(使用ChannelGroup简化关闭过程):http://static.netty.io/3.5/api/org/jboss/netty/channel/group/ChannelGroup.html
答案 1 :(得分:2)
我有同样的问题并解决了它。
您必须同步关闭所有EventLoopGroup
,然后关闭端口。
完全关闭可能需要4-5秒。
这是一个示例代码(我认为你应该使用 start 和 stop 按钮进行简单的GUI测试):
public class ReusePortServer {
private final int port;
ChannelFuture f;
EventLoopGroup group;
EpollEventLoopGroup bossGroup;
EpollEventLoopGroup workerGroup;
public ReusePortServer(int port) {
this.port = port;
}
public void start() throws Exception {
group = new EpollEventLoopGroup();
bossGroup = new EpollEventLoopGroup();
workerGroup = new EpollEventLoopGroup();
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
.channel(EpollServerSocketChannel.class)
.option(EpollChannelOption.SO_REUSEPORT, true)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new ReusePortHandler());
}
});
f = b.bind(port).sync();
log(String.format("%s started and listen on %s", ReusePortServer.class.getName(), f.channel().localAddress()));
}
private final static SimpleDateFormat datefmt = new SimpleDateFormat("HH:mm:ss ");
public static void log(final String msg) {
System.out.print(datefmt.format(new Date()));
System.out.println(msg);
System.out.flush();
}
public void stop() {
System.out.println("ReusePortServer.stop");
try {
// shutdown EventLoopGroup
bossGroup.shutdownGracefully().sync();
workerGroup.shutdownGracefully().sync();
f.channel().closeFuture().sync(); // close port
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(final String[] args) throws Exception {
int port = 12345;
new ReusePortServer(port).start();
}
}
注意:您可以将EpollEventLoopGroup
更改为您想要的任何EventLoopGroup
。
答案 2 :(得分:0)
Netty服务器关机
示例代码
ChannelFuture cf = serverChannel.close();
cf.awaitUninterruptibly();
bossExecutor.shutdown();
workerExecutor.shutdown();
thriftServer.releaseExternalResources();