公司分配給我一個活,讓我給Kong網關做一個獲取設定的站點,Kong網關號稱幾萬的QPS的神器,我有點慌,如果因為我的站點拖累了Kong我就是千古罪人,
配合Kong的站點必須要經過性能測驗,在性能測驗的時候就發現個很有意思的現象,如果我用25條執行緒壓我的站點,那么結果是這樣的,

如果我用50條執行緒去壓站點,結果是這樣的

現象就是,我提高了并發數量,我的QPS其實并沒有什么變化,但是我的單次平均回應時間缺提高了一倍,其實這種現象還是比較好解釋的,首先,我們來了解一下,IIS的大概處理邏輯,

其實IIS維護了這么幾個東西,首先一個是佇列,用來提高服務器的同時處理請求數用的,這么說吧,假設我現在程式很原始很簡陋,我一次只能處理一條請求,那么,我在處理一條請求的程序中,第二條請求過來了,那么這個時候我顯然不應該告訴他,我現在正忙,沒空搭理他,而應該是告訴他,你先等會,我馬上來處理你,讓他等會,其實就是相當于把他放到佇列里邊,一會再來處理,
另外一個概念叫做,同時處理數,剛才我的假設是我一次只能處理一條資料,但是我存在多個核,就算是一核在一個時間點上只能處理一條資料,那么,現在我機器是4核的,那么我最起碼也應該能處理4條資料,假設現在一次性來了4條資料,那么這4條資料基本上可以認為是同時在處理的,但是如果同時來了8條資料,那么就是4條在處理,4條在等待,
現在來解釋一下,為什么會出現50并發比25并發,提升了等待時間,但是QPS并沒有提高,我想可以這么解釋,其實QPS在25并發的時候已經接近于極限了,這個極限應該怎么算呢,大概就應該是1秒 * 同時處理數 / 每個請求的真實處理時間,可以看出來這個極限其實跟客戶端的并發數沒有什么直接聯系,那么50并發的時候,為什么等待的時間反而變長了呢?那是因為,客戶端并發數大于服務器同時處理數的時候,有一部分固定數量的請求在請求佇列里,他必須等待已經進入處理邏輯的部分處理完,然后再處理自己,所以就造成了QPS并沒有提升但是回應時間變長的現象發生,
因為是這樣的多倍疊加的模式,所以,有時候,你會發現,你的介面,如果只是幾毫秒回應的話,大家都很快,但是一旦你慢下來,回應時間是成指數級的增長,原因也很簡單,主要有以下幾個,
- 等待的佇列邊長了(因為前邊處理的很慢,所以等的人越來越多)
- 等待的單次邊長了(但是變長的人不止是你自己呀,還有你等待的其他人)
這幾個情況一綜合那可不是乘法運算嘛,那可不就是指數級增加嘛,
提問,那么究竟多少并發,才是最理想的狀態呢?
之前考慮這個問題的時候,可能理所當然的認為,這個東西嘛,應該是跟CPU核數有關系,應該跟核數一樣多就是最優解了吧,但是現實經常啪啪打臉,經過實測,一般是要比CPU核數多不少才是CPU不累,處理效率很高的狀態,那么為什么會出現這種情況呢?我覺得這個問題有點大,我們需要拆開來看,
1、一個核心真的是一條執行緒執行的最快嗎?
這個問題嘛,其實也對,也不對,說他對是因為,其實如果存在多條執行緒,那么多條執行緒之間切換的時候,其實也挺消耗資源的,但是多執行緒的為什么需要多執行緒呢?我覺得這個問題也可以拆成兩個問題,在拆問題之前先給介紹兩個概念,計算密集型、IO密集型,計算密集型就是你在做運算,加減乘除也好,比對也要,加密解密也好,這種主要依賴于CPU叫計算密集型執行緒,如果你的執行緒大部分時間都消耗在了讀取網路資料,讀取本地資料,或者驅動硬體等待回傳這種情況叫做IO密集型,
1.1 一個核心真的是一個計算密集型最快嗎?
是的,因為執行緒切換本身也是需要消耗資源的,頻繁的切換其實對于計算密集型執行緒沒有任何好處,因為計算量并沒有變少反而變多了,
1.2 一個核心真的是一個IO密集型執行緒最快嗎?
不對,多個IO密集型執行緒肯定比一個IO密集型執行緒要快,因為大部分時間,其實跟CPU沒有關系,CPU大部分時間都是在等而已,所以讓CPU一次性處理多個,反而更加占有優勢,
2、為什么不是并發量跟同時處理數相等時是最優解,
真實的業務場景,一條執行緒并不是純粹的IO或者計算,更多的時候是處于兩者都有的情況,那么對于這種執行緒的話,反正不是一核一個最快,因為它畢竟是存在IO的情況,他們肯定要多處理幾個才劃算,
這樣服務器等待客戶端請求的時間就太長了,如果并發數量跟處理數量相等的話,那么對于一個并發來說,就相當于客戶端發起請求、發送網路資料、服務器處理、發送網路資料、客戶端接收網路資料,然后進行下一輪處理,這樣的話就相當于客戶端與服務器端處于同一個執行緒,單執行緒作業,并且中間存在了大量的等待的時間,所以服務器的QPS并不會上來,
最理想的狀態應該是,以下的狀態
- 等待佇列中始終存在資料(不會讓處理執行緒等待客戶端請求)
- 客戶端的請求進入等待佇列后立馬被處理(不會因為別的請求而造成回應時間過長,而引發下一步的等待佇列過長)
根據上邊總結的多執行緒的相關結論,一般一個核心肯定要處理多個執行緒,并且等待佇列中存在并且存在不了多少資料,
那么最佳并發的結論應該是,核心數 * N(單核心同時處理執行緒數) + M(等待佇列中存在的少數請求),
題外話:為什么Golang號稱利用協成能夠更好的利用CPU 達到更高的運算效率呢?
我猜應該是將IO型執行緒中的多執行緒切換部分性能節省下來,用作于更多的CPU計算來提高了整體性能,
轉載請註明出處,本文鏈接:https://www.uj5u.com/net/40631.html
標籤:C#
