Web-Server有個配置,工作線程數。
Service一般也有個配置,工作線程數。
經驗豐富的架構師,懂得如何配置這些參數,使得系統的性能達到最優:有些業務設定為CPU核數的2倍,有些業務設定為CPU核數的8倍,有些業務設定為CPU核數的32倍。
“線程數”的設定依據,是本文要讨論的問題。
工作線程數是不是設定的越大越好?
答案顯然是否定的:
- 伺服器CPU核數有限,能夠同時并發的線程數有限,單核CPU設定1000個工作線程沒有意義
- 線程切換有開銷,如果線程切換過于頻繁,反而會使性能降低
調用sleep()函數的時候,線程是否一直占用CPU?
不占用,休眠時會把CPU讓出來,給其他需要CPU資源的線程使用。
不止sleep,一些阻塞調用,例如網絡程式設計中的:
- 阻塞accept(),等待用戶端連接配接
- 阻塞recv(),等待下遊回包
都會讓出CPU資源。
單核CPU,設定多線程有意義麼?
單核CPU,設定多線程能否提高并發性能?
即使是單核,使用多線程也是有意義的,大多數情況也能提高并發:
- 多線程編碼可以讓代碼更加清晰,例如:IO線程收發包,Worker線程進行任務處理,Timeout線程進行逾時檢測
-
如果有一個任務一直占用CPU資源在進行計算,此時增加線程并不能增加并發,例如以下代碼會一直占用CPU,并使得CPU占用率達到100%:
while(1){ i++; }
- 通常來說,Worker線程一般不會一直占用CPU進行計算,此時即使CPU是單核,增加Worker線程也能夠提高并發,因為這個線程在休息的時候,其他的線程可以繼續工作
常見服務線程模型有幾種?
了解常見的服務線程模型,有助于了解服務并發的原理,一般來說網際網路常見的服務線程模型有兩種:
- IO線程與工作線程通過任務隊列解耦
- 純異步
第一種,IO線程與工作線程通過隊列解耦類模型。

如上圖,大部分Web-Server與服務架構都是使用這樣的一種“IO線程與Worker線程通過隊列解耦”類線程模型:
- 有少數幾個IO線程監聽上遊發過來的請求,并進行收發包(生産者)
- 有一個或者多個任務隊列,作為IO線程與Worker線程異步解耦的資料傳輸通道(臨界資源)
- 有多個工作線程執行正真的任務(消費者)
這個線程模型應用很廣,符合大部分場景,這個線程模型的特點是,工作線程内部是同步阻塞執行任務的,是以可以通過增加Worker線程數來增加并發能力,今天要讨論的重點是“該模型Worker線程數設定為多少能達到最大的并發”。
第二種,純異步線程模型。
沒有阻塞,這種線程模型隻需要設定很少的線程數就能夠做到很高的吞吐量,該模型的缺點是:
- 如果使用單線程模式,難以利用多CPU多核的優勢
- 程式員更習慣寫同步代碼,callback的方式對代碼的可讀性有沖擊,對程式員的要求也更高
- 架構更複雜,往往需要server端收發元件,server端隊列,client端收發元件,client端隊列,上下文管理元件,有限狀态機元件,逾時管理元件的支援
however,這個模型不是今天讨論的重點。
第一類“IO線程與工作線程通過隊列解耦”類線程模型,工作線程的工作模式是怎麼樣的?
了解工作線程的工作模式,對量化分析線程數的設定非常有幫助:
上圖是一個典型的工作線程的處理過程,從開始處理start到結束處理end,該任務的處理共有7個步驟:
(1)從工作隊列裡拿出任務,進行一些本地初始化計算,例如http協定分析、參數解析、參數校驗等;
(2)通路cache拿一些資料;
(3)拿到cache裡的資料後,再進行一些本地計算,這些計算和業務邏輯相關;
(4)通過RPC調用下遊service再拿一些資料,或者讓下遊service去處理一些相關的任務;
(5)RPC調用結束後,再進行一些本地計算,怎麼計算和業務邏輯相關;
(6)通路DB進行一些資料操作;
(7)操作完資料庫之後做一些收尾工作,同樣這些收尾工作也是本地計算,和業務邏輯相關;
分析整個處理的時間軸,會發現:
- 其中1,3,5,7步驟中(上圖中粉色時間軸),線程進行本地業務邏輯計算時需要占用CPU
-
而2,4,6步驟中(上圖中橙色時間軸),通路cache、service、DB過程中線程處于一個等待結果的狀态,不需要占用CPU,進一步的分解,這個“等待結果”的時間共分為三部分:
2.1)請求在網絡上傳輸到下遊的cache、service、DB
2.2)下遊cache、service、DB進行任務處理
2.3)cache、service、DB将封包在網絡上傳回工作線程
如何量化分析,并合理設定工作線程數呢?
通過上面的分析,Worker線程在執行的過程中:
- 有一部計算時間需要占用CPU
- 另一部分等待時間不需要占用CPU
通過量化分析,例如打日志進行統計,可以統計出整個Worker線程執行過程中這兩部分時間的比例,例如:
- 執行計算,占用CPU的時間(粉色時間軸)是100ms
- 等待時間,不占用CPU的時間(橙色時間軸)也是100ms
得到的結果是,這個線程計算和等待的時間是1:1,即有50%的時間在計算(占用CPU),50%的時間在等待(不占用CPU):
- 假設此時是單核,則設定為2個工作線程就可以把CPU充分利用起來,讓CPU跑到100%
- 假設此時是N核,則設定為2N個工作現場就可以把CPU充分利用起來,讓CPU跑到N100%
當當當當!!!
結論來了:
N核伺服器,通過執行業務的單線程分析出本地計算時間為x,等待時間為y,則工作線程數(線程池線程數)設定為 N(x+y)/x,能讓CPU的使用率最大化。
一般來說,非CPU密集型的業務(加解密、壓縮解壓縮、搜尋排序等業務是CPU密集型的業務),瓶頸都在後端資料庫通路或者RPC調用,本地CPU計算的時間很少,是以設定幾十或者幾百個工作線程是能夠提升吞吐量的。
GET新技能了嗎?
本文轉自“架構師之路”公衆号,58沈劍提供。