1. 程式人生 > >Java NIO框架Netty教程(十六)-ServerBootStrap啟動流程源碼分析

Java NIO框架Netty教程(十六)-ServerBootStrap啟動流程源碼分析

ucc ask pip 以及 梳理 學習曲線 owa pan server

有一段事件沒有更新文章了,各種原因都有吧。搬家的瑣事,搬家後的安逸呵呵。不過,OneCoder明白,絕不能放松。對於Netty的學習,也該稍微深入一點了。

所以,這次OneCoder花了幾天時間,仔細梳理了一下Netty的源碼,總結了一下ServerBootStrap的啟動和任務處理流程,基本涵蓋了Netty的關鍵架構。OneCoder總結了一張流程圖:

技術分享

該圖是OneCoder通過閱讀Netty源碼,逐漸記錄下來的。基本可以說明Netty服務的啟動流程。這裏在具體講解一下。

首先說明,我們這次順利的流程是基於NioSocketServer的。也就是基於Java NIO Selector的實現方式。在第六講《Java NIO框架Netty教程(六)-Java NIO Selector模式》中,我們已經知道使用Selector的幾個關鍵點,所以這裏我們也重點關註一下,這些點在Netty中是如何使用的。

很多看過Netty源碼的人都說Netty源碼寫的很漂亮。可漂亮在哪呢?Netty通過一個ChannelFactory決定了你當前使用的協議類型(Nio/oio等),比如,OneCoder這裏使用的是NioServerSocket,那麽需要聲明的Factory即為NioServerSocketChannelFactory,聲明了這個Factory,就決定了你使用的Channel,pipeline以及pipeline中,具體處理業務的sink的類型。這種使用方式十分簡潔的,學習曲線很低,切換實現十分容易。

Netty采用的是基於事件的管道式架構設計,事件(Event)在管道(Pipeline)中流轉,交由(通過pipelinesink)相應的處理器(Handler

)。這些關鍵元素類型的匹配都是由開始聲明的ChannelFactory決定的。

Netty框架內部的業務也遵循這個流程,Server端綁定端口的binder其實也是一個Handler,在構造完Binder後,便要聲明一個Pipeline管道,並賦給新建一個ChannelNettynewChannel的過程中,相應調用了Java中的ServerSocketChannel.open方法,打開一個channel。然後觸發fireChannelOpen事件。這個事件的接受是可以復寫的。Binder自身接收了這個事件。在事件的處理中,繼續向下完成具體的端口的綁定。對應Selector中的 socketChannel.socket().bind()

。然後觸發fireChannelBound事件。默認情況下,該事件無人接受,繼續向下開始構造Boss線程池。我們知道在NettyBoss線程池是用來接受和分發請求的核心線程池。所以在channel綁定後,必然要啟動Boss線城池,隨時準備接受client發來的請求。在Boss構造函數中,第一次註冊了selector感興趣的事件類型,SelectionKey.OP_ACCEPT。至此,在第六講中介紹的使用Selector的幾個關鍵步驟都體現在Netty中了。在Boss中回啟動一個死循環來查詢是否有感興趣的事件發生,對於第一次的客戶端的註冊事件,Boss會將Channel註冊給worker保存。

這裏補充一下,也是圖中忽略的部分,就是關於worker線程池的初始化時機問題。worker池的構造,在最開始構造ChannelFactory的時候就已經準備好了。在NioServerSocketChannelFactory的構造函數裏,會new一個NioWorkerPool。在NioWorkerPool的基類AbstractNioWorkerPool的構造函數中,會調用OpenSelector方法,其中也打開了一個selector,並且啟動了worker線程池。<

private void openSelector() {
        try {
            selector = Selector.open();
        } catch (Throwable t) {
            throw new ChannelException(&quot;Failed to create a selector.&quot;, t);
        }

        // Start the worker thread with the new Selector.
        boolean success = false;
        try {
            DeadLockProofWorker.start(executor, new ThreadRenamingRunnable(this, &quot;New I/O  worker #&quot; + id));
            success = true;
        } finally {
            if (!success) {
                // Release the Selector if the execution fails.
                try {
                    selector.close();
                } catch (Throwable t) {
                    logger.warn(&quot;Failed to close a selector.&quot;, t);
                }
                selector = null;
                // The method will return to the caller at this point.
            }
        }
        assert selector != null &amp;&amp; selector.isOpen();
    }

至此,會分線程啟動AbstractNioWorker中run邏輯。同樣是循環處理任務隊列。

processRegisterTaskQueue();
processEventQueue();
processWriteTaskQueue();
processSelectedKeys(selector.selectedKeys());

這樣,設計使事件的接收和處理模塊完全解耦。

由此可見,如果你想從nio切換到oio,只需要構造不同的ChannelFacotry即可。果然簡潔優雅。

Java NIO框架Netty教程(十六)-ServerBootStrap啟動流程源碼分析