如何決定 Web 應用的執行緒池大小
阿新 • • 發佈:2019-01-24
在部署 web 應用到生產環境,或者在對 web 應用進行效能測試的時候,經常會有人問:如何決定 web 應用執行緒池大小?決定一個 IO 阻塞型 web 應用的執行緒池大小是一項很艱鉅的任務。通常是通過進行大量的效能測試來完成。在一個 web 應用中同時擁有多個執行緒池會讓決定最優執行緒池大小的過程變得更加複雜。本文將就這個常見的問題進行一些討論和建議。
請注意併發和並行不是一個概念。併發請求指的是正在處理中的請求數量,在某個時間點,只有其中的一小部分能夠得到 CPU 執行。而並行請求指的是正在處理的請求數量,在某個時間點,所有請求都在被 CPU 執行。
在非阻塞型 IO 應用中,比如 NodeJS,單個執行緒(程序)能夠同時處理多個請求。多核 CPU 處理器下,通過增加執行緒或程序數能夠處理並行請求。
在阻塞型 IO 應用中,比如 SpringMVC,單個執行緒只能同時處理一個請求。要同時處理多個併發請求的話,我們必須增加執行緒數量。
在計算密集型應用中,執行緒池的大小應該等同於主機中 CPU 的數量。再新增更多執行緒將會打斷請求的處理,因為執行緒的上下文切換也會延遲響應時間。
非阻塞型 IO 應用將會是 CPU 密集型的,因為在請求得到處理的時候沒有執行緒等待時間。
利特爾法則:在一個穩定的系統中,長時間觀察到的平均顧客數量 L,等於長時間觀察到的有效到達速率,λ,與平均每個顧客在系統中花費的時間之乘積:L = λW 。
適用於 web 應用的利特爾法則:一個系統中執行緒的平均數量(Threads),等於 web 請求的到達速率(WebRequests per sec),與平均每個處理的響應時間(ResponseTime)的乘積。
Threads = 執行緒的數量
WebRequests per sec = 一秒內能夠處理的 web 請求數
ResponseTime = 處理一次 web 請求所需要的時間
Threads = (WebRequests/sec) X ResponseTime
儘管上邊這個公式提供了處理進入請求的執行緒個數,它並沒有提供執行緒數和 CPU 核心數之間的比率資訊,比如一個 x 個 CPU 的主機需要分配多少個執行緒。
要找出合適的執行緒池大小,需要在吞吐量和響應時間之間進行權衡。先以一個最小值開始測試:一個 CPU 一個執行緒(也就是執行緒池大小 = CPU 個數),應用執行緒池大小與下游系統平均響應時間成正比增長,直到 CPU 使用率飽和或者響應時間開始退化為止。
下圖指出了請求數、CPU 以及響應時間等指標之間的關聯關係。
CPU Vs 請求數演示了在增加 web 應用負載時的 CPU 利用率。
響應時間 Vs 請求數圖演示了增加 web 應用負載對響應時間的影響。
綠點指出了最佳吞吐量和響應時間。
執行緒池大小 = CPU 個數
上圖描述的是 IO 等待型應用線上程數等於 CPU 數時的情況。應用的執行緒在等待下游系統響應時發生了阻塞。由於執行緒都阻塞住了,系統響應時間因請求進入等待佇列而被拉長。由於所有執行緒都處於阻塞狀態,應用開始拒絕請求,儘管 CPU 使用率還很低。
執行緒池很大
上圖描述的是 IO 等待型應用在 web 應用中建立了很多執行緒的情況。由於有很多數量的執行緒,執行緒的上下文切換將會很頻繁。由於不必要的執行緒上下文切換,儘管吞吐量還沒升上去的時候應用的 CPU 使用率就已經很高了。響應時間由於被請求的處理被執行緒的上下文切換所打斷而被拉長。
最佳執行緒池大小
上圖描述的是 IO 等待型應用在 web 應用中建立了合理數量的執行緒的情況。CPU 得到了有效利用,具備良好的吞吐量和較少的執行緒上下文切換。我們可以看到由於更少的打斷(上下文切換),請求處理更加有效,應用有一個良好的響應時間。
處理這種問題的兩個方案是:
原文連結:http://venkateshcm.com/2014/05/How-To-Determine-Web-Applications-Thread-Poll-Size/。
執行緒池
web 應用中的執行緒池大小決定了在指定時間內能夠處理的併發請求數。如果一個 web 應用接收到的請求數高於執行緒池大小,多出來的請求將進入佇列等待,或被拒絕。請注意併發和並行不是一個概念。併發請求指的是正在處理中的請求數量,在某個時間點,只有其中的一小部分能夠得到 CPU 執行。而並行請求指的是正在處理的請求數量,在某個時間點,所有請求都在被 CPU 執行。
在非阻塞型 IO 應用中,比如 NodeJS,單個執行緒(程序)能夠同時處理多個請求。多核 CPU 處理器下,通過增加執行緒或程序數能夠處理並行請求。
在阻塞型 IO 應用中,比如 SpringMVC,單個執行緒只能同時處理一個請求。要同時處理多個併發請求的話,我們必須增加執行緒數量。
計算密集型應用
非阻塞型 IO 應用將會是 CPU 密集型的,因為在請求得到處理的時候沒有執行緒等待時間。
IO 等待應用
決定 IO 等待應用的執行緒池大小會由於依賴於下游系統的響應時間而變得更加複雜,因為一個執行緒在其他系統響應之前始終是阻塞的。我們不得不像《應答者模式:I/O 阻塞型應用》中討論的那樣去增加執行緒的數量以提高 CPU 利用率。利特爾法則
利特爾法則應用於非技術領域,比如銀行,以估算處理進入銀行客戶所需要的銀行出納櫃檯的數量。利特爾法則:在一個穩定的系統中,長時間觀察到的平均顧客數量 L,等於長時間觀察到的有效到達速率,λ,與平均每個顧客在系統中花費的時間之乘積:L = λW
適用於 web 應用的利特爾法則:一個系統中執行緒的平均數量(Threads),等於 web 請求的到達速率(WebRequests per sec),與平均每個處理的響應時間(ResponseTime)的乘積。
Threads = 執行緒的數量
WebRequests per sec = 一秒內能夠處理的 web 請求數
ResponseTime = 處理一次 web 請求所需要的時間
Threads = (WebRequests/sec) X ResponseTime
儘管上邊這個公式提供了處理進入請求的執行緒個數,它並沒有提供執行緒數和 CPU 核心數之間的比率資訊,比如一個 x 個 CPU 的主機需要分配多少個執行緒。
測試決定執行緒池大小
下圖指出了請求數、CPU 以及響應時間等指標之間的關聯關係。
CPU Vs 請求數演示了在增加 web 應用負載時的 CPU 利用率。
響應時間 Vs 請求數圖演示了增加 web 應用負載對響應時間的影響。
綠點指出了最佳吞吐量和響應時間。
執行緒池大小 = CPU 個數
上圖描述的是 IO 等待型應用線上程數等於 CPU 數時的情況。應用的執行緒在等待下游系統響應時發生了阻塞。由於執行緒都阻塞住了,系統響應時間因請求進入等待佇列而被拉長。由於所有執行緒都處於阻塞狀態,應用開始拒絕請求,儘管 CPU 使用率還很低。
執行緒池很大
上圖描述的是 IO 等待型應用在 web 應用中建立了很多執行緒的情況。由於有很多數量的執行緒,執行緒的上下文切換將會很頻繁。由於不必要的執行緒上下文切換,儘管吞吐量還沒升上去的時候應用的 CPU 使用率就已經很高了。響應時間由於被請求的處理被執行緒的上下文切換所打斷而被拉長。
最佳執行緒池大小
上圖描述的是 IO 等待型應用在 web 應用中建立了合理數量的執行緒的情況。CPU 得到了有效利用,具備良好的吞吐量和較少的執行緒上下文切換。我們可以看到由於更少的打斷(上下文切換),請求處理更加有效,應用有一個良好的響應時間。
執行緒池隔離
對於大多數 web 應用而言,只有少數幾種型別的 web 請求會花費比較長的處理時間。這些慢的請求處理可能會拖累所有執行緒,並降低整個應用的效能。處理這種問題的兩個方案是:
- 為慢處理的 web 請求設定在一臺獨立的主機;
- 在同一個應用中為慢處理的 web 請求分配一個獨立的執行緒池;
原文連結:http://venkateshcm.com/2014/05/How-To-Determine-Web-Applications-Thread-Poll-Size/。