標籤:pipeline com 很多 bst tty event start 核心線程 關於
有一段事件沒有更新文章了,各種原因都有吧。搬家的瑣事,搬家後的安逸呵呵。不過,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管道,並賦給建立一個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線程池。<
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啟動流程源碼分析