2012-06-06 458 views
4

我试图关闭一个打开了连接的Netty服务器,它只是挂起。这就是我所做的。客户端连接打开时关闭Netty服务器

  • 在一台机器上启动服务器,在另一台机器上启动客户机。
  • 发送消息从客户端到服务器,我得到了答复。使用Ctrl-C

我已经注册了关闭此通道,并在ServerBootstrap调用releaseExternalResources(或实际上我使用的服务器上关闭挂钩

  • 关闭服务器的protobuf-亲的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: 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) 
    

    我使用了Netty 3.4.6.Final在Linux上的Java 7.04。谢谢!

    Br, Frank。

  • +0

    你能发布你的关机钩子代码吗? – Nicholas

    回答

    0

    的Netty服务器关机

    1. 关闭服务器通道
    2. 关机老板和工人执行
    3. 推出服务器引导资源

    实施例代码

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

    有同样的“ proble m'与裸Netty客户端/服务器以及。

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

    有用户指南中出色的文档(9关闭您的应用程序):http://static.netty.io/3.5/guide/与此通道API文档(简化与通道组关闭进程):http://static.netty.io/3.5/api/org/jboss/netty/channel/group/ChannelGroup.html

    +1

    断开的链接... –

    1

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

    完全关机可能需要4-5秒。

    下面是一个示例代码(我想你应该做一个简单的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你要。

    相关问题