Java NIO框架Netty教程(四) – ServerBootStrap啟動流程原始碼分析
阿新 • • 發佈:2019-01-05
該圖是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();
}
|