1. 程式人生 > >Netty原始碼分析--Channel註冊(上)(五)

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沒有執行緒安全問題,當然隨著後面的講解,你將會對這個地方理解的更加的深刻。 

       好了,註冊的內容我們下一節接著