Java NIO框架Netty教程(十六)-ServerBootStrap啟動流程源碼分析
有一段事件沒有更新文章了,各種原因都有吧。搬家的瑣事,搬家後的安逸呵呵。不過,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
Netty框架內部的業務也遵循這個流程,Server端綁定端口的binder其實也是一個Handler,在構造完Binder後,便要聲明一個Pipeline管道,並賦給新建一個Channel。Netty在newChannel的過程中,相應調用了Java中的ServerSocketChannel.open方法,打開一個channel。然後觸發fireChannelOpen事件。這個事件的接受是可以復寫的。Binder自身接收了這個事件。在事件的處理中,繼續向下完成具體的端口的綁定。對應Selector中的 socketChannel.socket().bind()
這裏補充一下,也是圖中忽略的部分,就是關於worker線程池的初始化時機問題。worker池的構造,在最開始構造ChannelFactory的時候就已經準備好了。在NioServerSocketChannelFactory的構造函數裏,會new一個NioWorkerPool。在NioWorkerPool的基類AbstractNioWorkerPool的構造函數中,會調用OpenSelector方法,其中也打開了一個selector,並且啟動了worker線程池。<
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();
}
至此,會分線程啟動AbstractNioWorker中run邏輯。同樣是循環處理任務隊列。
processRegisterTaskQueue();
processEventQueue();
processWriteTaskQueue();
processSelectedKeys(selector.selectedKeys());
這樣,設計使事件的接收和處理模塊完全解耦。
由此可見,如果你想從nio切換到oio,只需要構造不同的ChannelFacotry即可。果然簡潔優雅。
Java NIO框架Netty教程(十六)-ServerBootStrap啟動流程源碼分析