wrk是一個開源的、熱門的、現代的單機HTTP基準測試工具,目前在github開源平臺累計了26.9k的star數目,足以可見wrk在Http基準測試領域的熱門程度。它結合了多線程設計和可擴展的事件通知系統,如epoll和kqueue,能夠在有限的資源下併發出極致的的負載請求。而且內置了一個可選的LuaJIT腳本執行引擎,能夠處理複雜的HTTP請求生成、響應處理以及自定義壓測報告。
wrk項目地址:https://github.com/wg/wrkgit
mac下安裝:github
brew install wrk
其餘平臺參考:https://github.com/wg/wrk/wikishell
wrk -t12 -c100 -d30s --latency http://localhost:8010/healthz
如上指令描述了採用12個線程,100個連接,針對/healthz 接口服務,持續壓測30s。wrk自己不是依賴線程數來模擬併發數的因此線程數量設置在覈心數左右最好,線程數多了測試系統消耗大,可能帶來反效果。親測核心數一致的線程數和兩倍核心數的線程數,前者壓出的QPS更高。壓測結果以下:緩存
Running 30s test @ http://localhost:8010/healthz (運行30s測試) 12 threads and 100 connections(12個線程100個鏈接) Thread Stats Avg(均值) Stdev(標準差值) Max(最大值) +/- Stdev(正負標準差值) Latency(延遲) 1.39ms 668.10us 23.95ms 90.34% Req/Sec(每秒請求數) 5.44k 545.23 10.27k 76.47% Latency Distribution(延遲直方圖) 50% 1.32ms (50%請求延遲在1.32ms內) 75% 1.49ms (75%請求延遲在1.49ms內) 90% 1.72ms (90%請求延遲在1.72ms內) 99% 4.77ms (99%請求延遲在4.77ms內) 1952790 requests in 30.08s, 271.90MB read (共1952790次請求,用時30s,傳輸了271.9M數據) Requests/sec(每秒請求數): 64930.12 Transfer/sec(每秒傳輸數據): 9.04MB
wrk的結果相比ab測試結果來講,多了一個延時直方圖,有了這個直方圖,咱們能夠更清晰的看到延遲的分佈狀況。這也是博主選擇wrk最重要的緣由服務器
-c, --connections: 要保持打開的HTTP鏈接的總數,每一個線程處理數N =鏈接/線程 -d, --duration: 測試持續時間, 如 2s, 2m, 2h -t, --threads: 測試線程總數 -s, --script: 指定加載lua測試擴展腳本 -H, --header: 添加請求頭信息, 如"User-Agent: wrk" --latency: 打印延遲直方圖信息 --timeout: 若是在此時間內沒有收到響應,則記錄超時.
-開頭的指令爲簡寫的,後面兩個打印延遲直方圖和超時設置沒有簡寫的,只能--開頭指定多線程
wrk內置了全局變量,全局方法,以及五個測試請求發起流程的方法,還有一個模擬延遲發送的方法,wrk是內置對象,在lua測試腳本的每一個方法內均可以直接使用併發
-- 全局的變量 wrk = { scheme = "http", host = "localhost", port = nil, method = "GET", path = "/", headers = {}, body = nil, thread = userdata, }
-- 返回請求字符串值,其中包含所傳遞的參數和來自wrk表的值。例如:返回 http://www.kailing.pub 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)
-- 請求前,對每一個線程調用一次,並接收表示該線程的userdata對象。 function setup(thread) thread.addr = "http://www.kailing.pub" -- 設置請求的地址 thread:get("name") -- 獲取全局變量的值 thread:set("name", "kl") -- 在線程的環境中設置全局變量的值 thread:stop() -- 中止線程 end --初始化,每一個線程執行一次 function init(args) --args爲從命令行傳過來的額外參數 print(args) end --發起請求,每次請求執行一次,返回包含HTTP請求的字符串。每次構建新請求的開銷都很大,在測試高性能服務器時, --一種解決方案是在init()中預先生成全部請求,並在request()中進行快速查找。 function request() requests = requests + 1 return wrk.request() end --響應處理,每次請求執行一次 function response(status, headers, body) responses = responses + 1 end --請求完成,每次測試執行一次。done()函數接收一個包含結果數據的表和兩個統計數據對象,分別表示每一個請求延遲和每一個線程請求速率。 --持續時間和延遲是微秒值,速率是以每秒請求數來度量的。 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
整個腳本處理過程被分爲準備階段、運行階段、完成階段。準備階段在目標IP地址被解析而且全部線程都已經初始化但尚未啓動以後開始。運行階段從對init()的單個調用開始,而後對每一個請求週期調用request()和response()。init()函數接收腳本的任何額外命令行參數,這些參數必須用「——」與wrk參數分隔開。app
案例:咱們線上有一個帶緩存場景的接口服務,根據appId的值的查詢結果緩存,因此,若是單純對指定的appId壓測,就變成了測試緩存系統的負載了,測試不出實際的服務性能,這個場景就須要測試工具發起每次請求的測試參數都是動態的。根據這個場景咱們定製了以下的lua測試腳本:dom
-- 測試指令:wrk -t16 -c100 -d5s -sreview_digress_list.lua --latency htt://127.0.0.1:8081 wrk.method ="GET" wrk.path = "/app/{appId}/review_digress_list" function request() -- 動態生成每一個請求的url local requestPath = string.gsub(wrk.path,"{appId}",math.random(1,10)) -- 返回請求的完整字符串:http://127.0.0.1//app/666/review_digress_list return wrk.format(nil, requestPath) end