Netty笔记(1)

优雅退出机制

Posted by Jqy on January 12, 2020

优雅退出机制

源码分析

总入口是EventLoopGroup的shutdownGracefully方法

1.NioEventLoopGroup

即NioEventLoop线程组,其退出流程是遍历EventLoop数组循环调用退出方法

for (EventExecutor l: children) {
            l.shutdownGracefully(quietPeriod, timeout, unit);
        }

2.NioEventLoop

安全退出时首先设置进程状态为关闭,这一步骤需要对并发调用做保护。Netty5采用的是加锁策略,Netty则是采用CAS无锁自旋.该方法在NioEventLoop父类中实现

for (;;) {
            if (isShuttingDown()) {
                return terminationFuture();
            }
    //....
    if (STATE_UPDATER.compareAndSet(this, oldState, newState)) {
                break;
            }

完成状态修改后,剩下操作与NioEventLoop中进行

 try {
                if (isShuttingDown()) {
                    closeAll();
                    if (confirmShutdown()) {
                        return;
                    }
                }

//把所有注册在selector上的channel关闭
private void closeAll() {
        selectAgain();
        Set<SelectionKey> keys = selector.keys();
        Collection<AbstractNioChannel> channels = new ArrayList<AbstractNioChannel>(keys.size());
        for (SelectionKey k: keys) {
            Object a = k.attachment();
            if (a instanceof AbstractNioChannel) {
                channels.add((AbstractNioChannel) a);
            } else {
                k.cancel();
                @SuppressWarnings("unchecked")
                NioTask<SelectableChannel> task = (NioTask<SelectableChannel>) a;
                invokeChannelUnregistered(task, k, null);
            }
        }

        for (AbstractNioChannel ch: channels) {
            ch.unsafe().close(ch.unsafe().voidPromise());
        }
    }

AbstractUnsafe的close方法主要完成以下功能:

  1. 判断当前链路是否有消息正在发送,有则将SelectionKey的去注册操作封装为Task放入eventLoop稍后执行。
    if (inFlush0) {
                     invokeLater(new Runnable() {
                         @Override
                         public void run() {
                             fireChannelInactiveAndDeregister(wasActive);
                         }
                     });
                 } else {
                     fireChannelInactiveAndDeregister(wasActive);
                 }
    
  2. 清空发送队列,不允许发送新的消息
    final boolean wasActive = isActive();
    final ChannelOutboundBuffer outboundBuffer = this.outboundBuffer;
    this.outboundBuffer = null; // Disallow adding any messages and flushes to outboundBuffer.
    
  3. 关闭链路
  4. 调用pipeline的fireChannelInactive,触发链路关闭通知事件。
  5. 调用AbstractNioChannel的doDeregister,从多路复用器上取消selectionKey.
  6. 调用ChannelOutboundBuffer的close,释放发送队列所有尚未完成发送的ByteBuf,等待GC

3.TaskQueue

NioEventLoop执行完closeAll后,需要通过confirmShutdown进一步判断是否可以真的退出