使用 WRK 壓力測試工具對 ASP.NET Core 的接口進行壓力測試


0. 簡要介紹

WRK 是一款輕量且易用的 HTTP 壓力測試工具,通過該工具我們可以方便地對我們所開發的 WebAPI 項目進行壓力測試,並且針對測試的情況返回結果。

PS:Wrk 並不能針對測試的結果生成動態的圖表,如果有這種需要,可以嘗試使用另一款工具 Vegeta。該項目使用的 Golang 進行編寫,其 GitHub 地址為:https://github.com/tsenart/vegeta

下面的內容就是一個標准的測試結果信息:

# 針對 127.0.0.1:8080 進行壓力測試
wrk -t12 -c400 -d30s http://127.0.0.1:8080/index.html
# 這里是測試結果
Running 30s test @ http://127.0.0.1:8080/index.html
  12 threads and 400 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency   635.91us    0.89ms  12.92ms   93.69%
    Req/Sec    56.20k     8.07k   62.00k    86.54%
  22464657 requests in 30.00s, 17.76GB read
Requests/sec: 748868.53
Transfer/sec:    606.33MB

1. 安裝

關於 OS X 與 Windows 的安裝可以參考 Wrk 官方 WIKI 進行操作,本文主要講解一下 CentOS 7.x 下如果進行編譯。

sudo yum groupinstall 'Development Tools'
sudo yum install -y openssl-devel git 
git clone https://github.com/wg/wrk.git wrk
cd wrk
make

編譯之后,你會得到如下結果:

可以看到生成了一個 wrk 的可執行文件,你可以將其添加到環境變量的 PATH 當中,這里就不再贅述,我們等會兒使用的時候直接 ./wrk 使用。

2. 命令說明

./wrk -H "Authorization: Bearer TokenValue" -t 2 -c 50 -d 10s --latency --timeout 1s "http://"

上面的命令就是一個典型的壓力測試命令,關於參數的含義請看下表。

執行命令時的參數 含義 示例
-c 與 HTTP 保持連接的連接數,最終每個線程能夠處理的為 連接數/線程數。 -c 50
-d 指定壓力測試的時間有多長。 -d 10s,其他單位有 2s,2m,2h
如果不帶單位的話,默認為秒。
-t 壓力測試時所使用的線程數目,最好為你 CPU 核心的數量。 -t 4
-s 指定要執行的 Lua 腳本 -s ./post.lua
-H 執行請求的時候所附帶的 Header 組。 -H "User-Agent: wrk"
--latency 打印詳細的統計信息。 --latency
--timeout 每次請求所返回響應體的時間,如果超過了配置的時間,則視為請求超時。 --timeout 1s

3. 開始壓力測試

執行了上述代碼之后我們可以看到很直觀的信息,第一個就是 20s 的時間內完成了 2887 次請求,一共接受到了 2.46MB 的數據。在 Socket errors 里面我們可以看到有 35 個請求產生了超時的情況,每秒執行的請求大概為 144.20 個,每秒的數據傳輸大概為 125.75 KB。

除此之外,還說明了平均每次請求所消耗的時間為 338.44 ms,最極端的情況為 994.27ms。

4. LUA 腳本

在第三節我們可以看到一些標准的 GET 請求我們可以直接通過指定命令來進行測試,即便該接口有授權驗證,我們可以通過 -H 參數來指定 Authorization 頭來實現權限驗證。
但是針對一些復雜的情況,我們就需要編寫 LUA 腳本來實現壓力測試了。

官方編寫了很多的 LUA 腳本 DEMO ,存放在 GitHub 上面,其地址為:https://github.com/wg/wrk/tree/master/scripts

這里我們以實現 POST 請求為例:

wrk.method = "POST"
wrk.body   = '{"username":"admin","password":"123qwe","rememberClient":true}'
wrk.headers["Content-Type"] = "application/json"

這里我們的接口地址更改了一下,改變成了 Login 接口,該接口需要傳入用戶名與密碼,並且其 Method 為 POST。

將上述 LUA 腳本保存為 post.lua 文件,然后通過 -s 參數指定 LUA 腳本的路徑並執行。

5. LUA 腳本相關詳解

WRK 中執行 HTTP 請求的時候,調用 Lua 分為 3 個階段,setuprunningdone,每個 WRK 線程中都有獨立的腳本環境。

5.1 WRK 的全局屬性

wrk = {
  scheme  = "http",
  host    = "localhost",
  port    = nil,
  method  = "GET",
  path    = "/",
  headers = {},
  body    = nil,
  thread  = <userdata>,
}

5.2 WRK 的全局方法

-- 生成整個request的string,例如:返回
-- GET / HTTP/1.1
-- Host: tool.lu
function wrk.format(method, path, headers, body)

-- 獲取域名的IP和端口,返回table,例如:返回 `{127.0.0.1:80}`
function wrk.lookup(host, service)

-- 判斷addr是否能連接,例如:`127.0.0.1:80`,返回 true 或 false
function wrk.connect(addr)

5.3 Setup 階段

setup() 方法是在線程創建之后,啟動之前。

function setup(thread)

-- thread提供了1個屬性,3個方法
-- thread.addr 設置請求需要打到的ip
-- thread:get(name) 獲取線程全局變量
-- thread:set(name, value) 設置線程全局變量
-- thread:stop() 終止線程

5.4 Running 階段

function init(args)
-- 每個線程僅調用1次,args 用於獲取命令行中傳入的參數, 例如 --env=pre

function delay()
-- 每個線程調用多次,發送下一個請求之前的延遲, 單位為ms

function request()
-- 每個線程調用多次,返回http請求

function response(status, headers, body)
-- 每個線程調用多次,返回http響應

5.5 Done 階段

可以用於自定義結果報表,整個過程中只執行一次。

function done(summary, latency, requests)


latency.min              -- minimum value seen
latency.max              -- maximum value seen
latency.mean             -- average value seen
latency.stdev            -- standard deviation
latency:percentile(99.0) -- 99th percentile value
latency(i)               -- raw value and count

summary = {
  duration = N,  -- run duration in microseconds
  requests = N,  -- total completed requests
  bytes    = N,  -- total bytes received
  errors   = {
    connect = N, -- total socket connection errors
    read    = N, -- total socket read errors
    write   = N, -- total socket write errors
    status  = N, -- total HTTP status codes > 399
    timeout = N  -- total request timeouts
  }
}

而官方的 setup.lua 腳本則是重載這些方法並使用的一個 DEMO:

-- example script that demonstrates use of setup() to pass
-- data to and from the threads

local counter = 1
local threads = {}

function setup(thread)
   thread:set("id", counter)
   table.insert(threads, thread)
   counter = counter + 1
end

function init(args)
   requests  = 0
   responses = 0

   local msg = "thread %d created"
   print(msg:format(id))
end

function request()
   requests = requests + 1
   return wrk.request()
end

function response(status, headers, body)
   responses = responses + 1
end

function done(summary, latency, requests)
   for index, thread in ipairs(threads) do
      local id        = thread:get("id")
      local requests  = thread:get("requests")
      local responses = thread:get("responses")
      local msg = "thread %d made %d requests and got %d responses"
      print(msg:format(id, requests, responses))
   end
end

6. 參考資料

wrk中的lua腳本:https://type.so/linux/lua-script-in-wrk.html

http 性能測試 wrk使用教程:https://juejin.im/post/5a59e74f5188257353008fea


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM