2012-06-06 510 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你要。

    相關問題