最近有客戶對(duì)接口的qps要求特別高,所以我對(duì)接口做了很多壓力測試和代碼優(yōu)化。代碼優(yōu)化主要是在減少網(wǎng)絡(luò)請求,以及避免大量使用循環(huán),把有些邏輯拆到定時(shí)器,然后就是熱點(diǎn)數(shù)據(jù)緩存到redis以及jvm內(nèi)存中。
除了接口優(yōu)化,我想知道,當(dāng)接口不是瓶頸的時(shí)候,機(jī)器的性能和網(wǎng)絡(luò)帶寬對(duì)接口qps的影響。于是,我做了以下實(shí)驗(yàn)。
實(shí)驗(yàn)?zāi)康?/h4>
- 了解cpu和接口qps的關(guān)系
- 了解接口數(shù)據(jù)包大小和qps的關(guān)系
- 了解網(wǎng)絡(luò)帶寬和接口qps的關(guān)系
部署api的實(shí)驗(yàn)三臺(tái)機(jī)器配置分別為(阿里云內(nèi)網(wǎng)測試)
- 阿里云4核4g
- 阿里云8核16g
- 阿里云16核32g
測試工具
- wrk
部署wrk的機(jī)器配置
- 阿里云8核16g
wrk測試參數(shù)
wrk -t16 -c400 -d30s -s test-nginx.lua --latency http://172.26.68.135:9000/test/testApi
test-nginx.lua
wrk.method = "POST"
wrk.headers["Content-Type"] = "application/json"
wrk.body = "{ \"number\": 2}"
wrk -t16 -c400 -d30s -s test-nginx.lua --latency http://172.26.68.135:9000/test/testApi
wrk.method = "POST"
wrk.headers["Content-Type"] = "application/json"
wrk.body = "{ \"number\": 2}"
上面的參數(shù)是本地內(nèi)網(wǎng)環(huán)境下最優(yōu)參數(shù),測試過程中需要更改接口ip和端口。
測試代碼
Cache<String, List<TestNginxResp>> cache = Caffeine.newBuilder()
.expireAfterWrite(1000, TimeUnit.MINUTES)
.maximumSize(1000)
.build();
@PostMapping("/testApi")
public Response<List<TestNginxResp>> getTest(@Valid @RequestBody TestNginxReq req) {
List<TestNginxResp> cacheList = getCache(req);
if (cacheList != null) {
return Response.ok(cacheList);
}
List<TestNginxResp> list = new ArrayList<>();
for (int i = 0; i < req.getNumber(); i++) {
list.add(TestNginxResp.buildDefault());
}
cachePush(req, list);
return Response.ok(list);
}
private List<TestNginxResp> getCache(TestNginxReq req) {
String key = "test-api".concat(String.valueOf(req.getNumber()));
List<TestNginxResp> resps = cache.getIfPresent(key);
return resps;
}
public void cachePush(TestNginxReq req, List<TestNginxResp> klineRespList) {
String key = "test-api".concat(String.valueOf(req.getNumber()));
cache.put(key, klineRespList);
}
請求參數(shù)示例
{
"number":2
}
返回接口實(shí)例
{
"code" : 200 ,
"msg" : "ok" ,
"body" : [
{
"name" : "nginx" ,
"author" : "Igor Sysoev" ,
"version" : "1.18" ,
"desp":"高性能代理工具"
},
{
"name" : "nginx" ,
"author" : "Igor Sysoev" ,
"version" : "1.18" ,
"desp":"高性能代理工具"
}
]
}
接口說明
參數(shù)中number傳入數(shù)字幾,就返回幾個(gè)上面的bean。通過這個(gè)test-nginx.lua中的number控制返回?cái)?shù)據(jù)包的大小。接口第一次是通過循環(huán)創(chuàng)建list,然后放入Caffeine的本地緩存。下次同樣數(shù)量就直接從緩存拿。
影響qps的因素
- 1.網(wǎng)絡(luò)
- 2.cpu
- 3.內(nèi)存
- 4.磁盤
首先分析,我們上面的接口調(diào)用過程中,主要是通過從Caffeine的本地緩存中去拿取數(shù)據(jù),沒有mysql,redis,或者其他第三方調(diào)用,所以接口不可能成為瓶頸。我們的變量是number,也就是可以改變返回?cái)?shù)據(jù)包的大小。這里不存在磁盤的讀寫性能問題,全部走jvm內(nèi)存,jvm 2g堆內(nèi)存足夠了,所以機(jī)器內(nèi)存大小對(duì)本次實(shí)驗(yàn)沒影響。
綜上,磁盤性能和內(nèi)存的因素在本次測試中沒什么影響。本次主要是測試`cpu`和`網(wǎng)絡(luò)`對(duì)qps的影響。
實(shí)驗(yàn)結(jié)果
| 編號(hào) | 機(jī)器 | number | 數(shù)據(jù)包 | qps | 傳輸速度 | cpu使用程度 |
|---|---|---|---|---|---|---|
| 1 | 4核 | 10 | 2.03kb | 23191.98/s | 30.01MB/s | 4核全打滿 |
| 2 | 4核 | 391 | 77.57kb | 4301.49/s | 187.03MB/s | 4核全打滿 |
| 3 | 4核 | 600 | 119.00kb | 2812.82/s | 187.59MB/s | 4核全打滿 |
| 4 | 8核 | 10 | 2.03kb | 42986.40/s | 55.63MB/s | 8核全打滿 |
| 5 | 8核 | 391 | 77.57kb | 6870.61/s | 300.52MB | 8核未打滿 |
| 6 | 8核 | 600 | 119.00kb | 4497.81/s | 300.62MB | 8核未打滿 |
| 7 | 16核 | 10 | 2.03kb | 76086.64 | 98.47MB/s | 16核全打滿 |
| 8 | 16核 | 391 | 77.57kb | 6901.07/s | 300.44MBs | 16核未打滿 |
| 9 | 16核 | 600 | 119.00kb | 4517.75 | 300.90MB | 16核未打滿 |
備注:上面數(shù)據(jù)包大小是指:一次返回?cái)?shù)據(jù)包大小(只包括接口數(shù)據(jù),不包括tcp請求頭之類)。傳輸速度的峰值主要和網(wǎng)絡(luò)帶寬相關(guān)。
實(shí)驗(yàn)結(jié)果分析
-
對(duì)比5,6或者8,9號(hào)實(shí)驗(yàn)
現(xiàn)象為發(fā)現(xiàn)都是cup未打滿,但是傳輸速度300M/s左右。- 結(jié)論1:推斷這里cpu不是瓶頸,網(wǎng)絡(luò)帶寬是瓶頸。極限值300M/s,提工單問了阿里云,我們的機(jī)器內(nèi)網(wǎng)帶寬為2.5Gbps,的確帶寬極限下載速度為300M/s。
- 結(jié)論2:在每次請求數(shù)據(jù)量變大后,帶寬打滿了,qps下降了,說明包越大,接口越慢。(這很容易理解,馬路就那么寬,車變多了,車速就慢了)
-
對(duì)比1,4,7號(hào)實(shí)驗(yàn)。
現(xiàn)象為數(shù)據(jù)量為10條,包很小,全部都是cpu打滿,然后傳輸速度依次遞增,最大為98.47MB/s,沒有達(dá)到極限值300M/s,qps隨cpu核心數(shù)遞增。
- 結(jié)論1:這里帶寬不是瓶頸,cpu是瓶頸,且,cpu數(shù)量越多,qps越高,qps基本是和cpu核心數(shù)成正比。
- 結(jié)論2:如果繼續(xù)購買32核cpu,傳輸速度沒有達(dá)到300M/s的情況下,qps會(huì)更高。
-
對(duì)比5,8號(hào)或者6,9實(shí)驗(yàn)
現(xiàn)象:5,8號(hào)都是391條,數(shù)據(jù)包很大。傳輸速度300M/s,cpu都沒打滿,cpu雖然加了,但是qps沒變。(6,9一樣)
- 結(jié)論1:這里cpu不是瓶頸,帶寬是瓶頸,所以當(dāng)cpu增加的時(shí)候,qps并沒有對(duì)應(yīng)增加。
- 結(jié)論2:當(dāng)帶寬打滿了,繼續(xù)增加cpu,qps基本不會(huì)有變化。
總結(jié)
- 當(dāng)帶寬沒打滿,增加cpu能夠增加qps。
- 當(dāng)帶寬打滿了,增加cpu不能夠增加qps
- 當(dāng)帶寬都打滿了,cpu不變,每次包越大qps越低
- 當(dāng)一直增加cpu,但是一直打不滿帶寬的時(shí)候,就能說明瓶頸在接口,需要查看是否是mysql,redis或者程序中的哪些邏輯成為瓶頸。
最后上一張wrk測試接口圖,以及工單和阿里云溝通的截圖


小tip
- 極限壓力測試需要一個(gè)相對(duì)干凈的機(jī)器,避免機(jī)器上面應(yīng)用對(duì)接口性能的影響。為了測試機(jī)器性能對(duì)接口影響,甚至需要需要各種不同參數(shù)的機(jī)器,比如4核8g,8核8g,16核8g。這個(gè)時(shí)候可以在阿里云上開一臺(tái)按量付費(fèi)的機(jī)器,上面很干凈,然后做完測試就關(guān)閉,甚至釋放掉,一個(gè)小時(shí)也就一兩塊錢,很便宜,幾乎不耗資源。