公司分配給我一個(gè)活,讓我給Kong網(wǎng)關(guān)做一個(gè)獲取設(shè)置的站點(diǎn)。Kong網(wǎng)關(guān)號(hào)稱幾萬的QPS的神器,我有點(diǎn)慌,如果因?yàn)槲业恼军c(diǎn)拖累了Kong我就是千古罪人。
配合Kong的站點(diǎn)必須要經(jīng)過性能測(cè)試,在性能測(cè)試的時(shí)候就發(fā)現(xiàn)個(gè)很有意思的現(xiàn)象,如果我用25條線程壓我的站點(diǎn),那么結(jié)果是這樣的。
?
?如果我用50條線程去壓站點(diǎn),結(jié)果是這樣的
?
?現(xiàn)象就是,我提高了并發(fā)數(shù)量,我的QPS其實(shí)并沒有什么變化,但是我的單次平均響應(yīng)時(shí)間缺提高了一倍。其實(shí)這種現(xiàn)象還是比較好解釋的。首先,我們來了解一下,IIS的大概處理邏輯。
?
其實(shí)IIS維護(hù)了這么幾個(gè)東西,首先一個(gè)是隊(duì)列,用來提高服務(wù)器的同時(shí)處理請(qǐng)求數(shù)用的。這么說吧,假設(shè)我現(xiàn)在程序很原始很簡(jiǎn)陋,我一次只能處理一條請(qǐng)求,那么,我在處理一條請(qǐng)求的過程中,第二條請(qǐng)求過來了,那么這個(gè)時(shí)候我顯然不應(yīng)該告訴他,我現(xiàn)在正忙,沒空搭理他,而應(yīng)該是告訴他,你先等會(huì),我馬上來處理你。讓他等會(huì),其實(shí)就是相當(dāng)于把他放到隊(duì)列里邊,一會(huì)再來處理。
另外一個(gè)概念叫做,同時(shí)處理數(shù)。剛才我的假設(shè)是我一次只能處理一條數(shù)據(jù)。但是我存在多個(gè)核,就算是一核在一個(gè)時(shí)間點(diǎn)上只能處理一條數(shù)據(jù),那么,現(xiàn)在我機(jī)器是4核的,那么我最起碼也應(yīng)該能處理4條數(shù)據(jù),假設(shè)現(xiàn)在一次性來了4條數(shù)據(jù),那么這4條數(shù)據(jù)基本上可以認(rèn)為是同時(shí)在處理的,但是如果同時(shí)來了8條數(shù)據(jù),那么就是4條在處理,4條在等待。
現(xiàn)在來解釋一下,為什么會(huì)出現(xiàn)50并發(fā)比25并發(fā),提升了等待時(shí)間,但是QPS并沒有提高。我想可以這么解釋,其實(shí)QPS在25并發(fā)的時(shí)候已經(jīng)接近于極限了,這個(gè)極限應(yīng)該怎么算呢,大概就應(yīng)該是1秒
* 同時(shí)處理數(shù) /
每個(gè)請(qǐng)求的真實(shí)處理時(shí)間??梢钥闯鰜磉@個(gè)極限其實(shí)跟客戶端的并發(fā)數(shù)沒有什么直接聯(lián)系。那么50并發(fā)的時(shí)候,為什么等待的時(shí)間反而變長(zhǎng)了呢?那是因?yàn)?,客戶端并發(fā)數(shù)大于服務(wù)器同時(shí)處理數(shù)的時(shí)候,有一部分固定數(shù)量的請(qǐng)求在請(qǐng)求隊(duì)列里,他必須等待已經(jīng)進(jìn)入處理邏輯的部分處理完,然后再處理自己,所以就造成了QPS并沒有提升但是響應(yīng)時(shí)間變長(zhǎng)的現(xiàn)象發(fā)生。
因?yàn)槭沁@樣的多倍疊加的模式,所以,有時(shí)候,你會(huì)發(fā)現(xiàn),你的接口,如果只是幾毫秒響應(yīng)的話,大家都很快。但是一旦你慢下來,響應(yīng)時(shí)間是成指數(shù)級(jí)的增長(zhǎng)。原因也很簡(jiǎn)單,主要有以下幾個(gè)。
* 等待的隊(duì)列邊長(zhǎng)了(因?yàn)榍斑吿幚淼暮苈?,所以等的人越來越多?
* 等待的單次邊長(zhǎng)了(但是變長(zhǎng)的人不止是你自己呀,還有你等待的其他人)
這幾個(gè)情況一綜合那可不是乘法運(yùn)算嘛,那可不就是指數(shù)級(jí)增加嘛。
?
提問,那么究竟多少并發(fā),才是最理想的狀態(tài)呢?
之前考慮這個(gè)問題的時(shí)候,可能理所當(dāng)然的認(rèn)為,這個(gè)東西嘛,應(yīng)該是跟CPU核數(shù)有關(guān)系,應(yīng)該跟核數(shù)一樣多就是最優(yōu)解了吧。但是現(xiàn)實(shí)經(jīng)常啪啪打臉。經(jīng)過實(shí)測(cè),一般是要比CPU核數(shù)多少不少才是CPU不累,處理效率很高的狀態(tài)。那么為什么會(huì)出現(xiàn)這種情況呢?我覺得這個(gè)問題有點(diǎn)大,我們需要拆開來看。
?
1、一個(gè)核心真的是一條線程執(zhí)行的最快嗎?
這個(gè)問題嘛,其實(shí)也對(duì),也不對(duì)。說他對(duì)視因?yàn)椋鋵?shí)如果存在多條線程,那么多條線程之間切換的時(shí)候,其實(shí)也挺消耗資源的。但是多線程的意義是什么呢?我覺得這個(gè)問題也可以拆成兩個(gè)問題。在拆問題之前先給介紹兩個(gè)概念。計(jì)算密集型、IO密集型,計(jì)算密集型就是你在做運(yùn)算,加減乘除也好,比對(duì)也要,加密解密也好,這種主要依賴于CPU叫計(jì)算密集型線程。如果你的線程大部分時(shí)間都消耗在了讀取網(wǎng)絡(luò)數(shù)據(jù),讀取本地?cái)?shù)據(jù),或者驅(qū)動(dòng)硬件等待返回這種情況叫做IO密集型。
1.1 一個(gè)核心真的是一個(gè)計(jì)算密集型最快嗎?
是的。因?yàn)榫€程本身也是需要消耗資源的,頻繁的切換其實(shí)對(duì)于計(jì)算密集型線程沒有任何好處,因?yàn)橛?jì)算量并沒有變少反而變多了。
1.2 一個(gè)核心真的是一個(gè)IO密集型線程最快嗎?
不對(duì)。多個(gè)IO密集型線程肯定比一個(gè)IO密集型線程要快,因?yàn)榇蟛糠謺r(shí)間,其實(shí)跟CPU沒有關(guān)系,CPU大部分時(shí)間都是在等而已。所以讓CPU一次性處理多個(gè),反而更加占有優(yōu)勢(shì)。
?
2、為什么不是并發(fā)量跟同時(shí)處理數(shù)相等時(shí)是最優(yōu)解。
真實(shí)的業(yè)務(wù)場(chǎng)景,一條線程并不是純粹的IO或者計(jì)算,更多的時(shí)候是處于兩者都有的情況。那么對(duì)于這種線程的話。反正不是一核一個(gè)最快,因?yàn)樗吘故谴嬖贗O的情況。他們肯定要多處理幾個(gè)才劃算。
這樣服務(wù)器等待客戶端請(qǐng)求的時(shí)間就太長(zhǎng)了,如果并發(fā)數(shù)量跟處理數(shù)量相等的話,那么對(duì)于一個(gè)并發(fā)來說,就相當(dāng)于客戶端發(fā)起請(qǐng)求、發(fā)送網(wǎng)絡(luò)數(shù)據(jù)、服務(wù)器處理、發(fā)送網(wǎng)絡(luò)數(shù)據(jù)、客戶端接收網(wǎng)絡(luò)數(shù)據(jù),然后進(jìn)行下一輪處理。這樣的話就相當(dāng)于客戶端與服務(wù)器端處于同一個(gè)線程,單線程工作,并且中間存在了大量的等待的時(shí)間,所以服務(wù)器的QPS并不會(huì)上來。
?
最理想的狀態(tài)應(yīng)該是,以下的狀態(tài)
* 等待隊(duì)列中始終存在數(shù)據(jù)(不會(huì)讓處理線程等待客戶端請(qǐng)求)
* 客戶端的請(qǐng)求進(jìn)入等待隊(duì)列后立馬被處理(不會(huì)因?yàn)閯e的請(qǐng)求而造成響應(yīng)時(shí)間過長(zhǎng),而引發(fā)下一步的等待隊(duì)列過長(zhǎng))
根據(jù)上邊總結(jié)的多線程的相關(guān)結(jié)論,一般一個(gè)核心肯定要處理多個(gè)線程,并且等待隊(duì)列中存在并且存在不了多少數(shù)據(jù)。
那么最佳并發(fā)的結(jié)論應(yīng)該是,核心數(shù) * N(單核心同時(shí)處理線程數(shù)) + M(等待隊(duì)列中存在的少數(shù)請(qǐng)求)。
?
題外話:為什么Golang號(hào)稱利用協(xié)成能夠更好的利用CPU 達(dá)到更高的運(yùn)算效率呢?
我猜應(yīng)該是將IO型線程中的多線程切換部分性能節(jié)省下來,用作于更多的CPU計(jì)算來提高了整體性能。
?
熱門工具 換一換