3

JMeter 壓力測試練習 - 使用 JSON 檔作為 HTTP 請求內容

 1 year ago
source link: https://blog.darkthread.net/blog/jmeter-test-w-json-files/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

使用 JSON 檔作為 HTTP 請求內容-黑暗執行緒

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 方法,模擬投注站向中央系統登記顧客投注結果的動作:

Fig1_638161758928373695.png

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

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

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

Fig2_638161758931886756.png

接下來說說怎麼建一個 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 檔),後會再逐步說明重點:

Fig3_638161758933880215.png

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

Fig4_638161758935784287.png

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

Fig5_638161758937842367.png

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

Fig6_638161758939783635.png

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

Fig7_638161758941961189.png

由於不想寫死 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\\"));

Fig8_638161758943972806.png

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

Fig9_638161758945905152.png

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

FigA_638161758947895505.png

欄位說明:

  • # 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 倍,可推測已出現瓶頸。調參數跑一下測試便能拿到具體數字讓人興奮,馬上想到有好多有趣的實驗可以玩,但這篇就先到這邊,之後再慢慢分享。

FigB_638161758949900024.png


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK