jmeter壓測

阿里巴巴在開源壓測工具 JMeter 上的實踐和優化

原創: 韓勇 阿里巴巴中間件 

 

 

本文是 《如何做好性能壓測》系列專題分享的第三期,該專題將從性能壓測的設計、實現、執行、監控、問題定位和分析、應用場景等多個緯度對性能壓測的全過程進行拆解,以幫助大家構建完整的性能壓測的理論體系,並提供有例可依的實戰。

 

該系列專題分享由阿里巴巴 PTS 團隊出品,歡迎在文末處加入性能壓測交流羣,參與該系列的線上分享。

 

第一期:《壓測環境的設計和搭建》,點擊這裏

第二期:《性能壓測工具選型對比》,點擊這裏

 

第三期將分享阿里巴巴在 JMeter 上的實踐,並結合自研產品一一破解在 JMeter 上遇到的不足。

 

- 正文開始 -

🧯

 

Apache JMeter 是 Apache 旗下的開源壓測工具,創建於 1999 年初,迄今已有超過 20 年曆史。JMeter 功能豐富,社區(用戶羣體)龐大,是主流開源壓測工具之一。

 

 

性能測試通常集中在新系統上線或大型活動前(如電商大促、春節活動等), 以驗證系統能力,幫助排查定位性能瓶頸等問題。

 

一次壓測活動可粗略分爲幾個步驟:

 

1. 場景配置。配置壓測場景模擬用戶(業務)與系統的交互。

2. 壓測執行。按指定壓力量級啓動壓測。

3. 壓測監控分析。壓測中通常關注施壓 RPS,成功率,業務響應時間(RT),網絡帶寬等關鍵指標。

4. 報告總結。披露系統能力是否符合要求,同時沉澱記錄系統性能演變和優化過程。

 

 

下面我們討論如何使用 JMeter 完成上述步驟,及相關的最佳實踐建議。

 

JMeter 使用 Java 開發,需要先安裝 JDK 並配置好 PATH 環境變量,然後從官網下載 JMeter 二進制壓縮包解壓即可。建議將 JMeter bin 目錄也添加到 PATH 環境變量,這樣在命令行下輸入 jmeter 命令即可啓動 JMeter 。

 

 

JMeter 的場景配置


 

簡單 HTTP 請求配置

最常見的壓測場景即 HTTP 壓測。壓測場景在 JMeter 腳本中叫做 Test Plan(壓測計劃),打開 JMeter,默認即爲一個空 Test Plan 。JMeter 使用併發(線程)數控制壓力大小,一個線程可看做一個執行請求的虛擬用戶。在 Test Plan 上點擊右鍵,添加一個 Thread Group(線程組)。

 

線程組默認爲 1 個線程並只執行一次 1 次,這很方便測試執行腳本,保持此默認值即可。

 

JMeter 中發送請求的組件叫做 Sampler(採樣器)。在 Thread Group 上單擊右鍵,添加一個 HTTP Request 節點(採樣器)。

 

HTTP 請求最關鍵的配置即 URL, JMeter 允許將 URL 協議類型(Protocol)、服務器名、請求路徑(Path)等拆開單獨配置。也可以直接將整個 URL(如 JMeter 主頁 http://jmeter.apache.org/ )填寫到 Path,其他字段保留爲空即可。

 

這樣,一個最簡單的 HTTP 壓測腳本就配置好了。

 

爲了方便測試、調試腳本,可在 Test Plan 下添加一個 View Results Tree 監聽器(Listener)。這個監聽器僅用於編輯腳本時測試、調試腳本,查看請求執行詳情,不需要做任何配置。

 

 

測試執行腳本

第一次執行腳本前,需要先保存腳本,如保存爲 test.jmx 。以後每次執行腳本前,JMeter 默認會自動保存腳本。

 

連續多次執行腳本時,JMeter 默認不會清理歷史記錄。爲了避免歷史執行結果乾擾,可先點擊 Clear All 按鈕手動清空歷史記錄, 再點擊 Start 按鈕執行腳本,這樣看到的執行結果更清爽,方便排查問題。

 

按照默認線程組配置,腳本執行一次即結束。點擊 View Results Tree,可看到請求執行詳細信息,包括請求頭,請求體,響應頭和完整的響應體等信息。

 

場景編排

真實壓測場景通常不會只有一個請求,而是多個請求按一定順序和規則的編排組合,即場景編排。場景編排是 JMeter 等壓測引擎最重要的功能之一,也是與 Apache ab 等簡單壓測工具的重要區別之一。

 

這裏我們假設一個最簡單的場景,先訪問 JMeter 主頁,停留 1 秒鐘後跳轉到下載頁。

 

一個腳本訪問一個網站的不同頁面(Path)時,可添加一個 HTTP Request Defaults 節點,配置默認協議類型和服務器名。這樣可避免重複配置,需要修改協議類型(如 https 與 http 切換)或壓測域名時,只用修改 HTTP Request Defaults 即可。

 

HTTP Request Defaults 配置服務器名爲 jmeter.apache.org(協議類型默認爲 http), 鼠標可拖動 HTTP Request Defaults 節點移動到 HTTP 請求節點之前。

 

每個請求節點可設置一個具有業務含義的名字,方便理解和管理。訪問 JMeter 主頁的 HTTP 請求可改名爲 home ,同時 Path 修改爲 / 。再添加一個 HTTP 請求節點,命名爲 download page ,設置 Path 爲 /download_jmeter.cgi 即可。

 

模擬在 home 頁面停頓 1 秒鐘。home 節點上右鍵,添加一個 Constant Timer 子節點,設置延遲時間爲 1000 毫秒即可。

 

再次執行腳本,點擊 View Results Tree 可看到兩個 HTTP 請求節點的執行詳情。

 

注意:

Timer 節點作爲場景編排輔助節點,沒有請求執行動作,也沒有執行詳情顯示。

循環執行腳本時,最後一個節點 download page 執行結束後,會立即跳轉到腳本開頭,執行第一個節點 home 。 可在 download page 上也添加一個 Timer,模擬停留一秒之後再繼續後續請求。

 

 

JMeter 的壓測執行


 

編輯、調試腳本時,我們通常設置爲 1 個線程並且只執行 1 次。 執行壓力測試時,通常需要以較高的壓力持續執行一段時間。

 

腳本固定配置壓力

如計劃以 50 併發執行 2 分鐘,可修改腳本 Thread Group 配置如下。

 

配置說明:

1. 併發數(Number of Threads (users))設置爲 50 。

2. 循環次數(Loop Count)勾選永遠執行(Forever)。

3. 勾選 Scheduler,設置執行時長(Duration (seconds))爲 120 秒。

 

通常,我們在 JMeter 圖形界面(GUI)編輯腳本,但執行壓力測試時 GUI 佔用額外資源可能影響施壓性能, 而且施壓機可能沒有圖形界面環境(如 ssh 遠程登錄施壓機)。 因此腳本編輯完成後,通常以命令行模式執行 JMeter 壓力測試。

 

進入 JMeter 腳本目錄,執行 JMeter 壓力測試的命令爲:

jmeter -n -t <腳本>

 

如執行上述 test.jmx 腳本,命令如下:

jmeter -n -t test.jmx

 

輸出結果如下:


 

Creating summariser <summary>
Created the tree successfully using test.jmx
Starting the test @ Tue Jun 25 14:38:32 CST 2019 (1561444712414)
Waiting for possible Shutdown/StopTestNow/Heapdump message on port 4445
summary + 553 in 00:00:27 = 20.3/s Avg: 1378 Min: 252 Max: 8587 Err: 0 (0.00%) Active: 50 Started: 50 Finished: 0
summary + 882 in 00:00:30 = 29.4/s Avg: 685 Min: 222 Max: 4272 Err: 0 (0.00%) Active: 50 Started: 50 Finished: 0
summary = 1435 in 00:00:57 = 25.1/s Avg: 952 Min: 222 Max: 8587 Err: 0 (0.00%)
summary + 829 in 00:00:30 = 27.5/s Avg: 815 Min: 222 Max: 21310 Err: 0 (0.00%) Active: 50 Started: 50 Finished: 0
summary = 2264 in 00:01:27 = 25.9/s Avg: 902 Min: 222 Max: 21310 Err: 0 (0.00%)
summary + 881 in 00:00:30 = 29.5/s Avg: 700 Min: 221 Max: 3896 Err: 0 (0.00%) Active: 50 Started: 50 Finished: 0
summary = 3145 in 00:01:57 = 26.8/s Avg: 845 Min: 221 Max: 21310 Err: 0 (0.00%)
summary + 127 in 00:00:05 = 24.2/s Avg: 797 Min: 224 Max: 3819 Err: 0 (0.00%) Active: 0 Started: 50 Finished: 50
summary = 3272 in 00:02:02 = 26.7/s Avg: 843 Min: 221 Max: 21310 Err: 0 (0.00%)
Tidying up ... @ Tue Jun 25 14:40:35 CST 2019 (1561444835251)
... end of run

壓測過程中默認每 30 秒輸出一次統計數據,2 分鐘後(實際爲 00:02:02,比預設的 2 分鐘多出少許誤差)壓測結束。 看最後一行統計數據,平均 RPS(每秒請求數)爲 26.7,平均 RT (響應時間)爲 843 毫秒。

 

嘗試覈對一下統計數據。腳本包含兩個請求,每個請求附加 1 秒鐘等待時間,發送一個請求平均耗時爲 RT 843 毫秒 + 等待 1000 毫秒。 單線程理論 RPS 爲 1000.0 / (843 + 1000), 總共 50 個線程,全場景理論 RPS 爲 1000.0 / (843 + 1000) * 50 = 27.13,與統計值 26.7 有一定誤差。 這是因爲除了請求 RT 和等待時間,腳本執行請求之間還可能存在少量時間消耗。

 

命令行動態設置壓力

實際工作中,常常需要以不同的壓力大小反覆執行壓力測試,在腳本中寫死壓力大小(併發數)和執行時間顯然很不方便。

 

如何動態指定壓力大小呢,這裏有一個技巧。 JMeter 腳本支持使用 JMeter 屬性進行配置,JMeter 命令行支持使用 -J 參數動態指定 JMeter 屬性。 把這兩者結合起來,即可實現在命令行通過 -J 參數動態設置壓力大小。

 

修改 JMeter 腳本使用 JMeter 屬性配置壓力大小,配置如下:

 

 

 

配置說明:

1. 併發數配置爲 ${__P(load.concurrency,1)},循環次數取消勾選 Forever,配置爲 ${__P(load.count,1)}。 未設置對應的 JMeter 屬性時,默認爲 1 ,滿足只執行 1 次以測試、調試腳本的需求。

2. 執行時長配置爲 ${__P(load.duration,60)},默認 1 分鐘(60 秒)。

 

測試命令行直接執行腳本:

jmeter -n -t test.jmx

 

可看到統計輸出如下:

summary =      2 in 00:00:03 =    0.6/s Avg:   485 Min:   408 Max:   563 Err:     0 (0.00%)

 

默認一個併發並且只執行一次,發出 2 個請求(腳本循環一次發出兩個請求),約 3 秒後腳本停止。

 

注意:默認執行時間是 1 分鐘,同時配置了循環次數和執行時間時,有一個條件先滿足腳本即停止。

 

爲了按指定時長執行,需要將執行次數設置爲 Forever 。在 JMeter 內部實現中,執行次數爲 -1 即表示 Forever 。指定以 50 併發執行 2 分鐘,JMeter 命令行如下:

jmeter -n -t test.jmx -Jload.concurrency=50 -Jload.duration=120 -Jload.count=-1

 

 

執行結果與前述腳本固定配置 50 併發的結果類似。

 

 

雲上的 JMeter 實踐


 

阿里巴巴有着非常豐富的業務形態,每一種業務形態背後都由一系列分佈式的技術體系提供服務,隨着業務的快速發展,特別是在雙11等大促營銷等活動場景下,準確評估整個業務站點的服務能力成爲一大技術難題。

 

在這個過程中,我們打造了自己的全鏈路壓測系統,以應對更復雜、更多樣的壓測需求,並將此技術輸出到 性能測試PTS 上,同時支持原生 JMeter 壓測。

 

 

打開 控制檯 主頁, 左側導航欄選擇 創建壓測 > JMeter 壓測 , 新建 JMeter 壓測場景。填寫場景名,如 jmeter-test 。 場景配置 頁面點擊 上傳文件按鈕,上傳本地測試通過的 test.jmx 腳本。

施壓配置 頁面,併發數設置爲 50,壓測時長設置爲 2 分鐘。

點擊 保存去壓測,彈出提示框點擊 確認,即可開始在雲端引擎執行 JMeter 腳本發起壓力。

 

壓測中頁面如下:

 

壓測中實時展示場景(及每個請求頁面)的實時 RPS 和 RT 等信息。可看到場景併發數爲 50,RPS 爲 26,RT 爲 812 毫秒,與本地壓測的結果差不多。

 

注意:因爲機器配置和網絡環境的差異(施壓機默認爲 4核 8G,BGP多線路公網),雲上的壓測結果可能與本地壓測結果存在一定差異。

 

 

針對 JMeter 施壓配置,再補充幾點說明:

 

1. PTS 上的施壓配置會覆蓋原腳本中的配置。原腳本無論是寫死固定配置還是使用 JMeter 屬性配置都沒關係。

2. 循環次數表示每個線程循環執行腳本的次數,可能與用戶的直覺理解不一樣,如 總請求數 = 腳本執行一次的請求數 * 循環次數 * 併發數 。

3. 循環次數與預熱時間同時配置時可能導致意外行爲(如不能達到最大併發), 因此 PTS 上不允許同時配置預熱時間和循環次數(即只有預熱時間爲 0 時才允許設置循環次數)。

 

 

壓測監控分析


 

性能測試不僅僅是簡單的發起壓力,對壓力負載(RPS、網絡帶寬等)和業務表現(RT、成功率等)的監控和分析也是壓測活動的重要組成部分。

 

JMeter 腳本中每個請求節點(Sampler)可設置一個具有業務含義的名字(如 home 和 download page ),我們可稱之爲業務 API 。JMeter 監控統計按業務 API 名字彙總,如兩個名字相同的請求節點將彙總統計爲一個業務 API 。配置腳本時需注意,不同業務 API 節點應配置爲不同的名字。

 

業務 API 壓力負載和表現

實際工作中,不同業務 API 的統計數據可能存在巨大差異(如瀏覽商品 RT 通常比提交訂單快很多), 因此 PTS 默認將各個業務 API 獨立統計展示(如上述壓測中頁面展示的 home 和 download page)。

 

壓測中每個時間點的數據 PTS 都在後臺記錄了下來,最終將形成完整直觀的壓測報告。點擊業務 API 實時監控趨勢圖按鈕 , 即可查看對應的 RPS、成功率、響應時間、網絡帶寬等監控數據的變化趨勢圖。

 

業務 API 採樣日誌

很多時候我們還希望看到一個具體請求執行的詳細信息。如有 1% 的請求失敗,需要查看完整的請求、響應內容,以排查失敗原因等。JMeter 圖形界面下測試腳本時,可添加 View Results Tree 查看單個請求的詳細信息, 但執行壓力測試時,對每個請求都記錄詳細信息,不僅沒有必要,而且非常耗費資源,影響施壓性能。

 

PTS 採取了一個折中的辦法, 施壓引擎每 5 秒對每個業務 API(壓測Sampler)分別採樣記錄一條成功和失敗(如果有)的請求詳細信息。在壓測中或壓測報告頁面,點擊 查看採樣日誌 按鈕即可查詢記錄的請求採樣信息, 並支持按業務 API(壓測Sampler),響應狀態(是否成功),請求時間等進行搜索過濾。

 

點擊 查看詳情 即可看到單個請求的詳細信息。目前對詳細信息提供了通用和 HTTP 兩種展示模板, HTTP 展示模板可針對 HTTP 請求進行更友好的排版展示, 展示內容包括請求 URL,請求方法,返回碼,完整的請求頭、請求體,響應頭、響應體等。

 

因爲頁面上只展示文本內容,請求體或響應體包含圖片等無法識別爲文本的內容時,可能顯示爲亂碼。另外當請求體或響應體很大時,對應的內容可能被截斷。

 

JMeter 日誌

本地執行 JMeter 腳本時,默認將日誌記錄到 jmeter.log 文件。在 PTS 上執行 JMeter 腳本時,可通過 JMeter日誌 頁面實時查看 JMeter 日誌, 並支持根據日誌級別、時間或線程名進行查詢過濾。

 

JMeter 日誌主要用於腳本執行報錯時排查錯誤原因。一些插件可能通過 JMeter 日誌輸出一些重要信息,用戶在 groovy 腳本等代碼中也可以直接打印日誌。

 

日誌打印過於頻繁時,不僅可讀性極差(大量重複日誌淹沒重要信息),而且影響 JMeter 性能,對 PTS 採集存儲 JMeter 日誌造成額外開銷。因此 PTS 在採集 JMeter 日誌時默認進行了限流,每秒鐘打印日誌條數超過 10 條時部分日誌可能會丟失。良好設計的 JMeter 腳本應避免大量打印重複日誌。

 

同樣,良好設計的 JMeter 腳本應避免通過標準輸出(System.out)或標準錯誤(System.err)打印輸出信息, 需要輸出查看的重要信息應使用 JMeter 日誌輸出(如 groovy 腳本中使用 log.info("") )。PTS 上不支持查看 JMeter 腳本執行產生的標準輸出和標準錯誤內容。

 

報告總結

壓測結束後,PTS 將彙總監控數據形成壓測報告。用戶根據壓測報告分析評估系統性能是否符合要求,如 RPS、成功率和 RT(響應時間)是否符合期望,並可輔助用戶排查分析業務系統性能瓶頸。

 

壓測報告頁面可查詢歷史壓測報告列表。

 

點擊 查看報告 打開查看報告詳情。壓測報告在 PTS 上默認保存 30 天,可點擊 報告導出 按鈕,導出保存 PDF 版壓測報告到本地。壓測報告概要信息包括壓測執行時間、RPS、RT、成功率等概要數據。場景詳情包含全場景維度和業務 API 維度的監控統計信息。注意:受 JMeter 引擎本身統計功能的限制,僅全場景維度包含併發數統計。

 

此外,全場景維度和業務 API 維度均包含 RPS、成功率、網絡帶寬等統計。如 home 請求相關監控趨勢圖如下。

 

相比手動命令行執行 JMeter 腳本,PTS 更加 簡單易用 ,提供 簡單直觀的監控 ,並提供 海量施壓能力 。

 

免費 開通 PTS 服務, 購買 5000 VU 以上資源包,即可使用 JMeter 壓測。PTS 計費單位是 VUM ,即併發(VU)* 分鐘(最低消費 100 VUM)。上述場景爲 50 併發 * 2 分鐘 = 100 VUM 。如購買 5000 VU 資源包,包含 10 萬 VUM,售價 ¥278 , 上述計費可換算爲 (100 / 10萬) * ¥278 = ¥0.278

 

 

使用 CSV 參數文件


 

上述 JMeter 腳本僅簡單請求固定 URL ,真實業務 API 通常帶有請求參數。

 

JMeter 中可使用 CSV Data Set Config 讀取 CSV 數據文件,簡單實現請求參數化。CSV 文件默認首行爲變量名(列名),其餘行爲 CSV 數據。準備一個 user.csv 文件,包含 id , name 兩列,內容如下:

id,name
1,ali
2,pts
3,jmeter

注意:手工編輯 CSV 文件容易出錯,推薦使用 Excel、Numbers 等軟件導出,或編程使用 apache commons-csv 生成。

 

編輯 JMeter 腳本,右鍵單擊 Thread Group 添加一個 CSV Data Set Config 節點。

 

配置如下:

 

其中,有兩個地方需要注意(其他配置保持默認即可):

 

1. Filename 配置爲文件名 user.csv 即可,不要包含文件路徑。不同施壓機上 CSV 文件路徑可能不一樣,只使用文件名(並在當前路徑下執行腳本)以便兼容不同的施壓機環境。

2. Sharing mode 設置爲 Current thread group,指定 CSV 文件只被當前線程組使用。

 

假設 home 請求需要設置參數,配置請求參數直接使用 ${id} ,${name} 引用對應的變量即可。

 

測試執行腳本,查看 View Results Tree ,可看到執行請求時即會帶上請求參數。

 

在阿里雲 PTS 上執行腳本,只需編輯 JMeter 場景, 上傳修改後的 test.jmx 腳本文件和 user.csv 數據文件,點擊 保存去壓測 即可。

 

查看請求採樣,可看到請求 URL 已添加對應的參數。

 

 

使用 JMeter 插件和附加 Jar 包


 

JMeter 社區提供了豐富的插件,用戶還可以自由添加使用 Java 庫 Jar 包。在 PTS 上執行腳本時,只需要將額外添加的插件和 Jar 包一起上傳到 PTS 上, PTS 執行 JMeter 腳本時即可自動加載這些 Jar 包 。

 

如果遇到問題,請先確認本地已測試通過,並確認額外使用的 Jar 包已全部上傳到 PTS 。

 

 

海量壓測施壓能力


 

之前的腳本中,我們在請求後添加了 1 秒鐘的等待時間以演示業務場景編排。如果壓測場景前後請求沒有關聯,從服務器的角度看只是不停的收到各種請求,與客戶端是否等待無關, 因此 JMeter 腳本可去掉這些等待時間,以最大壓力向服務器發起極限壓測。

 

 

JMeter 使用併發(線程)數控制壓力大小,通常(併發較小時)線程數越多壓力越大。但單臺施壓機的能力畢竟有限,單機線程數增加到某個閾值時請求壓力即達到極限。顯然,爲了繼續增加壓力,必須使用多臺施壓機進行分佈式壓測。

 

分佈式壓測需要注意以下幾點:

 

1. 每臺施壓機需要安裝、啓動 JMeter,拷貝分發腳本、CSV 數據、附加 jar 包等文件。

2. 協調分配施壓併發數。通常施壓機配置相同,均分施壓併發數即可。

3. 統一控制啓動、停止壓測,統一收集聚合監控數據。

4. 希望 CSV 數據唯一或打散數據時,多臺施壓機需要拆分 CSV 數據文件 。

 

使用阿里雲 PTS 執行 JMeter 腳本時,用戶只需要設置期望的最大併發即可 , PTS 自動透明處理了分佈式壓測的問題,用戶不用關心是單機壓測還是分佈式壓測。PTS 默認每臺 JMeter 施壓機最大分配 500 併發,場景併發數超過 500 時即自動分配多臺施壓機。如果希望多臺施壓機拆分 CSV 數據文件,只要在上傳 CSV 文件後勾選 切分文件 即可。

 

詳情參考 切分 CSV 數據文件: 

https://help.aliyun.com/document_detail/103105.html#split

 

 

單機線程數極限與 JMeter 腳本的複雜程度和資源佔用(如 CPU,內存,網絡帶寬佔用等)情況有關。佔用內存過高的 JMeter 腳本在併發過高時甚至可能會因內存溢出而導致 JMeter 引擎異常退出。對有特殊需求的高級用戶,PTS 允許用戶手動指定施壓機數量,以更精確的控制總併發數和單機併發數。只需在 施壓配置 頁面勾選 指定IP數,設置相應的施壓機數量即可。每臺施壓機有一個IP地址,指定IP數即指定施壓機數量。

注意:指定IP數後,壓測執行時獨佔了指定數量的施壓機,每臺施壓機將按 500 併發計費(無論實際併發多少)。此功能專爲有特殊需求的高級用戶提供,普通用戶通常不需要使用。

 

 本文作者:

韓勇,阿里雲 PTS 技術專家。

 

本文縮略圖:icon by Joina

 

👪 性能壓測技術交流釘釘羣(推薦)

👦 若沒有釘釘,請添加中間件小姐姐微信,再拉入微信羣(回覆較慢,請耐心等待⌛️)

 

 

©每週一推

第一時間獲得下期分享

 

Tips:

# 點下“在看”❤️

# 然後,公衆號對話框內發送“肥皂”,試試手氣?😆

# 本期獎品是來自淘寶心選的手工保溼滋潤冷凝潔面古方皁

文章已於2019-07-04修改

微信掃一掃
關注該公衆號

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章