常用高併發網路執行緒模型效能優化實現-體驗百萬級高併發執行緒模型設計
關於作者
前滴滴出行技術專家,現任OPPO 文件資料庫 mongodb 負責人,負責 oppo 千萬級峰值 TPS/ 十萬億級資料量文件資料庫 mongodb 研發和運維工作,一直專注於分散式快取、高效能服務端、資料庫、中介軟體等相關研發。後續持續分享《 MongoDB 核心原始碼設計、效能優化、最佳運維實踐》, Github 賬號地址 : https://github.com/y123456yz
前言:
服務端通常需要支援高併發業務訪問,如何設計優秀的服務端網路IO
工作執行緒
/
程式模型對業務的高併發訪問需求起著至關重要的核心作用。
本文總結了了不同場景下的多種網路IO 執行緒 / 程式模型,並給出了各種模型的優缺點及其效能優化方法,非常適合服務端開發、中介軟體開發、資料庫開發等開發人員借鑑。
1. 執行緒模型一 . 單執行緒網路 IO 複用模型
說明:
1. 所有網路 IO 事件 (accept 事件、讀事件、寫事件 ) 註冊到 epoll 事件集
2. 主迴圈中通過 epoll_wait 一次性獲取核心態收集到的 epoll 事件資訊,然後輪詢執行各個事件對應的回撥。
3. 事件註冊、 epoll_wait 事件獲取、事件回撥執行全部由一個執行緒處理
1.1 一個完整請求組成
一個完整的請求處理過程主要包含以下幾個部分:
步驟1 :通過 epoll_wait 一次性獲取網路 IO 事件
步驟2 :讀取資料及協議解析
步驟3 :解析成功後進行業務邏輯處理,然後應答客戶端
1.2 該網路執行緒模型缺陷
1. 所有工作都由一個執行緒執行,包括 epoll 事件獲取、事件處理 ( 資料讀寫 ) 、只要任一一個請求的事件回撥處理阻塞,其他請求都會阻塞。例如 redis 的 hash 結構,如果 filed 過多,假設一個 hash key 包含數百萬 filed ,則該 Hash key 過期的時候,整個 redis 阻塞。
2. 單執行緒工作模型, CPU 會成為瓶頸,如果 QPS 過高,整個 CPU 負載會達到 100% ,時延抖動厲害。
1.3 典型案例
1. redis 快取
2. 推特快取中介軟體twemproxy
1.4 主迴圈工作流程
1. while (1) {
2. //epoll_wait等待網路事件,如果有網路事件則返回,或者超時範圍
3. size_t numevents= epoll_wait();
4.
5. //遍歷前面epoll獲取到的網路事件,執行對應事件回撥
6. for (j = 0; j < numevents; j++) {
7. if(讀事件) {
8. //讀資料
9. readData()
10. //解析
11. parseData()
12. //讀事件處理、讀到資料後的業務邏輯處理
13. requestDeal()
14. } else if(寫事件) {
15. //寫事件處理,寫資料邏輯處理
16. writeEentDeal()
17. } else {
18. //異常事件處理
19. errorDeal()
20. }
21. }
22. }
說明: 後續多執行緒/ 程式模型中,每個執行緒 / 程式的主流程和該 while() 流程一致。
1.5 redis 原始碼分析及非同步網路 IO 複用精簡版 demo
由於之前工作需要,需要對redis 核心做二次優化開發,因此對整個 redis 程式碼做了部分程式碼註釋,同時把 redis 的網路模組獨立出來做成了簡單 demo ,該 demo 對理解 epoll 網路事件處理及 Io 複用實現會有幫助,程式碼比較簡短,可以參考如下地址 :
2. 執行緒模型二 . 單 listener+ 固定 worker 執行緒
該執行緒模型圖如下圖所示:
說明:
1. listener 執行緒負責接受所有的客戶端連結
2. listener 執行緒每接收到一個新的客戶端連結產生一個新的 fd ,然後通過分發器傳送給對應的工作執行緒 (hash 方式 )
3. 工作執行緒獲取到對應的新連結 fd 後,後續該連結上的所有網路 IO 讀寫都由該執行緒處理
4. 假設有 32 個連結,則 32 個連結建立成功後,每個執行緒平均處理 4 個連結上的讀寫、報文處理、業務邏輯處理
2.1 該網路執行緒模型缺陷
1.
進行
accept
處理的
listener
執行緒只有一個,在瞬間高併發場景容易成為瓶頸
2.
一個執行緒通過
IO
複用方式處理多個連結
fd
的資料讀寫、報文解析及後續業務邏輯處理,這個過程會有嚴重的排隊現象,例如某個連結的報文接收解析完畢後的內部處理時間過長,則其他連結的請求就會阻塞排隊
2.2 典型案例
memcache 快取,適用於內部處理比較快的快取場景、代理中間場景。 memcache 原始碼實現中文分析可以詳見 : memcache 原始碼實現分析
3. 執行緒模型三 . 固定 worker 執行緒模型 (reuseport)
該模型原型圖如下:
說明:
1. Linux kernel 3.9 開始支援 reuseport 功能,核心協議棧每獲取到一個新連結自動均衡分發給使用者態 worker 執行緒。
2. 該模型解決了模型一的 listener 單點瓶頸問題 , 多個程式/ 執行緒同時做為 listener ,都可以 accept 客戶端新連結。
3.1
該網路程式
/
執行緒模型缺陷
reuseport
支援後,核心通過負載均衡的方式分發不同新連結到多個使用者態
worker
程式
/
執行緒
,
每個程式/
執行緒
通過IO
複用方式處理多個
客戶端新
連結fd
的資料讀寫、報文解析、解析後的業務邏輯處理
。
每個工作程式/
執行緒同時處理多個連結的請求
,
如果
某個連結的報文接收解析完畢後的內部處理時間
過長
,則其他連結的請求就會阻塞排隊
。
該模型雖然解決了 listener 單點瓶頸問題 , 但是工作執行緒內部的排隊問題沒有解決。
不過,Nginx 作為七層轉發代理,由於都是記憶體處理,所以內部處理時間比較短,所以適用於該模型。
3.2 典型案例
1. nginx(nginx 用的是程式,模型原理一樣 ) ,該模型適用於內部業務邏輯簡單的場景,如 nginx 代理等
2. reuseport 支援效能提升過程可以參考我另一篇分享: https://my.oschina.net/u/4087916/blog/3016162
Nginx 多程式高併發、低時延、高可靠機制在快取 (redis 、 memcache)twemproxy 代理中的應用
4. 執行緒模型四. 一個連結一個執行緒模型
該執行緒模型圖如下圖:
說明:
1. listener 執行緒負責接受所有的客戶端連結
2. listener 執行緒每接收到一個新的客戶端連結就建立一個執行緒,該執行緒只負責處理該連結上的資料讀寫、報文解析、業務邏輯處理。
4.1 該網路執行緒模型缺陷:
1. 一個連結建立一個執行緒,如果 10 萬個連結,那麼就需要 10 萬個執行緒,執行緒數太多,系統負責、記憶體消耗也會很多
2. 當連結關閉的時候,執行緒也需要銷燬,頻繁的執行緒建立和消耗進一步增加系統負載
4.2 典型案例:
1. mysql 預設方式、 mongodb 同步執行緒模型配置,適用於請求處理比較耗時的場景,如資料庫服務
2. Apache web 伺服器,該模型限制了 apache 效能, nginx 優勢會更加明顯
5. 執行緒模型五 . 單 listener+ 動態 worker 執行緒 ( 單佇列 )
該執行緒模型圖如下圖所示:
說明:
1. listener 執行緒接收到一個新連結 fd 後,把該 fd 交由執行緒池處理,後續該連結的所有讀寫、報文解析、業務處理都由執行緒池中多個執行緒處理。
2. 該模型把一次請求轉換為多個任務 ( 網路資料讀寫、 讀取資料 後的業務邏輯處理) 入隊到全域性佇列,執行緒池中的執行緒從佇列中獲取任務執行。
3. 同一個請求訪問被拆分為多個任務,一次請求可能由多個執行緒處理。
3. 當任務太多,系統壓力大的時候,執行緒池中執行緒數動態增加
4. 當任務減少,系統壓力減少的時候,執行緒池中執行緒數動態減少
5.1 工作執行緒執行時間相關的幾個統計:
T1: 呼叫底層 asio 庫接收一個完整 mongodb 報文的時間
T2: 接收到報文後的後續所有處理 ( 含報文解析、認證、引擎層處理、傳送資料給客戶端等 )
T3: 執行緒等待資料的時間 ( 例如:長時間沒有流量,則現在等待讀取資料 )
5.2 單個工作執行緒如何判斷自己處於”空閒”狀態:
執行緒執行總時間=T1 + T2 +T3 ,其中 T3 是無用等待時間。如果 T3 的無用等待時間佔比很大,則說明執行緒比較空閒。工作執行緒每一次迴圈處理後判斷有效時間佔比,如果小於指定閥值,則自己直接 exit 退出銷燬
5.3 如何判斷執行緒池中工作執行緒“太忙”:
控制執行緒專門用於判斷執行緒池中工作執行緒的壓力情況,以此來決定是否線上程池中建立新的工作執行緒來提升效能。
控制執行緒每過一定時間迴圈檢查執行緒池中的執行緒壓力狀態,實現原理就是簡單的實時記錄執行緒池中的執行緒當前執行情況,為以下兩類計數:匯流排程數_threadsRunning 、當前正在執行 task 任務的執行緒數 _threadsInUse 。如果 _threadsRunning=_threadsRunning ,說明所有工作執行緒當前都在處理 task 任務,執行緒池中執行緒壓力大,這時候控制執行緒就開始增加執行緒池中執行緒數。
該模型詳細原始碼實現過程更多細節詳見:https://my.oschina.net/u/4087916/blog/4295038
5.4 該網路執行緒模型缺陷:
1. 執行緒池獲取任務執行,有鎖競爭,這裡就會成為系統瓶頸
5.5 典型案例:
mongodb 動態 adaptive 執行緒模型,適用於請求處理比較耗時的場景,如資料庫服務
該模型詳細原始碼優化分析實現過程參考:
https://my.oschina.net/u/4087916/blog/4295038
Mongodb 網路傳輸處理原始碼實現及效能調優 - 體驗核心效能極致設計
6. 執行緒模型六. 單 listener+ 動態 worker 執行緒 ( 多佇列 ) -mongodb 網路執行緒模型優化實踐
該執行緒模型圖如下:
說明:
把一個全域性佇列拆分為多個佇列,任務入隊的時候按照hash 雜湊到各自的佇列,工作執行緒獲取獲取任務的時候,同理通過 hash 的方式去對應的佇列獲取任務,通過這種方式減少鎖競爭,同時提升整體效能。
6.1 典型案例:
mongodb 核心多佇列 adaptive 執行緒模型優化,效能有很好的提升,適用於請求處理比較耗時的場景,如資料庫服務。該模型詳細原始碼優化分析實現過程參考:
Mongodb 網路傳輸處理原始碼實現及效能調優 - 體驗核心效能極致設計
6.2 疑問?為啥 mysql 、 mongodb 等資料庫沒有利用核心的 reuseport 特殊 - 多執行緒同時處理 accept 請求?
答: 實際上所有服務都可以利用這一特性,包括資料庫服務(mongodb 、 mysql 等 ) 。但是因為資料庫服務訪問時延一般都是 ms 級別,如果 reuseport 特性利用起來,時延會有幾十 us 的效能提升,這相比資料庫內部處理的 ms 級時延,這幾十 us 的效能提升,基本上可以忽略掉,這也是大部分資料庫服務沒有支援該功能的原因。
快取,代理等中介軟體,由於本身內部處理時間就比較小,也是us 級別,所以需要充分利用該特性。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69984922/viewspace-2727336/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 常用高併發網路執行緒模型設計及mongodb執行緒模型優化實踐執行緒模型MongoDB優化
- mongodb核心原始碼實現及效能最佳化:常用高併發執行緒模型設計及mongodb執行緒模型最佳化實踐MongoDB原始碼執行緒模型
- 多執行緒與高併發(二)執行緒安全執行緒
- Java高併發與多執行緒(二)-----執行緒的實現方式Java執行緒
- 【多執行緒高併發程式設計】二 實現多執行緒的幾種方式執行緒程式設計
- nodejs 單執行緒 高併發NodeJS執行緒
- 多執行緒與高併發(一)多執行緒入門執行緒
- 併發程式設計之多執行緒執行緒安全程式設計執行緒
- 【多執行緒與高併發】- 執行緒基礎與狀態執行緒
- 【多執行緒與高併發3】常用鎖例項執行緒
- epoll程式設計,單epoll+執行緒池?執行緒池+epoll?nginx實現高併發的原理?程式設計執行緒Nginx
- 【多執行緒與高併發原理篇:3_java記憶體模型】執行緒Java記憶體模型
- 【高併發】深入理解執行緒的執行順序執行緒
- java併發筆記之java執行緒模型Java筆記執行緒模型
- Java高併發與多執行緒(一)-----概念Java執行緒
- 多執行緒高併發解決辦法執行緒
- 【python高併發】程序、執行緒的理解Python執行緒
- 併發程式設計之:執行緒程式設計執行緒
- JAVA多執行緒下高併發的處理經驗Java執行緒
- boost中asio網路庫多執行緒併發處理實現,以及asio在多執行緒模型中執行緒的排程情況和執行緒安全。執行緒模型
- Java併發程式設計之執行緒安全、執行緒通訊Java程式設計執行緒
- 【多執行緒與高併發 2】volatile 篇執行緒
- 學好執行緒池,搞定高併發!(文末福利)執行緒
- 【多執行緒與高併發】- 淺談volatile執行緒
- JUC之Exchanger-多執行緒與高併發執行緒
- 分散式叢集與多執行緒高併發分散式執行緒
- 併發程式設計之volatile與JMM多執行緒記憶體模型程式設計執行緒記憶體模型
- 用多執行緒,實現併發,TCP執行緒TCP
- Java併發實戰一:執行緒與執行緒安全Java執行緒
- MySQL多執行緒併發調優MySql執行緒
- 併發程式設計與執行緒安全程式設計執行緒
- java併發程式設計——執行緒池Java程式設計執行緒
- 從執行緒到併發程式設計執行緒程式設計
- java併發程式設計——執行緒同步Java程式設計執行緒
- Java併發程式設計:Java執行緒Java程式設計執行緒
- 多執行緒併發篇——如何停止執行緒執行緒
- 多執行緒高併發程式設計(10) -- ConcurrentHashMap原始碼分析執行緒程式設計HashMap原始碼
- 多執行緒高併發程式設計(8) -- Fork/Join原始碼分析執行緒程式設計原始碼