Netty源碼分析--Channel註冊(上)(五)

        其實在將這一節以前,咱們來分析一個東西,方便下面的工做好開展。算法

        打開啓動類,最開始的時候建立了一個NioEventLoopGroup 事件循環組,咱們來跟一下這個。數組

        

      這裏bossGroup, 我傳入了一個線程, workerGroup 沒有入參,默認0, 也就是說父級我用一個線程來處理客戶端的接入, 多個線程來處理客戶端的讀寫操做安全

      經過一連串的構造方法進入到異步

     executor 是 null , selectorProvider 用來建立一個多路複用器, 最後一個參數傳入了 一個多路複用器的一個策略, 這個策略咱們後面會講到ide

    

     緊接着又傳入了一個拒絕策略, 因爲  NioEventLoopGroup 繼承了 MultithreadEventLoopGroup,因此進入 MultithreadEventLoopGroup 的構造方法oop

     

    這裏的判斷, bossGroup 傳入了 1 ,因此nThreads = 1;  workerGroup 沒有入參,默認是0 ,因此這裏到了一個判斷默認線程數的地方。性能

   

     就是這了, NettyRuntime.availableProcessors() * 2  表明 CPU核心數 * 2(處理器超線程數) * 2的值  或  CPU數 * 2的值  spa

     在cmd命令中輸入「wmic」,而後在出現的新窗口中輸入「cpu get *」。
     NumberOfCores:表示CPU核心數
     NumberOfLogicalProcessors:表示CPU線程數線程

     

     因此我這裏是 CPU核心數 是 2 , CPU 線程數 是 4 , 因此個人 NettyRuntime.availableProcessors() * 2 = 8, 而後這裏Math.max(1,8) 取大的 就是 8 了blog

     因此個人 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沒有線程安全問題,固然隨着後面的講解,你將會對這個地方理解的更加的深入。 

       好了,註冊的內容咱們下一節接着說。

相關文章
相關標籤/搜索