Netty原始碼分析--Channel註冊(上)(五)
其實在將這一節之前,我們來分析一個東西,方便下面的工作好開展。
開啟啟動類,最開始的時候建立了一個NioEventLoopGroup 事件迴圈組,我們來跟一下這個。
這裡bossGroup, 我傳入了一個執行緒, workerGroup 沒有入參,預設0, 也就是說父級我用一個執行緒來處理客戶端的接入, 多個執行緒來處理客戶端的讀寫操作
通過一連串的構造方法進入到
executor 是 null , selectorProvider 用來建立一個多路複用器, 最後一個引數傳入了 一個多路複用器的一個策略, 這個策略我們後面會講到
緊接著又傳入了一個拒絕策略, 由於 NioEventLoopGroup 繼承了 MultithreadEventLoopGroup,所以進入 MultithreadEventLoopGroup 的構造方法
這裡的判斷, bossGroup 傳入了 1 ,所以nThreads = 1; workerGroup 沒有入參,預設是0 ,所以這裡到了一個判斷預設執行緒數的地方。
就是這了, NettyRuntime.availableProcessors() * 2 代表 CPU核心數 * 2(處理器超執行緒數) * 2的值 或 CPU數 * 2的值
在cmd命令中輸入“wmic”,然後在出現的新視窗中輸入“cpu get *”。
NumberOfCores:表示CPU核心數
NumberOfLogicalProcessors:表示CPU執行緒數
所以我這裡是 CPU核心數 是 2 , CPU 執行緒數 是 4 , 所以我的 NettyRuntime.availableProcessors() * 2 = 8, 然後這裡Math.max(1,8) 取大的 就是 8 了
所以我的 workerGroup 執行緒數 是 8
好了,我們繼續跟進去
這裡建立了一個執行緒工廠,主要是為執行緒設定名字、是否守護程序、執行緒的優先順序等等。然後建立一個任務執行器,把執行緒工廠傳進去賦給成員變數
這個executor 後面在每個 事件執行器 建立子執行緒處理task來用
接下來建立一個長度是nThreads的 EventExecutor[] ,對於 子事件迴圈組來說,這裡其實是建立了一個長度為8 的NioEventLoop的陣列, 即 EventExecutor[] children = new NioEventLoop[8]
這個地方我糾結了一下,因為我語文太差了,我決定畫個圖來展示一下, 雖然我美術也不好。
大家看下這個程式碼層級結構, 其實就很顯而易見了,這裡我要說一下,SingleThreadEventExecutor 中有一個thread成員變數,說明每個都只有一個執行緒來處理,並且含有任務佇列和任務的執行器。
另外每一個NioEventLoop都含有一個selector 多路複用器 。
繼續看,這裡通過預設的選擇工廠來建立一個選擇器。
跟進去,我們看到下面的這段程式碼,不得不感嘆Netty真的已經把效能發揮到了極致,能用位運算的絕不會用數學計演算法,所以這裡對選擇器進行了區分
看這個判斷方法,意思是 如果是 2的次方 ,那麼建立一個 PowerOfTwoEventExecutorChooser 選擇器
我特意去驗證了一個這個演算法
結果是:
好了,講到這裡我們可以重新開始講註冊了,仍然進入 initAndRegister()方法
config().group() 這個獲取到的 是 ServerBootStrap.group(), 那麼也就是取到了 父級的事件迴圈組 也就是bossGroup
根據我上上圖的分析,那麼註冊方法進入的肯定是 MultithreadEventLoopGroup
這裡有一個next()方法,用來選擇一個NioEventLoop。由於我這裡是1個執行緒的陣列,所以進入
因此,對於bossGroup來說,就是 0 & 0 = 0 1 & 0 = 0 2 & 0 = 0 .....
對於workerGroup來說,就是 0 & 7 = 0 1 & 7 = 1 2 & 7 = 2 .... 8 & 7 = 0 9 & 7 = 1 ....
所以這裡其實是一個輪詢的演算法。
ok, 看到這裡我們猜測是 從 new NioEventLoop[1] 中輪詢一個 NioEventLoop, 然後把channel註冊到上面的多路複用器上。
繼續看, 根據那個流程圖,可以推斷出是進入到 SingleThreadEventLoop
接著傳入了一個 DefaultChannelPromise ,用來做註冊結果的非同步通知的。傳入了channel 和 當前的這個 SingleThreadEventLoop ,當然具體怎麼非同步通知的,我們後面會講到
繼續看
這裡把剛剛選擇出來的 NioEventLoop 賦給 Channel 的 eventLoop 的成員變數, 這裡也就意味著 ,這個NioEventLoop 也將一直伴隨 這個channel 的所有的讀寫操作, 因為通過上面的那個流程圖表明瞭 一個NioEventLoop 上面只有一個Thread , 那麼也可以得出 一個Channel 整個週期 內所有的讀寫操作,全部由同一個Thread來完成, 這也就說明了為什麼Netty沒有執行緒安全問題,當然隨著後面的講解,你將會對這個地方理解的更加的深刻。
好了,註冊的內容我們下一節接著