netty源碼分析(7)-NioEventLoop啓動過程

上一節,我們研究了NioEventLoop的創建過程,其實也就是做了一些初始化,把該準備的準備好了。重點有兩個

  1. 準備了一個ThreadPerTaskExecutor,以後添加的task,每次執行的時候其實是實例化了了一個netty自定義的Thread :(FastThreadLocalThread)然後再調用start()執行任務。
  2. 準備了一個NioEventLoop數組(EventExecutor),配套了一個循環使用該數組的選擇器chooser,該選擇器根據"線程數是否是2的冪次方"提供了不同選擇策略。
  3. 準備了taskQueuetailQueue用於存放eventLoop任務,和外部線程任務

這一節,具體研究NioEventLoop的的啓動過程。

入口: 端口綁定的時候AbstractBootstrap#doBind0()

//入口
ChannelFuture future = serverBootstrap.bind(PORT).sync();
    doBind(localAddress)
      doBind0(regFuture, channel, localAddress, promise);
    private static void doBind0(
            final ChannelFuture regFuture, final Channel channel,
            final SocketAddress localAddress, final ChannelPromise promise) {

        // This method is invoked before channelRegistered() is triggered.  Give user handlers a chance to set up
        // the pipeline in its channelRegistered() implementation.
        channel.eventLoop().execute(new Runnable() {
            @Override
            public void run() {
                if (regFuture.isSuccess()) {
                    channel.bind(localAddress, promise).addListener(ChannelFutureListener.CLOSE_ON_FAILURE);
                } else {
                    promise.setFailure(regFuture.cause());
                }
            }
        });
    }

首先是channel.eventLoop()獲取的肯定是NioEventLoop,但是具體是怎麼獲取的呢?往下研究

    @Override
    public NioEventLoop eventLoop() {
        return (NioEventLoop) super.eventLoop();
    }

    @Override
    public EventLoop eventLoop() {
        EventLoop eventLoop = this.eventLoop;
        if (eventLoop == null) {
            throw new IllegalStateException("channel not registered to an event loop");
        }
        return eventLoop;
    }

問題來了。該方法確實是獲取的NioEventLoop,但是是什麼時候初始化AbstractChannel#eventLoop這個成員變量的呢?翻看之前的《註冊Selector》 過程的分析,發現確實是註冊的時候做的。也就是AbstractChannel.AbstractUnsafe#register()方法中,通過方法參數講eventLoop傳入,以下這段代碼

        @Override
        public final void register(EventLoop eventLoop, final ChannelPromise promise) {
                //省略代碼
                AbstractChannel.this.eventLoop = eventLoop;
                //省略代碼
        }

該方法被SingleThreadEventLoop#register()調用並將自己傳入的,其本身就是NioEventLoop的父類,在創建的時候就被存儲子啊EventExecutor[]數組中了。那麼是怎麼從數組中取出來的呢?

    @Override
    public ChannelFuture register(final ChannelPromise promise) {
        ObjectUtil.checkNotNull(promise, "promise");
        promise.channel().unsafe().register(this, promise);
        return promise;
    }

前面也提到過,註冊的過程我們發現從EventExecutor[]中選擇EventLoop的代碼是在MultithreadEventLoopGroup#register()

    @Override
    public ChannelFuture register(Channel channel) {
        return next().register(channel);
    }
    @Override
    public EventLoop next() {
        return (EventLoop) super.next();
    }
    //io.netty.util.concurrent.MultithreadEventExecutorGroup#next()
    @Override
    public EventExecutor next() {
        return chooser.next();
    }
    
    @UnstableApi
    interface EventExecutorChooser {
        /**
         * Returns the new {@link EventExecutor} to use.
         */
        EventExecutor next();
    }    

