JMeter 是目前業界常用的負載測試工具,是一套 100% 使用 Java 開發的開源軟體,由 Apache 基金會維護。除了測試網站、WebAPI、Web Service,JMeter 還延伸到 FTP、資料庫(透過JDBC)、LDAP、SMTP、IMAP、TCP、Shell 指令測試,包山包海。

JMeter 的運作概念是以 Thread Group 為單位,同時開啟多條 Thread 對目標函式連續進行多次取樣(Sampling)並記錄每次執行時間,最後得到平均執行時間、每秒完成次數... 等統計值,用以衡量系統在不同負載壓力下的表現。JMeter 已發展多年,生態系統興盛,有大量第三方 Plugin 可用,也能跟 Marven、Gradle、Jenkins 等 DevOps 平台整合,是值得一學的工具,因此被我排入學習清單。

為了測試,我簡單拼湊了一個模擬樂透投注的 ASP.NET Core WebAPI 小網站,主要測試對象為下圖的 Registration/Register 方法,模擬投注站向中央系統登記顧客投注結果的動作:

登記資料包含投注站代號、售出時間、樂透選號、特別號等資料,為了防止偽造竄改,我還加了個 reqSign 欄位,打算用投注站的 RSA 私鑰對投注單內容加數位簽章。結果一下子把測試難度拉高,等於 HTTP 請求不能隨機胡亂產生,必須先做好一批投注站 RSA 金鑰,預先產生一堆請求並用對映的投注站 RSA 私鑰加上簽章。(有考慮過現場產生請求並簽章,但可能會讓發送端變成瓶頸)

不過這樣也好,這是實務上終究要面對的問題。想貼近實際狀況,有時就是得用預先準備好的資料,不能全靠現場隨機產生,早點學會這個技能也好~

於是我先做好一百組投注站 RSA 金鑰,為每組金鑰產生一百張投注單並加上數位簽章存成 JSON 檔,依投注站分資料夾存放,準備好一萬個 JSON 檔。

接下來說說怎麼建一個 JMeter 測試計劃(Test Plan),讀取這一萬個 JSON 檔執行壓力測試。

首先,要在另外一台機器安裝 JMeter,不宜直接在網站跑,以避免測試端跟網站爭搶 CPU/IO 資源干擾結果。
註:如果想在雲端測試,可將 JMeter 測試計劃丟到 Azure 負載測試服務去跑,調校上時動態調整 CPU 記憶體及 IO 等級,測量的範圍又更大了,這部分以後再介紹。

安裝軟體我現在多改在 CLI 下指令安裝(指令式軟體安裝服務比較:Chocolatey、Scoop 與 winget),JMeter 的話,用 Scoop 裝較方便,相關安裝說明可參考以下文章:

我這次的安裝步驟為先安裝 Scoop, 安裝 JMeter

Set-ExecutionPolicy RemoteSigned -scope CurrentUser
Invoke-Expression (New-Object System.Net.WebClient).DownloadString('https://get.scoop.sh')
# 如果主機沒裝 Git 的話,裝一下 Git
scoop install git
scoop bucket add extras
scoop install jmeter

JMeter 需要 Java 執行環境,可下載微軟 OpenJDK 或用 Chocolatey 安裝 choco install -y openjdk

網路上 JMeter 教學多如牛毛,這裡就不細說操作步驟了。以下是我最後做出來的測試計劃(.jmx 檔),後會再逐步說明重點:

這個測試計劃有兩個 Thread Group,最上面的 setUp Thread Group 會在測試開始前執行,如果有前置作業可放在這裡。我的案例每張投注單有唯一序號,故開始前要清空資料庫以免違反 UNIQUE INDEX 限制出錯。做法是 Thread Group 設定一條 Thread 做一次:

在 setUp Thread Group 加一個 HTTP Request,呼叫 /DemoData/ClearLetteryEntries,裡面有邏輯會清空投注資料表。

View Result Tree 可用來檢視執行結果,可加可不加,但用它可以查看 HTTP Request 及 Response 內容,偵錯方便許多:

再來的 Thread Group 是測試核心,由於我要用 JSON 檔當作 HTTP Request Body,設定稍稍複雜一些。首先準備一個 CSV 檔,並設定 CSV Data Set Config 指向它,這個 CSV 只有一欄,每筆對映到一個 JSON 檔路徑,在 Variable Names 這個欄位輸入 JSON_FILE,將 JSON 檔路徑存入名為 "JSO_FILE" 的變數,稍後會在 HTTP Request 用到:

由於不想寫死 JSON 資料夾路徑,我試了由 .jmx 檔相對路徑推算 JSON 檔所在路徑(後來覺得有點自找麻煩),參考網路資料:JMeter - FileToString with relative file path to test plan?),我加了一個 BeanShell PreProcessor 用一段 Java 程式碼推算路徑存成變數 ticketsLocation:

import org.apache.jmeter.gui.GuiPackage;
import org.apache.commons.io.FilenameUtils;
String testPlan = GuiPackage.getInstance().getTestPlanFile();
String testPlanLocation = FilenameUtils.getFullPathNoEndSeparator(testPlan);
vars.put("ticketsLocation", testPlanLocation.replace("JMeter", "Data\\Tickets\\"));

HTTP Request 設定有兩處較特別。第一是 Body Data 要用${__FileToString(${__eval(${ticketsLocation})}${JSON_FILE}.json,,)}指令讀取檔案,再來要設一個 HTTP Header Management 設定 Content-Type: application/json:

就醬,我成功做到讀取 JSON 檔當成 HTTP Request Body 呼叫 Web API。下圖為用一條 Thread 跑 100 次的 Summary Report

欄位說明:

  • # Samples - 樣本數
  • Average、Min、Max、Std Dev. - 每次請求耗費時間之平均、最小、最大值及標準差
  • Error % - 錯誤率
  • Throughput - 每單位時間(秒/分/時)的請求數
  • Received KB/sec、Sent KB/sec - 每秒接收及傳送資料大小(KB)
  • Avg. Bytes - 平均回應內容長度(Bytes)

再來,我試了 10 條 Thread 各跑 100 次,平均執行時間由 85ms 上升到 139ms,Throughput 由 11.7 次/秒 上升到 62.1 次/秒,Throughput 沒有放大 10 倍,可推測已出現瓶頸。調參數跑一下測試便能拿到具體數字讓人興奮,馬上想到有好多有趣的實驗可以玩,但這篇就先到這邊,之後再慢慢分享。

This article introduces how to use JSON files as HTTP request bodies in JMeter load test.


Comments

Be the first to post a comment

Post a comment