 壓力測(cè)試工具:從入門到實(shí)戰(zhàn)性能調(diào)優(yōu))
1. 項(xiàng)目概述為什么我們需要ab命令在網(wǎng)站開發(fā)和運(yùn)維的日常工作中性能始終是懸在頭頂?shù)倪_(dá)摩克利斯之劍。一個(gè)新功能上線一個(gè)促銷活動(dòng)開啟最怕的就是服務(wù)器在流量洪峰面前“躺平”。作為一線工程師我們手里得有趁手的“壓力測(cè)試”工具來(lái)模擬真實(shí)用戶訪問提前發(fā)現(xiàn)瓶頸。ApacheBench也就是我們常說(shuō)的ab命令就是這樣一個(gè)簡(jiǎn)單、直接、高效的瑞士軍刀。它沒有復(fù)雜的圖形界面不依賴龐大的測(cè)試平臺(tái)僅僅通過一行命令就能告訴你服務(wù)器在并發(fā)請(qǐng)求下的表現(xiàn)每秒能處理多少請(qǐng)求QPS平均響應(yīng)時(shí)間是多少有多少請(qǐng)求失敗了。這種“開箱即用”的特性讓它成為L(zhǎng)inux環(huán)境下進(jìn)行快速、初步性能評(píng)估的首選工具。無(wú)論是開發(fā)者在本地驗(yàn)證接口性能還是運(yùn)維同學(xué)在生產(chǎn)環(huán)境變更前做一輪基準(zhǔn)測(cè)試ab都能在幾分鐘內(nèi)給出關(guān)鍵數(shù)據(jù)。今天我們就來(lái)徹底拆解這個(gè)工具從安裝到實(shí)戰(zhàn)從參數(shù)解讀到結(jié)果分析讓你不僅能“跑”起來(lái)更能“看懂”和“用好”它。2. ab命令核心原理與安裝部署2.1 ab命令的工作原理淺析ab本質(zhì)上是一個(gè)用于對(duì)HTTP/HTTPS服務(wù)器進(jìn)行基準(zhǔn)測(cè)試的命令行工具。它的工作模式非常純粹模擬多個(gè)并發(fā)用戶向指定的URL發(fā)送大量HTTP請(qǐng)求并統(tǒng)計(jì)服務(wù)器處理這些請(qǐng)求所花費(fèi)的時(shí)間。其核心工作流程可以概括為單線程、多連接。ab本身是一個(gè)單進(jìn)程程序但它會(huì)創(chuàng)建多個(gè)并發(fā)的“客戶端”通過操作系統(tǒng)套接字實(shí)現(xiàn)這些客戶端同時(shí)向服務(wù)器發(fā)起請(qǐng)求。它主要測(cè)量的是服務(wù)器的網(wǎng)絡(luò)處理能力和請(qǐng)求處理吞吐量而不是客戶端的負(fù)載生成能力。這意味著ab運(yùn)行所在的測(cè)試機(jī)性能不能太差否則可能成為瓶頸無(wú)法給服務(wù)器施加足夠的壓力。一個(gè)常見的誤解是認(rèn)為ab會(huì)模擬復(fù)雜的用戶行為比如點(diǎn)擊鏈接、填寫表單。實(shí)際上它只做一件事重復(fù)請(qǐng)求同一個(gè)URL。這對(duì)于測(cè)試API接口、靜態(tài)頁(yè)面或簡(jiǎn)單的動(dòng)態(tài)頁(yè)面如首頁(yè)的極限性能非常有效。如果你想測(cè)試包含登錄狀態(tài)Session、復(fù)雜業(yè)務(wù)流程的場(chǎng)景ab就顯得力不從心了這時(shí)需要考慮 JMeter、Locust 等更高級(jí)的工具。但對(duì)于快速回答“這個(gè)接口在100個(gè)并發(fā)下響應(yīng)時(shí)間是多少”這類問題ab的效率無(wú)與倫比。2.2 在不同Linux發(fā)行版上安裝abab工具通常作為 Apache HTTP Server 工具包的一部分提供包名通常是apache2-utils(Debian/Ubuntu) 或httpd-tools(RHEL/CentOS/Fedora)。安裝非常簡(jiǎn)單。在 Debian/Ubuntu 及其衍生系統(tǒng)上sudo apt update sudo apt install apache2-utils -y安裝完成后可以通過ab -V來(lái)查看版本信息確認(rèn)安裝成功。在 RHEL/CentOS/Fedora 及其衍生系統(tǒng)上# 對(duì)于 CentOS 7/RHEL 7 sudo yum install httpd-tools -y # 對(duì)于 CentOS 8/RHEL 8/Fedora sudo dnf install httpd-tools -y在 macOS 上如果你使用 Homebrew可以很方便地安裝brew install apache-httpd # 安裝后ab命令通常位于 /usr/local/bin/ab注意在某些極簡(jiǎn)的Docker鏡像或云服務(wù)器模板中可能沒有預(yù)裝。按照上述命令安裝即可。另外請(qǐng)確保測(cè)試機(jī)本身有足夠的可用端口和網(wǎng)絡(luò)帶寬避免因本地資源限制影響測(cè)試結(jié)果。安裝完成后一個(gè)簡(jiǎn)單的ab -n 10 -c 2 http://localhost/命令就可以開始你的第一次測(cè)試了。其中-n 10表示總請(qǐng)求數(shù)為10-c 2表示并發(fā)數(shù)為2。3. 命令參數(shù)深度解析與實(shí)戰(zhàn)場(chǎng)景ab的命令行參數(shù)是其強(qiáng)大功能的體現(xiàn)。理解每個(gè)參數(shù)的含義是設(shè)計(jì)有效壓力測(cè)試場(chǎng)景的關(guān)鍵。下面我們分類詳解最常用和最重要的參數(shù)。3.1 基礎(chǔ)負(fù)載參數(shù)定義測(cè)試的“量”與“形”這是構(gòu)建測(cè)試場(chǎng)景的骨架決定了壓力的大小和模式。-n requests總請(qǐng)求數(shù)。這是測(cè)試的停止條件。例如-n 1000表示總共發(fā)送1000個(gè)請(qǐng)求后結(jié)束測(cè)試。設(shè)置一個(gè)足夠大的數(shù)可以讓測(cè)試持續(xù)一段時(shí)間得到更穩(wěn)定的統(tǒng)計(jì)結(jié)果。對(duì)于快速驗(yàn)證可以設(shè)小一點(diǎn)如100對(duì)于正式基準(zhǔn)測(cè)試建議至少5000以上。-c concurrency并發(fā)用戶數(shù)。這是模擬的同時(shí)向服務(wù)器發(fā)起請(qǐng)求的客戶端數(shù)量。這是壓力測(cè)試的核心參數(shù)直接決定了服務(wù)器的并發(fā)負(fù)載。-c 10表示模擬10個(gè)用戶同時(shí)操作。這個(gè)值需要根據(jù)你服務(wù)器的預(yù)估并發(fā)量來(lái)設(shè)置可以從一個(gè)較小的值如10開始逐步增加觀察性能拐點(diǎn)。-t timelimit最大測(cè)試時(shí)間秒。當(dāng)測(cè)試時(shí)間達(dá)到這個(gè)限制無(wú)論是否完成-n指定的請(qǐng)求數(shù)測(cè)試都會(huì)停止。例如-t 30表示測(cè)試最多運(yùn)行30秒。這個(gè)參數(shù)在你想進(jìn)行“持續(xù)一段時(shí)間”的壓力測(cè)試時(shí)非常有用比如想觀察服務(wù)器在長(zhǎng)時(shí)間壓力下的穩(wěn)定性可以和-n參數(shù)二選一使用。實(shí)戰(zhàn)場(chǎng)景選擇容量規(guī)劃測(cè)試使用-n和-c。例如-n 50000 -c 100發(fā)送5萬(wàn)請(qǐng)求并發(fā)100看總耗時(shí)和QPS。穩(wěn)定性/耐力測(cè)試使用-t和-c。例如-t 600 -c 50用50并發(fā)持續(xù)壓測(cè)10分鐘觀察過程中響應(yīng)時(shí)間、錯(cuò)誤率是否有波動(dòng)。3.2 請(qǐng)求定制參數(shù)模擬更真實(shí)的請(qǐng)求默認(rèn)情況下ab發(fā)送的是簡(jiǎn)單的 GET 請(qǐng)求。但現(xiàn)實(shí)中的請(qǐng)求要復(fù)雜得多。-m method指定HTTP方法。默認(rèn)是GET你可以設(shè)置為-m POST、-m PUT等。-p postfile當(dāng)使用POST方法時(shí)包含POST數(shù)據(jù)的文件路徑。文件內(nèi)容通常是表單數(shù)據(jù)如useradminpasswd123或JSON字符串。這是測(cè)試API接口的關(guān)鍵參數(shù)。-T content-type設(shè)置POST/PUT數(shù)據(jù)時(shí)的Content-Type頭。例如發(fā)送JSON數(shù)據(jù)時(shí)必須設(shè)置為-T application/json。-H custom-header添加自定義的HTTP頭??梢灾貜?fù)使用此參數(shù)添加多個(gè)頭。這在測(cè)試需要認(rèn)證Authorization頭、特定客戶端標(biāo)識(shí)或處理CORS時(shí)必不可少。ab -n 100 -c 10 -H “Authorization: Bearer xxxxxxxx” -H “User-Agent: MyTestClient” http://api.example.com/v1/resource-C cookie-namevalue為請(qǐng)求附加Cookie??梢灾貜?fù)使用以添加多個(gè)Cookie。用于測(cè)試需要會(huì)話狀態(tài)的頁(yè)面。-k啟用HTTP KeepAlive持久連接。這會(huì)讓ab復(fù)用TCP連接來(lái)發(fā)送多個(gè)請(qǐng)求而不是為每個(gè)請(qǐng)求新建連接。這能大幅減少TCP握手和慢啟動(dòng)的開銷更貼近現(xiàn)代瀏覽器或客戶端的真實(shí)行為測(cè)出的QPS通常會(huì)高很多。在進(jìn)行性能對(duì)比時(shí)務(wù)必統(tǒng)一此參數(shù)的設(shè)置。實(shí)戰(zhàn)示例測(cè)試一個(gè)登錄API假設(shè)我們有一個(gè)登錄接口http://api.example.com/login接受JSON格式的POST請(qǐng)求。創(chuàng)建一個(gè)文件post_data.json內(nèi)容如下{username: testuser, password: testpass}執(zhí)行命令ab -n 1000 -c 50 -p post_data.json -T ‘a(chǎn)pplication/json’ -H “Accept: application/json” http://api.example.com/login這個(gè)命令會(huì)以50的并發(fā)向登錄接口發(fā)送1000個(gè)包含JSON體的POST請(qǐng)求。3.3 輸出與調(diào)試參數(shù)獲取更詳細(xì)的信息這些參數(shù)幫助你更好地理解測(cè)試過程和結(jié)果。-v verbosity設(shè)置詳細(xì)級(jí)別。-v 2會(huì)打印出每個(gè)請(qǐng)求的響應(yīng)頭信息-v 3會(huì)打印響應(yīng)體-v 4會(huì)打印更多調(diào)試信息。在排查“為什么請(qǐng)求失敗了”時(shí)非常有用但輸出會(huì)非常冗長(zhǎng)不建議在正式壓測(cè)時(shí)使用高等級(jí)。-w將結(jié)果以HTML表格形式輸出??梢詫⑤敵鲋囟ㄏ虻轿募缓笤跒g覽器中打開查看格式更友好。ab -n 100 -c 10 -w http://localhost/ result.html-X proxy[:port]通過代理服務(wù)器發(fā)送請(qǐng)求。-V顯示版本號(hào)并退出。實(shí)操心得參數(shù)組合的常見陷阱-n和-t同時(shí)使用ab會(huì)以先達(dá)到的條件為準(zhǔn)停止測(cè)試。如果你設(shè)置了-n 10000 -t 10可能10秒內(nèi)只完成了2000個(gè)請(qǐng)求測(cè)試就停止了總請(qǐng)求數(shù)并非10000。POST數(shù)據(jù)文件格式確保-p指定的文件內(nèi)容格式與-T指定的Content-Type匹配。如果是application/x-www-form-urlencoded文件內(nèi)容應(yīng)是key1value1key2value2如果是application/json則應(yīng)是標(biāo)準(zhǔn)的JSON格式。Cookie和Header的覆蓋通過-H設(shè)置的Header會(huì)覆蓋ab內(nèi)部生成的一些默認(rèn)頭如User-Agent。如果你需要保留默認(rèn)頭并添加新的需要顯式地一起設(shè)置。4. 測(cè)試結(jié)果報(bào)告全方位解讀執(zhí)行完ab命令后屏幕上會(huì)輸出一份詳細(xì)的報(bào)告。這份報(bào)告是性能分析的依據(jù)每一個(gè)數(shù)據(jù)都有其特定含義。我們以一個(gè)典型的輸出為例分段解讀。假設(shè)我們執(zhí)行了ab -n 1000 -c 100 http://demo.example.com/得到如下結(jié)果數(shù)據(jù)為示例This is ApacheBench, Version 2.3 $Revision: 1879490 $ Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/ Licensed to The Apache Software Foundation, http://www.apache.org/ Benchmarking demo.example.com (be patient) Completed 100 requests Completed 200 requests Completed 300 requests Completed 400 requests Completed 500 requests Completed 600 requests Completed 700 requests Completed 800 requests Completed 900 requests Completed 1000 requests Finished 1000 requests Server Software: nginx/1.18.0 # 目標(biāo)服務(wù)器軟件 Server Hostname: demo.example.com Server Port: 80 Document Path: / Document Length: 15243 bytes # 單個(gè)響應(yīng)體的大小 Concurrency Level: 100 # 并發(fā)數(shù) Time taken for tests: 2.347 seconds # 整個(gè)測(cè)試持續(xù)的總時(shí)間 Complete requests: 1000 # 成功的請(qǐng)求數(shù) Failed requests: 15 # 失敗的請(qǐng)求數(shù) (Connect: 0, Receive: 0, Length: 15, Exceptions: 0) # 失敗分類 Non-2xx responses: 15 # 非2xx狀態(tài)碼的響應(yīng)數(shù) Total transferred: 15423000 bytes # 所有響應(yīng)數(shù)據(jù)的總大小包括頭信息 HTML transferred: 15243000 bytes # 所有響應(yīng)體中HTML內(nèi)容的總大小 Requests per second: 426.08 [#/sec] (mean) # 核心指標(biāo)每秒請(qǐng)求數(shù)QPS Time per request: 234.700 [ms] (mean) # 核心指標(biāo)每個(gè)請(qǐng)求的平均耗時(shí)用戶視角 Time per request: 2.347 [ms] (mean, across all concurrent requests) # 核心指標(biāo)服務(wù)器平均處理一個(gè)請(qǐng)求的耗時(shí) Transfer rate: 6417.00 [Kbytes/sec] received # 網(wǎng)絡(luò)吞吐量 Connection Times (ms) min mean[/-sd] median max Connect: 0 1 1.2 0 12 Processing: 20 233 45.6 225 412 Waiting: 18 230 45.1 222 410 Total: 20 234 45.7 226 412 Percentage of the requests served within a certain time (ms) 50% 226 # 中位數(shù)響應(yīng)時(shí)間 66% 245 75% 256 80% 263 90% 281 95% 302 98% 345 99% 367 100% 412 (longest request) # 最慢的請(qǐng)求耗時(shí)4.1 核心性能指標(biāo)解讀這是報(bào)告中最需要關(guān)注的部分直接反映了服務(wù)器的性能水平。Requests per second (QPS)每秒請(qǐng)求數(shù)這是衡量服務(wù)器吞吐量的黃金指標(biāo)。示例中426.08 [#/sec]表示服務(wù)器平均每秒能處理426個(gè)請(qǐng)求。這個(gè)值越高越好。在對(duì)比測(cè)試時(shí)例如優(yōu)化前后這是首要關(guān)注的指標(biāo)。Time per request (mean)這里有兩行容易混淆。第一行 (234.700 [ms] (mean))這是從單個(gè)用戶模擬客戶端視角看到的平均請(qǐng)求耗時(shí)。計(jì)算公式是總測(cè)試時(shí)間 * 并發(fā)數(shù) / 成功請(qǐng)求數(shù)。即2.347s * 100 / 1000 ≈ 0.2347s。它代表了用戶感受到的延遲。第二行 (2.347 [ms] (mean, across all concurrent requests))這是服務(wù)器平均處理每個(gè)請(qǐng)求所花費(fèi)的時(shí)間。計(jì)算公式是總測(cè)試時(shí)間 / 成功請(qǐng)求數(shù)。即2.347s / 1000 ≈ 0.002347s。這個(gè)值更接近服務(wù)器處理能力的理論值。這兩個(gè)值的關(guān)系是第一行 第二行 * 并發(fā)數(shù)。Failed requests / Non-2xx responses失敗請(qǐng)求數(shù)和非2xx響應(yīng)數(shù)。任何非零值都需要警惕。示例中失敗了15個(gè)且都是因?yàn)長(zhǎng)ength失敗響應(yīng)體長(zhǎng)度與第一個(gè)成功請(qǐng)求的長(zhǎng)度不一致或返回了非2xx狀態(tài)碼。這可能是服務(wù)器在高壓下出現(xiàn)了錯(cuò)誤如5xx或者響應(yīng)內(nèi)容不一致。必須結(jié)合日志排查原因。4.2 連接時(shí)間與百分比分布這部分?jǐn)?shù)據(jù)揭示了請(qǐng)求耗時(shí)的分布情況對(duì)于發(fā)現(xiàn)長(zhǎng)尾延遲至關(guān)重要。Connection Times分解了請(qǐng)求各階段的時(shí)間。Connect建立TCP連接的時(shí)間。如果這個(gè)值很大可能是網(wǎng)絡(luò)問題或服務(wù)器連接池滿了。Processing從發(fā)送完請(qǐng)求到接收完響應(yīng)的時(shí)間可以近似理解為服務(wù)器的處理時(shí)間。Waiting從發(fā)送完請(qǐng)求到接收到響應(yīng)第一個(gè)字節(jié)的時(shí)間TTFB - Time To First Byte。這個(gè)值很關(guān)鍵反映了服務(wù)器的即時(shí)響應(yīng)能力。Total整個(gè)請(qǐng)求的總耗時(shí)Connect Processing。mean[/-sd]表示平均值和標(biāo)準(zhǔn)差。標(biāo)準(zhǔn)差大說(shuō)明響應(yīng)時(shí)間波動(dòng)大性能不穩(wěn)定。Percentage of the requests served within a certain time響應(yīng)時(shí)間百分比分布。這是評(píng)估服務(wù)穩(wěn)定性和用戶體驗(yàn)的關(guān)鍵。50%中位數(shù)一半的請(qǐng)求在這個(gè)時(shí)間內(nèi)完成。示例是226ms。90%/95%/99%90%/95%/99%的請(qǐng)求在這個(gè)時(shí)間內(nèi)完成。我們尤其關(guān)注90%或95%線。示例中90%的請(qǐng)求在281ms內(nèi)完成但最慢的請(qǐng)求達(dá)到了412ms。如果99%線367ms比中位數(shù)226ms高很多說(shuō)明存在一些慢請(qǐng)求影響了部分用戶的體驗(yàn)需要優(yōu)化。在SLA服務(wù)等級(jí)協(xié)議中通常會(huì)承諾“95%的請(qǐng)求響應(yīng)時(shí)間低于X毫秒”。4.3 網(wǎng)絡(luò)與資源指標(biāo)Transfer rate網(wǎng)絡(luò)傳輸速率。示例6417.00 [Kbytes/sec]表示平均每秒從服務(wù)器接收了約6.4MB數(shù)據(jù)。這個(gè)值可以幫助判斷網(wǎng)絡(luò)帶寬是否成為瓶頸。如果這個(gè)值接近測(cè)試機(jī)或服務(wù)器的網(wǎng)絡(luò)帶寬上限那么性能瓶頸可能在網(wǎng)絡(luò)I/O上。Document Length / Total transferred反映了響應(yīng)數(shù)據(jù)的大小。過大的響應(yīng)體會(huì)消耗更多網(wǎng)絡(luò)帶寬和處理時(shí)間。分析心法如何從報(bào)告中發(fā)現(xiàn)問題看失敗率失敗率 0% 是最高優(yōu)先級(jí)問題。立刻檢查服務(wù)器日志看是超時(shí)、5xx錯(cuò)誤還是應(yīng)用層錯(cuò)誤??碤PS對(duì)比歷史基準(zhǔn)值或預(yù)期值。如果QPS過低說(shuō)明吞吐量不足??错憫?yīng)時(shí)間分布如果90%/95%線比50%線高很多例如3倍以上說(shuō)明服務(wù)有“長(zhǎng)尾”問題部分請(qǐng)求很慢??赡茉虬〝?shù)據(jù)庫(kù)慢查詢、緩存失效、外部依賴服務(wù)不穩(wěn)定、垃圾回收GC等??催B接時(shí)間如果Connect或Waiting時(shí)間異常高可能指向網(wǎng)絡(luò)問題、服務(wù)器負(fù)載過高導(dǎo)致無(wú)法快速接受連接或開始處理。結(jié)合監(jiān)控在壓測(cè)時(shí)同時(shí)使用top,vmstat,iostat等命令監(jiān)控服務(wù)器的CPU、內(nèi)存、磁盤I/O和網(wǎng)絡(luò)流量。如果QPS上不去但CPU已跑滿說(shuō)明是計(jì)算瓶頸如果CPU空閑但QPS低可能是I/O磁盤/網(wǎng)絡(luò)或外部服務(wù)瓶頸。5. 高級(jí)實(shí)戰(zhàn)技巧與腳本化壓測(cè)掌握了基礎(chǔ)用法和報(bào)告解讀我們可以進(jìn)行更貼近真實(shí)場(chǎng)景的復(fù)雜測(cè)試。5.1 模擬混合場(chǎng)景與參數(shù)化請(qǐng)求ab本身一次只能測(cè)試一個(gè)固定URL。但我們可以通過Shell腳本組合多個(gè)ab命令來(lái)模擬簡(jiǎn)單的混合場(chǎng)景。例如一個(gè)電商頁(yè)面70%的請(qǐng)求是瀏覽商品GET /product/{id}30%的請(qǐng)求是加入購(gòu)物車POST /cart。#!/bin/bash # simulate_mixed_traffic.sh BASE_URL“http://localhost:8080” CONCURRENCY50 TOTAL_REQUESTS1000 # 計(jì)算不同場(chǎng)景的請(qǐng)求數(shù) PRODUCT_REQS$((TOTAL_REQUESTS * 70 / 100)) CART_REQS$((TOTAL_REQUESTS * 30 / 100)) echo “ 開始?jí)簻y(cè)商品頁(yè) (70%) ” ab -n $PRODUCT_REQS -c $CONCURRENCY “${BASE_URL}/product/123” result_product.txt 21 echo “ 開始?jí)簻y(cè)購(gòu)物車接口 (30%) ” # 假設(shè)購(gòu)物車接口需要POST一個(gè)JSON body cat cart_data.json EOF {“productId”: 123, “quantity”: 1} EOF ab -n $CART_REQS -c $CONCURRENCY -p cart_data.json -T ‘a(chǎn)pplication/json’ -m POST “${BASE_URL}/cart” result_cart.txt 21 # 等待所有后臺(tái)任務(wù)完成 wait echo “ 壓測(cè)完成 ” echo “商品頁(yè)結(jié)果見 result_product.txt” echo “購(gòu)物車結(jié)果見 result_cart.txt”這個(gè)腳本同時(shí)啟動(dòng)了兩個(gè)ab進(jìn)程分別壓測(cè)不同的接口。需要注意的是這樣測(cè)試的是兩個(gè)獨(dú)立的流它們會(huì)競(jìng)爭(zhēng)測(cè)試機(jī)的網(wǎng)絡(luò)和端口資源并非嚴(yán)格的比例控制但可以作為一個(gè)近似的混合負(fù)載測(cè)試。對(duì)于URL路徑中的變量如/product/{id}ab無(wú)法直接參數(shù)化。一個(gè)變通的方法是準(zhǔn)備一個(gè)包含多個(gè)URL的文件然后寫一個(gè)循環(huán)但這樣每個(gè)ab進(jìn)程只測(cè)一個(gè)URL效率較低。對(duì)于復(fù)雜的參數(shù)化測(cè)試建議轉(zhuǎn)向 JMeter 或 Locust。5.2 持續(xù)壓力與梯度加壓測(cè)試我們經(jīng)常需要觀察系統(tǒng)在持續(xù)壓力下的表現(xiàn)或者逐步增加壓力找到性能拐點(diǎn)。持續(xù)壓力測(cè)試穩(wěn)定性測(cè)試# 持續(xù)壓測(cè)5分鐘并發(fā)100 ab -t 300 -c 100 -k http://localhost/api/health使用-t參數(shù)和-k啟用長(zhǎng)連接模擬穩(wěn)定持續(xù)的負(fù)載。梯度加壓測(cè)試尋找瓶頸點(diǎn)#!/bin/bash # step_load_test.sh URL“http://localhost:8080/api/data” DURATION60 # 每個(gè)梯度持續(xù)60秒 for concurrent in 10 30 50 80 100 150 200 do echo “” echo “正在測(cè)試并發(fā)數(shù): $concurrent” echo “” # 每個(gè)梯度壓測(cè)60秒輸出結(jié)果到獨(dú)立文件 ab -t $DURATION -c $concurrent -k “$URL” “result_${concurrent}.txt” 21 # 簡(jiǎn)單提取關(guān)鍵指標(biāo) grep “Requests per second:” “result_${concurrent}.txt” grep “Time per request:” “result_${concurrent}.txt” | head -1 grep “Failed requests:” “result_${concurrent}.txt” echo “” # 可選每個(gè)梯度之間休息10秒讓系統(tǒng)恢復(fù) sleep 10 done運(yùn)行這個(gè)腳本你會(huì)看到隨著并發(fā)數(shù)增加QPS和響應(yīng)時(shí)間的變化。當(dāng)并發(fā)數(shù)增加到某個(gè)點(diǎn)后QPS不再增長(zhǎng)甚至下降平均響應(yīng)時(shí)間急劇上升失敗率開始出現(xiàn)這個(gè)點(diǎn)就是系統(tǒng)當(dāng)前的一個(gè)性能拐點(diǎn)。5.3 結(jié)果數(shù)據(jù)的自動(dòng)化提取與分析手動(dòng)查看每個(gè)輸出文件效率低下。我們可以用grep,awk等命令快速提取關(guān)鍵指標(biāo)生成CSV格式便于導(dǎo)入Excel或數(shù)據(jù)分析工具進(jìn)行繪圖和對(duì)比。#!/bin/bash # extract_metrics.sh # 遍歷所有結(jié)果文件 for file in result_*.txt; do # 從文件名提取并發(fā)數(shù)例如 result_50.txt - 50 concurrency$(echo $file | grep -o -E ‘[0-9]’) # 使用awk提取關(guān)鍵指標(biāo) qps$(grep “Requests per second:” “$file” | awk ‘{print $4}’) # 提取用戶視角的平均時(shí)間第一行Time per request time_per_req$(grep “Time per request:” “$file” | head -1 | awk ‘{print $4}’) failed$(grep “Failed requests:” “$file” | awk ‘{print $3}’) # 提取90%響應(yīng)時(shí)間 p90$(grep “90%” “$file” | awk ‘{print $2}’) # 輸出CSV格式的一行 echo “${concurrency}, ${qps}, ${time_per_req}, ${failed}, ${p90}” done運(yùn)行./extract_metrics.sh metrics.csv你會(huì)得到一個(gè)包含并發(fā)數(shù)、QPS、平均響應(yīng)時(shí)間、失敗數(shù)、P90響應(yīng)時(shí)間的表格可以輕松地繪制出“并發(fā)數(shù)-QPS”和“并發(fā)數(shù)-響應(yīng)時(shí)間”曲線圖直觀地展示系統(tǒng)性能變化。6. 常見問題、性能瓶頸分析與排查指南在實(shí)際使用ab進(jìn)行壓測(cè)時(shí)你會(huì)遇到各種問題。下面是一些典型場(chǎng)景和排查思路。6.1 ab工具自身限制與誤區(qū)“Socket: Too many open files (24)” 錯(cuò)誤 這是Linux系統(tǒng)限制。ab每個(gè)并發(fā)連接都需要一個(gè)文件描述符。當(dāng)并發(fā)數(shù) (-c) 設(shè)置過高時(shí)可能超過用戶或系統(tǒng)的最大文件打開數(shù)限制。解決方案臨時(shí)提高限制ulimit -n 65535(僅對(duì)當(dāng)前Shell會(huì)話有效)。永久修改編輯/etc/security/limits.conf添加* soft nofile 65535和* hard nofile 65535重啟后生效。檢查系統(tǒng)全局限制cat /proc/sys/fs/file-max如果太小可以通過sysctl -w fs.file-max100000修改。測(cè)試機(jī)成為瓶頸 這是新手最容易忽略的問題。ab是單進(jìn)程的雖然能發(fā)起很多并發(fā)連接但其請(qǐng)求發(fā)送和接收統(tǒng)計(jì)都在一個(gè)進(jìn)程內(nèi)。如果測(cè)試的QPS非常高例如數(shù)萬(wàn)測(cè)試機(jī)本身的CPU可能被ab進(jìn)程占滿或者網(wǎng)絡(luò)帶寬被打滿導(dǎo)致無(wú)法對(duì)服務(wù)器施加足夠壓力測(cè)出的結(jié)果偏低。排查方法在運(yùn)行ab時(shí)另開一個(gè)終端在測(cè)試機(jī)上運(yùn)行top觀察ab進(jìn)程的CPU使用率。如果接近100%說(shuō)明測(cè)試機(jī)是瓶頸。運(yùn)行sar -n DEV 1查看網(wǎng)絡(luò)接口的吞吐量rxkB/s, txkB/s如果接近網(wǎng)卡帶寬上限也是瓶頸。解決方案使用性能更強(qiáng)的測(cè)試機(jī)或者采用分布式壓測(cè)用多臺(tái)機(jī)器同時(shí)跑ab。“apr_socket_recv: Connection reset by peer (104)” 錯(cuò)誤 服務(wù)器主動(dòng)重置了連接。這通常是因?yàn)榉?wù)器在高壓下崩潰、重啟或者達(dá)到了其連接數(shù)限制如Nginx的worker_connections主動(dòng)斷開了連接。排查方向立即檢查服務(wù)器日志如nginx error.log,dmesg查看是否有“out of memory”、“worker_connections are not enough”、“socket overflow”等錯(cuò)誤。6.2 服務(wù)器端性能瓶頸定位當(dāng)ab報(bào)告顯示QPS低、響應(yīng)時(shí)間長(zhǎng)或失敗率高時(shí)問題通常在服務(wù)器端。你需要登錄服務(wù)器進(jìn)行系統(tǒng)級(jí)和應(yīng)用級(jí)排查。系統(tǒng)級(jí)瓶頸排查使用命令行工具CPU瓶頸運(yùn)行top或htop。如果%us(用戶態(tài)CPU) 或%sy(系統(tǒng)態(tài)CPU) 長(zhǎng)期高于80%說(shuō)明CPU是瓶頸。進(jìn)一步用pidstat -u 1或top -Hp [pid]查看是哪個(gè)進(jìn)程或線程消耗CPU最多。內(nèi)存瓶頸運(yùn)行free -m或top。觀察available內(nèi)存是否充足。如果swap使用量 (Si,So) 在vmstat 1輸出中持續(xù)不為0說(shuō)明物理內(nèi)存不足發(fā)生了內(nèi)存交換性能會(huì)急劇下降。磁盤I/O瓶頸運(yùn)行iostat -x 1。關(guān)注%util(利用率)如果持續(xù)接近100%說(shuō)明磁盤I/O飽和。同時(shí)觀察await(平均等待時(shí)間)如果很高說(shuō)明磁盤響應(yīng)慢。網(wǎng)絡(luò)瓶頸運(yùn)行sar -n DEV 1。觀察網(wǎng)絡(luò)接口的吞吐量是否接近帶寬上限。也可以使用iftop查看實(shí)時(shí)流量。應(yīng)用級(jí)瓶頸排查Web服務(wù)器配置檢查Nginx/Apache的配置。關(guān)鍵參數(shù)包括worker_processes/StartServers工作進(jìn)程數(shù)建議設(shè)置為CPU核心數(shù)。worker_connections/MaxClients單個(gè)進(jìn)程的最大連接數(shù)。確保其值大于你的測(cè)試并發(fā)數(shù)。keepalive_timeout長(zhǎng)連接超時(shí)時(shí)間。適當(dāng)調(diào)大有助于提升性能。應(yīng)用服務(wù)器/框架查看應(yīng)用日志是否有大量錯(cuò)誤、超時(shí)或慢查詢?nèi)罩?。?duì)于Java應(yīng)用關(guān)注GC日志頻繁的Full GC會(huì)導(dǎo)致停頓。對(duì)于Python/Node.js等檢查是否有阻塞操作或內(nèi)存泄漏。數(shù)據(jù)庫(kù)/緩存這是最常見的瓶頸源。在壓測(cè)時(shí)監(jiān)控?cái)?shù)據(jù)庫(kù)的CPU、連接數(shù)、慢查詢。檢查緩存命中率是否過低。6.3 測(cè)試結(jié)果不穩(wěn)定的常見原因服務(wù)器有緩存第一次訪問和后續(xù)訪問性能差異巨大。解決方案在正式測(cè)試前先使用ab進(jìn)行幾輪預(yù)熱Warm-up讓服務(wù)器的緩存如數(shù)據(jù)庫(kù)查詢緩存、應(yīng)用層緩存熱起來(lái)然后再開始正式測(cè)試并記錄結(jié)果。外部依賴服務(wù)不穩(wěn)定如果你的應(yīng)用依賴其他API或服務(wù)它們的性能波動(dòng)會(huì)直接影響你的測(cè)試結(jié)果。盡量在隔離的環(huán)境如壓測(cè)專用數(shù)據(jù)庫(kù)、Mock外部服務(wù)下進(jìn)行測(cè)試。測(cè)試環(huán)境干擾測(cè)試環(huán)境與別人共享資源被搶占。盡量在獨(dú)立的、干凈的機(jī)器上進(jìn)行測(cè)試。JIT編譯影響針對(duì)Java/PHP等應(yīng)用服務(wù)器在啟動(dòng)初期JIT編譯器尚未優(yōu)化熱點(diǎn)代碼性能會(huì)較差。預(yù)熱一段時(shí)間后性能才會(huì)達(dá)到穩(wěn)定。這也是需要進(jìn)行預(yù)熱測(cè)試的原因之一。避坑技巧實(shí)錄一次真實(shí)的性能調(diào)優(yōu)案例我曾壓測(cè)一個(gè)返回JSON數(shù)據(jù)的API初始QPS只有120。ab報(bào)告顯示Time per request很高。服務(wù)器CPU使用率卻不高。首先我懷疑是網(wǎng)絡(luò)但sar顯示網(wǎng)絡(luò)流量很低。查看Nginx錯(cuò)誤日志發(fā)現(xiàn)大量*1024 worker_connections are not enough while connecting to upstream警告。原來(lái)是Nginx到后端應(yīng)用服務(wù)器的連接池滿了。調(diào)整Nginx配置增加了upstream塊中的keepalive連接數(shù)并增大了worker_connections。再次壓測(cè)QPS提升到300但CPU依然空閑。使用curl -w “\ntime_total: %{time_total}\n”手動(dòng)測(cè)試發(fā)現(xiàn)DNS解析時(shí)間占了大部分。原來(lái)API URL用的是域名每次請(qǐng)求Nginx都要解析。在Nginx的upstream中直接使用IP地址或者在Nginx的http塊中配置resolver并啟用緩存。最終壓測(cè)QPS穩(wěn)定在了850左右。關(guān)鍵教訓(xùn)性能瓶頸可能出現(xiàn)在任何環(huán)節(jié)客戶端、網(wǎng)絡(luò)、Web服務(wù)器、應(yīng)用服務(wù)器、數(shù)據(jù)庫(kù)。需要結(jié)合ab的報(bào)告和系統(tǒng)的全方位監(jiān)控像偵探一樣一層層排查。ab告訴你“病了”性能差而其他工具幫你找到“病因”瓶頸點(diǎn)。