終於,在這裏獲取了之前創建NioEventLoop準備好的選擇器。那麼next()方法是在兩個選擇器策略裏面定義的,用於繼續按下標後,循環獲取EventExecutor[]數組中的EventLoop(這裏是NioEventLoop)

    private static final class PowerOfTwoEventExecutorChooser implements EventExecutorChooser {
        private final AtomicInteger idx = new AtomicInteger();
        private final EventExecutor[] executors;

        PowerOfTwoEventExecutorChooser(EventExecutor[] executors) {
            this.executors = executors;
        }

        @Override
        public EventExecutor next() {
            return executors[idx.getAndIncrement() & executors.length - 1];
        }
    }

    private static final class GenericEventExecutorChooser implements EventExecutorChooser {
        private final AtomicInteger idx = new AtomicInteger();
        private final EventExecutor[] executors;

        GenericEventExecutorChooser(EventExecutor[] executors) {
            this.executors = executors;
        }

        @Override
        public EventExecutor next() {
            return executors[Math.abs(idx.getAndIncrement() % executors.length)];
        }
    }

回過到啓動過程,NioEventLoop已經獲取,調用execute()方法,並且實例化了一個Runnable,該Runnable就是一個新的task

       channel.eventLoop().execute(new Runnable() {
            @Override
            public void run() {
                if (regFuture.isSuccess()) {
                    channel.bind(localAddress, promise).addListener(ChannelFutureListener.CLOSE_ON_FAILURE);
                } else {
                    promise.setFailure(regFuture.cause());
                }
            }
        });

execute()方法其實是在NioEventLoop的父類,SingleThreadEventExecutor#execute()。這裏有三個動作。

  1. inEventLoop() :判斷當前線程是否是EventLoop持有的線程
  2. startThread() :開啓自定義線程
  3. addTask(task) :添加到任務隊列
    @Override
    public void execute(Runnable task) {
        if (task == null) {
            throw new NullPointerException("task");
        }
        //判斷當前線程是否是EventLoop持有的線程,這個時間點返回的是false
        boolean inEventLoop = inEventLoop();
        if (inEventLoop) {
            addTask(task);
        } else {
            //不在同一線程,開啓自定義線程
            startThread();
            //添加任務隊列
            addTask(task);
            if (isShutdown() && removeTask(task)) {
                reject();
            }
        }

        if (!addTaskWakesUp && wakesUpForTask(task)) {
            wakeup(inEventLoop);
        }
    }
  • inEventLoop()很多地方都用到,值得深究,追綜到SingleThreadEventExecutor#inEventLoop(),確實是跟持有的成員變量private volatile Thread thread;進行對比。注意,該成員變量this.thread此時並沒有被初始化,因此爲null,返回的是false。可以Debug進行跟蹤。
    @Override
    public boolean inEventLoop() { 
        //傳入當前線程,當前線程爲:main 線程   
        return inEventLoop(Thread.currentThread());
    }

    @Override
    public boolean inEventLoop(Thread thread) {
        return thread == this.thread;
    }
  • startThread() 開啓一個線程用於執行NioEventLooprun方法,並且讓NioEventLoop持有這個線程。這一刻,NioEventLoop跑起來了。
    private void startThread() {
        //判斷當前線程是否是未啓動的
        if (STATE_UPDATER.get(this) == ST_NOT_STARTED) {
            if (STATE_UPDATER.compareAndSet(this, ST_NOT_STARTED, ST_STARTED)) {
                //啓動
                doStartThread();
            }
        }
    }

    private void doStartThread() {
        assert thread == null;
        //ThreadPerTaskExecutor#execute() 開啓netty自定義的線程FastThreadLocalThread,並執行
        executor.execute(new Runnable() {
            @Override
            public void run() {
                //運行時,開始持有運行時線程
                thread = Thread.currentThread();
               
                //省略代碼
                //執行NioEventLoop.run()
                SingleThreadEventExecutor.this.run();
                
                 //省略代碼
        });
    }

  • addTask(task)是在NioEventLoop的線程跑起來了之後,將最初綁定端口的任務offer()到了taskQueue,存儲起來,異步執行。
    protected void addTask(Runnable task) {
        if (task == null) {
            throw new NullPointerException("task");
        }
        if (!offerTask(task)) {
            reject(task);
        }
    }

    final boolean offerTask(Runnable task) {
        if (isShutdown()) {
            reject();
        }
        return taskQueue.offer(task);
    }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章