1. 程式人生 > >Java NIO框架Netty教程(四) – ServerBootStrap啟動流程原始碼分析

Java NIO框架Netty教程(四) – ServerBootStrap啟動流程原始碼分析

該圖是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管道,並賦給新建一個Channel。Netty在newChannel的過程中,相應呼叫了Java中的 ServerSocketChannel.open方法,開啟一個channel。然後觸發fireChannelOpen事件。這個事件的接受是可以複寫的。Binder自身接收了這個事件。在事件的處理中,繼續向下完成具體的埠的繫結。對應Selector中的 socketChannel.socket().bind()。然後觸發fireChannelBound事件。預設情況下,該事件無人接受,繼續向下開始構造Boss執行緒池。我們知道在Netty中Boss執行緒池是用來接受和分發請求的核心執行緒池。所以在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執行緒池。
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 private void openSelector() { try { selector = Selector.open(); catch (Throwable t) { throw new ChannelException("Failed to create a selector.", t); } // Start the worker thread with the new Selector. boolean success = false; try { DeadLockProofWorker.start(executor, new ThreadRenamingRunnable(
this"New I/O  worker #" + id)); success = true; finally { if (!success) { // Release the Selector if the execution fails. try { selector.close(); catch (Throwable t) { logger.warn("Failed to close a selector.", t); } selector = null; // The method will return to the caller at this point. } } assert
 selector != null && selector.isOpen(); }