1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        面試官:Netty的線程模型可不是Reactor這么簡單

        共 2855字,需瀏覽 6分鐘

         ·

        2021-03-27 10:14

        筆者看來Netty的內(nèi)核主要包括如下圖三個部分:


        其各個核心模塊主要的職責如下:

        • 內(nèi)存管理
          主要提高高效的內(nèi)存管理,包含內(nèi)存分配,內(nèi)存回收。
        • 網(wǎng)通通道
          復制網(wǎng)絡(luò)通信,例如實現(xiàn)對NIO、OIO等底層JAVA API 的封裝,簡化網(wǎng)絡(luò)編程模型。
        • 線程模型
          提供高效的線程協(xié)作模型。

        大家不妨回想一下在以往的面試的過程中,面試官通常會問:Netty 的線程模型是什么?

        主從多 Reactor 模型,相信大家都能脫口而出,然后呢?就沒有然后了?

        線程模型在網(wǎng)絡(luò)通信中主要解決什么樣的問題?在 Netty 中又是如何解決的,Netty 的線程模型為什么如此高效?請容我慢慢道來。

        溫馨提示:為了保證文章觀點的嚴謹性,將探究領(lǐng)域鎖定在:Netty NIO 相關(guān)。

        1、主從多 Reactor 模型


        主從多 Reactor 模型是業(yè)界一種非常經(jīng)典的線程編程模型,其原理圖如下所示:


        我們首先簡單介紹一下上圖中涉及的幾個重要角色:

        • Acceptor
          請求接收者,在實踐時其職責類似服務器,并不真正負責連接請求的建立,而只將其請求委托 Main Reactor 線程池來實現(xiàn),起到一個轉(zhuǎn)發(fā)的作用。
        • Main Reactor
          主 Reactor 線程組,主要負責連接事件,并將IO讀寫請求轉(zhuǎn)發(fā)到 SubReactor 線程池。當然在一些需要對客戶端進行權(quán)限控制等場景下,權(quán)限校驗的職責可以放到 Main Reactor 線程池,即 Main Reactor 也可以注冊通道的讀寫事件,讀取客戶端權(quán)限校驗相關(guān)的數(shù)據(jù)包,執(zhí)行權(quán)限驗證,權(quán)限驗證通過后再將2通道注冊到IO線程。
        • Sub Reactor
          Main Reactor 通常監(jiān)聽客戶端連接后會將通道的讀寫轉(zhuǎn)發(fā)到 Sub Reactor 線程池中一個線程(負載均衡),負責數(shù)據(jù)的讀寫。在 NIO 中 通常注冊通道的讀(OP_READ)、寫事件(OP_WRITE)。

        為了更加深刻的理解主從 Reactor 模型,我們來看一下網(wǎng)絡(luò)通訊一般會包含哪些關(guān)鍵動作:


        一個網(wǎng)絡(luò)交互通常的幾個步驟如下:

        • 服務端啟動,并在特定端口上監(jiān)聽,例如 web 應用的 80端口。
        • 客戶端發(fā)起TCP的三次握手,與服務端建立連接,這里以 NIO 為例,連接成功建立后會創(chuàng)建NioSocketChannel對象。
        • 服務端通過 NioSocketChannel 從網(wǎng)卡中讀取數(shù)據(jù)
        • 服務端根據(jù)通信協(xié)議從二進制流中解碼出一個個請求。
        • 根據(jù)請求,執(zhí)行對應的業(yè)務操作,例如 Dubbo 服務端接受一個查詢用戶ID為1的用戶信息。
        • 將業(yè)務執(zhí)行結(jié)果返回到客戶端,通常涉及到協(xié)議編碼、壓縮等。

        線程模型需要解決的問題:連接監(jiān)聽、網(wǎng)絡(luò)讀寫、編碼、解碼、業(yè)務執(zhí)行這些操作步驟如何運用多線程編程,提升性能。

        主從多Reactor模型是如何解決上面的問題呢?

        1. 連接建立(OP_ACCEPT)由 Main Reactor 線程池負責,創(chuàng)建NioSocketChannel后,將其轉(zhuǎn)發(fā)給SubReactor。
        2. SubReactor 線程池主要負責網(wǎng)絡(luò)的讀寫(從網(wǎng)絡(luò)中讀字節(jié)流、將字節(jié)流發(fā)送到網(wǎng)絡(luò)中),即注冊O(shè)P_READ、OP_WRITE,并且同一個通道會綁定一個SubReactor線程。
        3. 編碼、解碼、業(yè)務執(zhí)行,則具體情況具體分析
          通常編碼、解碼會放在IO線程中執(zhí)行,而業(yè)務邏輯的執(zhí)行通常會采用額外的線程池,但不是絕對的,一個好的框架通常會使用參數(shù)來進行定制化選擇,例如 ping、pong 這種心跳包,直接在 IO 線程中執(zhí)行,無需再轉(zhuǎn)發(fā)到業(yè)務線程池,避免線程切換開銷。

        溫馨提示:在網(wǎng)絡(luò)編程中,通常將用于網(wǎng)絡(luò)讀寫的線程稱為IO線程。

        2、Netty 的線程模型


        Netty的線程模型是基于主從多Reactor模型。


        Netty 中網(wǎng)絡(luò)的連接事件(OP_ACCEPT)由Main Reactor 線程組實現(xiàn),即 Boss Group,通常只需設(shè)置一個線程。

        網(wǎng)絡(luò)的讀寫操作由 Work Group ( Sub Reactor) 線程組來實現(xiàn),線程的個數(shù)默認為 2 * CPU Core,一個 Channel 綁定到其中一個 Work 線程,一個 Work 線程中可以綁定多個 Channel

        在 Netty 中編碼、解碼等操作會被封裝成一個一個事件處理器(ChannelHandler),那這些 Handler 是在IO線程池中執(zhí)行?

        默認情況下ChannelHandler 是在 IO 線程中執(zhí)行,那如何改變默認行為呢?其關(guān)鍵代碼如下:


        關(guān)鍵點:在將事件處理器添加到事件鏈時可以指定在哪個線程池中執(zhí)行,如果不指定則為IO線程中執(zhí)行。

        面試官:通常業(yè)務操作會專門開辟一個線程池,那業(yè)務處理完成之后,如何將響應結(jié)果通過 IO 線程寫入到網(wǎng)卡中呢?


        業(yè)務線程調(diào)用 Channel 對象的 write 方法并不會立即寫入網(wǎng)絡(luò),只是將數(shù)據(jù)放入一個待寫入隊列(緩存區(qū)),然后IO線程每次執(zhí)行事件選擇后,會從待寫入緩存區(qū)中獲取寫入任務,將數(shù)據(jù)真正寫入到網(wǎng)絡(luò)中,數(shù)據(jù)到達網(wǎng)卡之前會經(jīng)過一系列的 Channel Handler(Netty事件傳播機制),最終寫入網(wǎng)卡。

        最后再來介紹一下 Netty 中 IO 線程的大體工作流程。


        IO線程處理的關(guān)鍵點:

        • 每一IO線程在執(zhí)行上述操作時是串行執(zhí)行的,即注冊在一個 Selector(事件選擇器)中的所有通道,同一時間只有一個通道的事件被處理。這也是為什么NIO應對大文件傳輸時不具備優(yōu)勢的根本原因。
        • IO 線程在處理完所有就緒事件后,還會從任務隊列(Task Queue)獲取任務,例如上文中提到的業(yè)務線程在執(zhí)行完業(yè)務后需要將返回結(jié)果寫入網(wǎng)絡(luò),Netty 中所有的網(wǎng)絡(luò)讀寫操作只能在IO線程中真正獲得運行,故業(yè)務線程需要將帶寫入的響應結(jié)果封裝成 Task,放入到 IO 線程任務隊列中。

        3、總結(jié)


        回到到主題,如果我們在面試過程中碰到面試官提問“Netty 的線程模型是什么?”時,我們應該可以從容應對了。

        我覺得可以從如下幾個方面進行展開。

        1. Netty的線程模型基于主從多Reactor模型。通常由一個線程負責處理OP_ACCEPT事件,擁有 CPU 核數(shù)的兩倍的IO線程處理讀寫事件。
        2. 一個通道的IO操作會綁定在一個IO線程中,而一個IO線程可以注冊多個通道。
        3. 在一個網(wǎng)絡(luò)通信中通常會包含網(wǎng)絡(luò)數(shù)據(jù)讀寫,編碼、解碼、業(yè)務處理。默認情況下編碼、解碼等操作會在IO線程中運行,但也可以指定其他線程池。
        4. 通常業(yè)務處理會單獨開啟業(yè)務線程池,但也可以進一步細化,例如心跳包可以直接在IO線程中處理,而需要再轉(zhuǎn)發(fā)給業(yè)務線程池,避免線程切換。
        5. 在一個IO線程中所有通道的事件是串行處理的。


        有道無術(shù),術(shù)可成;有術(shù)無道,止于術(shù)
        歡迎大家關(guān)注Java之道公眾號

        好文章,我在看??
        瀏覽 65
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        評論
        圖片
        表情
        推薦
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            bl趴跪覆揉指双龙嗯啊bl | 日韩视频在线观看免费 | 日批视频在线观看 | 男男视频h| 国产蜜臀97一区二区三区 | ass中国裸体pics | 成人毛片大全 | 大型操逼网站 | 肥婆一级毛BB毛片 | 做爱网站在线观看 |