背景
- 要用 wrk 進行壓測
- 看了下其他同事的壓測,都用了 Lua 指令碼來自定義一些東西
- 所以這一篇主要講 Lua 指令碼
Lua 介紹
- Lua 指令碼是一種輕量小巧的指令碼語言,用標準 c 語言編寫,並以原始碼形式開放
- 其設計目的是為了嵌入應用程式中,從而為程式提供靈活的擴充套件和定製功能。
- wrk 工具嵌入了 Lua 指令碼語言
- 因此,在自定義壓測場景時,可在 wrk 目錄下使用 Lua 定製壓測場景
Lua 指令碼的三個階段
wrk 支援在三個不同的階段執行 LuaJIT 指令碼
- setup:設定階段
- running:執行階段
- done:結束階段
每個 wrk 執行緒都有一個獨立的指令碼環境,因為獨有獨立的 Lua 虛擬機器
setup、done 階段在一個單獨的環境中執行,不參與 running 階段
官方文件:https://github.com/wg/wrk/blob/master/SCRIPTING
POST 請求
前言
- 之前說過,如果沒有自定義的 Lua 指令碼,wrk 預設傳送的是 HTTP 1.1 GET 請求
- 這裡如果想發起 POST 請求的話,Lua 指令碼要怎麼寫
官方指令碼
-- POST 請求,演示如何新增
-- HTTP method, body, header wrk.method = "POST"
wrk.body = "foo=bar&baz=quux"
wrk.headers["Content-Type"] = "application/x-www-form-urlencoded"
wrk 變數
- wrk 是一個內建的全域性 table 型別變數,不需要定義可以直接使用
- 修改 wrk 變數的值,會對所有請求都生效
wrk = {
scheme = "http",
host = "localhost",
port = nil,
method = "GET",
path = "/",
headers = {},
body = nil,
thread = <userdata>
}
wrk 內建函式
function wrk.format(method, path, headers, body)
- 根據函式的引數和全域性 wrk 變數,返回一個自定義的 http 請求字串
- 注意:函式的引數會覆蓋 wrk 全域性變數對應的引數值
- 可以通過 format 可以構造出不同的 request
function wrk.lookup(host, service)
返回所有可用伺服器的地址資訊
function wrk.connect(addr)
- 測試指定的伺服器地址是否能正常連線
- 如果地址可以連線到 wrk.connect,則返回true,否則返回false
- 地址必須是從 wrk.lookup() 返回的地址
Lua 指令碼三個階段的內建函式
前言
上面也說到有三個階段,setup、running、done 階段,他們分別都有一些內建函式
setup 啟動階段
function setup(thread)
- 每個執行緒初始化時執行一次,wrk 會在測試執行緒已經初始化但還沒有啟動的時候呼叫該方法
- setup 方法會傳入一個 thread 物件,可以修改或設定 thread 相關引數,也可以終止執行緒執行
- 這裡一般做一些初始化的工作,例如讀取配置檔案,載入到記憶體(不要每次請求的時候讀取一遍,這樣對測試準確性影響很大)
thread 的一些方法和變數
thread.addr - get or set the thread's server address,獲取或設定伺服器地址資訊
thread:get(name) - get the value of a global in the thread's env,獲取當前執行緒引數
thread:set(name, value) - set the value of a global in the thread's env,設定當前執行緒引數
thread:stop() - stop the thread,終止執行緒
- 只有布林值、nil值、數字和字串值或相同的 table 可以通過 get() / set() 進行操作
- thread:stop() 只能在執行緒執行時被呼叫
running 執行階段
function init(args)
- 由執行緒呼叫,線上程開始啟動時僅執行一次
- args 是通過命令列傳入的引數,通過 -- 指定
function delay()
- 每次傳送請求時,間隔時間(ms)
- 每次傳送請求前都會執行一次
function request()
- 每次傳送請求都會執行一次
- 返回一個自定義的 HTTP 請求字串
官方建議
- 每次構建一個新的請求都很耗時耗資源
- 當測試高效能伺服器時,建議在 init() 中預生成所有請求,並在 request() 中進行快速查詢
實際使用
- 一般在這裡會配合
wrk.format()
方法,動態建立請求 - 這裡不要執行耗時的程式碼,否則會影響測試結果準確性
function response(status, headers, body)
- 每次請求得到響應時執行一次
- status:響應狀態碼
- headers:響應頭
- body:響應體
- 解析 header 和 body 的開銷比較大,所以如果沒有定義
response
回撥方法的話,wrk 就不會解析 header 和 body - 這樣測試結果會更加準確(解析響應資料是客戶端負責的,不能算到伺服器處理時間裡面)
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
}
}
這個感覺不常用,用到再舉栗子吧
具體的栗子
Lua 指令碼
-- example script that demonstrates use of setup() to pass
-- data to and from the threads local counter = 1
local threads = {} function setup(thread)
-- 給每個執行緒設定一個 id 引數
thread:set("id", counter)
-- 將執行緒新增到 table 中
table.insert(threads, thread)
counter = counter + 1
end function init(args)
-- 初始化兩個引數,每個執行緒都有獨立的 requests、responses 引數
requests = 0
responses = 0 -- 列印執行緒被建立的訊息,列印完後,執行緒正式啟動執行
local msg = "thread %d created"
print(msg:format(id))
end function request()
-- 每發起一次請求 +1
requests = requests + 1
return wrk.request()
end function response(status, headers, body)
-- 每得到一次請求的響應 +1
responses = responses + 1
end function done(summary, latency, requests)
-- 迴圈執行緒 table
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
執行命令
wrk -d3s -c20 -t5 -s test.lua https://*****/get
執行結果
建立了 5 個執行緒, 以及每個執行緒發起的請求數和得到的響應數都有打印出來
工作上的模板栗子
Lua 指令碼
為防止被盜,只放圖片
官方指令碼栗子
https://github.com/wg/wrk/tree/master/scripts