歡迎光臨
每天分享高質量文章

為什麼建議 Netty 的 I/O 執行緒與業務執行緒分離

點選上方“芋道原始碼”,選擇“置頂公眾號”

技術文章第一時間送達!

原始碼精品專欄

 

來源:Netty 之家

問題背景:

某網際網路同學諮詢一個Netty使用問題:最近在研究公司內部的RPC框架,發現底層通訊框架使用的是Netty,而且Netty的I/O執行緒與處理業務的執行緒分離。具體如下:

1、負責服務端監聽的是Accept  NioEventLoopGroup執行緒組

2、負責鏈路讀寫操作的是Work NioEventLoopGroup執行緒組

3、訊息解碼完成之後,投遞到後端的一個業務執行緒池中處理,執行緒池使用的是JDK自帶的執行緒池

該同學的疑問:為什麼業務的處理不能放到Work NioEventLoopGroup中?

1、如果業務執行緒處理比較慢,即便I/O執行緒處理再快,業務端到端響應還是不會縮短

2、I/O執行緒到業務執行緒存在執行緒背景關係切換,增加了額外的開銷

想法:

構造一個執行緒數較大(例如1024)的NioEventLoopGroup,同時處理鏈路的讀寫和業務處理。即業務處理和訊息讀寫統一使用Netty的I/O執行緒池(實質自定義的執行緒組)。


問題答覆

Netty I/O執行緒和業務處理執行緒分離原因:

1、充分利用多核的並行處理能力:I/O執行緒和業務執行緒分離,雙方可以並行的處理網路I/O和業務邏輯,充分利用多核的平行計算能力,提升效能。

2、故障隔離:後端的業務執行緒池處理各種型別的業務訊息,有些是I/O密集型、有些是CPU密集型、有些是純記憶體計算型,不同的業務處理時延,以及發生故障的機率都是不同的。如果把業務執行緒和I/O執行緒合併,就會存在如下問題:

1)某類業務處理較慢,阻塞I/O執行緒,導致其它處理較快的業務訊息的響應無法及時發送出去。

2)即便是同類業務,如果使用同一個I/O執行緒同時處理業務邏輯和I/O讀寫,如果請求訊息的業務邏輯處理較慢,同樣會導致響應訊息無法及時發送出去。

3、可維護性:I/O執行緒和業務執行緒分離之後,雙方職責單一,有利於程式碼維護和問題定位。如果合設在一起,當RPC呼叫時延增大之後,到底是網路問題、還是I/O執行緒問題、還是業務邏輯問題導致的時延大,糾纏在一起,問題定位難度非常大。例如業務執行緒中訪問快取或者資料庫偶爾時延增大,就會導致I/O執行緒被阻塞,時延出現毛刺,這些時延毛刺的定位,難度非常大。

4、資源代價:NioEventLoopGroup的建立並不是廉價的,它會聚合Selector,Selector本身就會消耗控制代碼資源。

Netty的NioEventLoop設計理念就是透過有限的I/O執行緒,透過多路復用和非阻塞的方式,一個執行緒同時處理成百上千個鏈路,來解決傳統一連線一執行緒的同步阻塞模型。

因此,它的建立成本也較高,一個行程中不宜建立過多NioEventLoop。

相關程式碼如下所示:

5、執行緒切換的代價:如果不是追求極致的效能,執行緒切換隻要不過於頻繁,它的代價還是可以接受的。在一個複雜的系統中,當你整合第三方SDK時,例如Redis Client,通常都包含著隱式的執行緒切換。一些場景下,為了實現系統的高可用性,例如 基於Hystrix做故障隔離,同樣會存在執行緒切換:Hystrix基於執行緒做故障隔離


該問題引申的其它幾個問題

1、Netty的I/O執行緒 NioEventLoop是否可以處理非I/O任務?

答案是肯定的,透過它提供的介面就可以看出這點:

它的execute方法引數是Runnable,與JDK的執行緒池execute方法是等價的(異常處理策略存在差異)。

開了這個口子之後,就會存在風險,例如使用者為了簡化執行緒處理模型,把所有的業務任務封裝成Task,丟到Nett用的I/O執行緒NioEventLoop中執行。為了防止過多的業務任務阻塞I/O執行緒的網路讀寫操作,NioEventLoop提供了設定I/O任務和非I/O任務的處理比例,透過合理的調整處理比例,來保證更合理的資源排程。

Netty並不反對在I/O執行緒中處理非I/O任務,而是需要使用者必須要避免意外的I/O執行緒阻塞,以及過多的佔用I/O任務排程,導致網路I/O處理效能下降。

2、一個超大的JDK業務執行緒池是不合適的,原因有兩個:

1)效能問題:JDK執行緒池預設採用一個阻塞佇列,N個work執行緒的樣式,隨著work執行緒數的增加、佇列的爭用會非常激烈,進而導致效能下降。

建議採用N組執行緒池,每個執行緒池執行緒數儘量少的方式增加並行處理能力,

減少鎖爭用。

2)故障隔離問題:如果後端只有一個執行緒池,某個服務故障將會導致整個行程不可用。採用分組處理業務服務的方式,可以降低故障的影響範圍,示例如下所示:




如果你對 Dubbo / Netty 等等原始碼與原理感興趣,歡迎加入我的知識星球一起交流。長按下方二維碼噢

目前在知識星球更新了《Dubbo 原始碼解析》目錄如下:

01. 除錯環境搭建
02. 專案結構一覽
03. 配置 Configuration
04. 核心流程一覽

05. 拓展機制 SPI

06. 執行緒池

07. 服務暴露 Export

08. 服務取用 Refer

09. 註冊中心 Registry

10. 動態編譯 Compile

11. 動態代理 Proxy

12. 服務呼叫 Invoke

13. 呼叫特性 

14. 過濾器 Filter

15. NIO 伺服器

16. P2P 伺服器

17. HTTP 伺服器

18. 序列化 Serialization

19. 叢集容錯 Cluster

20. 優雅停機

21. 日誌適配

22. 狀態檢查

23. 監控中心 Monitor

24. 管理中心 Admin

25. 運維命令 QOS

26. 鏈路追蹤 Tracing

… 一共 69+ 篇

目前在知識星球更新了《Netty 原始碼解析》目錄如下:

01. 除錯環境搭建
02. NIO 基礎
03. Netty 簡介
04. 啟動 Bootstrap

05. 事件輪詢 EventLoop

06. 通道管道 ChannelPipeline

07. 通道 Channel

08. 位元組緩衝區 ByteBuf

09. 通道處理器 ChannelHandler

10. 編解碼 Codec

11. 工具類 Util

… 一共 61+ 篇

目前在知識星球更新了《資料庫物體設計》目錄如下:


01. 商品模組
02. 交易模組
03. 營銷模組
04. 公用模組

… 一共 17+ 篇

原始碼不易↓↓↓

點贊支援老艿艿↓↓

贊(0)

分享創造快樂