当客户端连接打开时关闭Netty服务器

当客户端连接打开时关闭Netty服务器,netty,Netty,我正试图关闭一个与它有开放连接的Netty服务器,但它只是挂起。我就是这么做的 在一台机器上启动服务器,在另一台机器上启动客户端 将消息从客户端发送到服务器,我将得到响应 使用Ctrl-C关闭服务器 我已经在服务器上注册了一个关闭钩子,它关闭ChannelGroup并调用ServerBootstrap上的releaseExternalResources(或者实际上我正在使用protobuf pro duplex库的DuplexTcpServerBootstrap来实现这一点)。无论如何,关闭

我正试图关闭一个与它有开放连接的Netty服务器,但它只是挂起。我就是这么做的

  • 在一台机器上启动服务器,在另一台机器上启动客户端
  • 将消息从客户端发送到服务器,我将得到响应
  • 使用Ctrl-C关闭服务器
我已经在服务器上注册了一个关闭钩子,它关闭ChannelGroup并调用ServerBootstrap上的releaseExternalResources(或者实际上我正在使用protobuf pro duplex库的DuplexTcpServerBootstrap来实现这一点)。无论如何,关闭钩子在关闭时被正确调用,但它永远不会返回。当我对正在发生的事情进行线程转储时,我可以看到两个有趣的堆栈:

   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:定时等待(停车)
在sun.misc.Unsafe.park(本机方法)
-停车等待(java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
位于java.util.concurrent.locks.LockSupport.parknos(LockSupport.java:226)
位于java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.awaitNanos(AbstractQueuedSynchronizer.java:2082)
位于java.util.concurrent.ThreadPoolExecutor.waiting(ThreadPoolExecutor.java:1433)
位于org.jboss.netty.util.internal.ExecutorUtil.terminate(ExecutorUtil.java:103)
位于org.jboss.netty.channel.socket.nio.AbstractNiowerPool.releaseExternalResources(AbstractNiowerPool.java:80)
位于org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory.releaseExternalResources(NioServerSocketChannelFactory.java:162)
位于org.jboss.netty.bootstrap.bootstrap.releaseExternalResources(bootstrap.java:319)
位于com.googlecode.protobuf.pro.duplex.server.DuplexTcpServerBootstrap.releaseExternalResources(DuplexTcpServerBootstrap.java:132)
位于com.xxx.yyy.node.NodeServer$2.run(NodeServer.java:104)
运行(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)
java.lang.Thread.State:可运行
at sun.nio.ch.EPollArrayWrapper.interrupt(本机方法)
在sun.nio.ch.EPollArrayWrapper.interrupt处(EPollArrayWrapper.java:274)
在sun.nio.ch.EPollSelectorImpl.wakeup(EPollSelectorImpl.java:193)
-锁定(一个java.lang.Object)
位于java.nio.channels.spi.AbstractSelector$1.interrupt(AbstractSelector.java:210)
位于java.nio.channels.spi.AbstractSelector.begin(AbstractSelector.java:216)
位于sun.nio.ch.EPollSelectorImpl.doSelect(EPollSelectorImpl.java:80)
在sun.nio.ch.SelectorImpl.lock和doselect上(SelectorImpl.java:87)
-锁定(sun.nio.ch.Util$2)
-锁定(java.util.Collections$UnmodifiableSet)
-锁定(sun.nio.ch.EPollSelectorImpl)
在sun.nio.ch.SelectorImpl.select上(SelectorImpl.java:98)
位于org.jboss.netty.channel.socket.nio.SelectorUtil.select(SelectorUtil.java:52)
位于org.jboss.netty.channel.socket.nio.AbstractNiower.run(AbstractNiower.java:208)
位于org.jboss.netty.channel.socket.nio.niower.run(niower.java:38)
位于org.jboss.netty.util.ThreadRenamingRunnable.run(ThreadRenamingRunnable.java:102)
位于org.jboss.netty.util.internal.DeadLockProofWorker$1.run(DeadLockProofWorker.java:42)
位于java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1110)
位于java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:603)
运行(Thread.java:722)
我在Linux上使用Netty 3.4.6.Final和Java 7.04。谢谢

比尔, 弗兰克。

网络服务器关闭

  • 关闭服务器通道
  • 停工负责人和工人执行人
  • 释放服务器引导资源
  • 示例代码

    ChannelFuture cf = serverChannel.close();
    cf.awaitUninterruptibly();
    bossExecutor.shutdown();
    workerExecutor.shutdown();
    thriftServer.releaseExternalResources();
    

    裸Netty客户端/服务器也有同样的“问题”

    问题是,关闭服务器通道并不会关闭为已接受的客户端连接创建的开放通道。必须明确跟踪服务器中的客户端通道。这可以通过通道组和将客户端通道添加到此组的处理程序来完成。 在关闭服务器时,组中的所有通道都应以批处理方式关闭,而不仅仅是一个服务器通道(也可以放入通道组)


    《用户指南》(9.关闭应用程序)和ChannelGroup API文档(使用ChannelGroup简化关闭过程)中有很好的文档说明:

    我遇到了同样的问题并解决了它。 您必须同步关闭所有
    EventLoopGroup
    ,然后关闭端口

    完全关闭可能需要4-5秒

    下面是一个示例代码(我认为您应该制作一个带有启动和停止按钮的简单GUI来测试它):

    公共类ReusePortServer{
    私人最终国际港口;
    渠道f;
    事件组;
    EpollEventLoopGroup-bossGroup;
    EpollEventLoopGroup工作组;
    公共ReusePortServer(int端口){
    this.port=端口;
    }
    public void start()引发异常{
    group=新的EpollEventLoopGroup();
    bossGroup=new-EpollEventLoopGroup();
    workerGroup=new-EpollEventLoopGroup();
    ServerBootstrap b=新的ServerBootstrap();
    b、 组(bossGroup、workerGroup)
    .channel(epollserverssocketchannel.class)
    .option(EpollChannelOption.SO_REUSEPORT,true)
    .childHandler(新的通道初始值设定项(){
    @凌驾
    public void initChannel(SocketChannel ch)引发异常{
    ch.pipeline().addLast(新的重用处理器());
    }
    });
    f=b.bind(port.sync();
    日志(String.format(“%s已启动并在%s上侦听”,ReusePortServer.class.getName(),f.channel().localAddress());
    }
    私有最终静态SimpleDataFormat datefmt=新SimpleDataFormat(“HH:mm:ss”);
    公共静态无效日志(最终字符串msg)
    
    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();
        }
    }