價值說明: 利用wrk 2分鐘知曉你新開發(fā)的接口的基本性能
一、簡介
????????wrk是一個開源的、熱門的、現(xiàn)代的單機HTTP基準測試工具。它能夠在單機多核 CPU 的條件下,使用系統(tǒng)自帶的高性能 I/O 機制,如 epoll,kqueue 等,通過多線程和事件模式,對目標機器產(chǎn)生大量的負載。
????????????PS: 其實,wrk 是復用了 redis 的 ae 異步事件驅動框架,準確來說 ae 事件驅動框架并不是 redis 發(fā)明的, 它來至于 Tcl 的解釋器 jim, 這個小巧高效的框架, 因為被 redis 采用而被大家所熟知。
????????????并且內置了一個可選的Lua? JIT腳本執(zhí)行引擎,可以處理復雜的HTTP請求生成、響應處理以及自定義壓測報告。
二、跟其他壓測工具優(yōu)缺點對比
一些常用的性能測試工具,如 Apache ab,? Apache JMeter (互聯(lián)網(wǎng)公司用的較多),LoadRunner 等。
wrk 的優(yōu)勢:
????*輕量級性能測試工具; wrk的結果相比ab測試結果來說,多了一個延時直方圖,有了這個直方圖,我們可以更清晰的看到延遲的分布情況。這也是博主選擇wrk最重要的原因
????* 安裝簡單(相對 Apache ab 來說);
????* 學習曲線基本為零,幾分鐘就能學會咋用了;
? ? *基于系統(tǒng)自帶的高性能 I/O 機制,如 epoll, kqueue, 利用異步的事件驅動框架,通過很少的線程就可以壓出很大的并發(fā)量;
劣勢
????????wrk 目前僅支持單機壓測,后續(xù)也不太可能支持多機器對目標機壓測,因為它本身的定位,并不是用來取代 JMeter, LoadRunner 等專業(yè)的測試工具,wrk 提供的功能,對我們后端開發(fā)人員來說,應付日常接口性能驗證還是比較友好的。
結論:應付日常接口性能驗證還是比較友好的
三、Mac上安裝
先安裝Homebrew,安裝方式參考官網(wǎng) https://brew.sh
也可以執(zhí)行下面命令? 一鍵安裝? > /bin/bash -c "$(curl -fsSLhttps://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
安裝wrk:? >? brew install wrk
驗證是否安裝成功
> wrk -v

四、常用命令、注釋和結果分析
命令:? > wrk -t12 -c400 -d30shttp://www.baidu.com
????????這條命令表示,利用 wrk 對 www.baidu.com 發(fā)起壓力測試,線程數(shù)為 12,模擬 400 個并發(fā)請求,持續(xù) 30 秒。
詳細參數(shù)含義解析
????????常用指令說明
????????????????-c, --connections: 要保持打開的HTTP連接的總數(shù),每個線程處理數(shù)N =連接/線程
????????????????-d, --duration:? ? 測試持續(xù)時間, 如 2s, 2m, 2h
????????????????-t, --threads:? ? 測試線程總數(shù)
????????????????-s, --script:? ? ? 指定加載lua測試擴展腳本
????????????????-H, --header:? ? ? 添加請求頭信息, 如"User-Agent: wrk"
????????????????? --latency:? ? 打印延遲直方圖信息
????????????????? --timeout:? ? 如果在此時間內沒有收到響應,則記錄超時.
????????????????????????????????-開頭的指令為簡寫的,后面兩個打印延遲直方圖和超時設置沒有簡寫的,只能--開頭指定
????命令行中輸入 wrk --help, 可以看到支持以下子命令:
結果分析:

五、Get請求如何進行壓測
-- 測試指令:wrk -t16 -c100 -d5s -s review_digress_list.lua --latency htt://127.0.0.1:8081
wrk.method ="GET"?
wrk.path = "/app/{appId}/review_digress_list"
?function request()?
? ???????-- 動態(tài)生成每個請求的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
六、Post請求如何進行壓測
6.1、Post請求
wrk默認是采用GET請求方式進行接口測試,如果需要使用POST請求就需要使用到lua腳本,通過加載編寫好的lua腳本來進行定制化的請求。采用-s 或者 --script可以加載腳本文件。
wrk的請求構造過程主要是針對每一個線程的,所以對于壓測的環(huán)境也提供了一些函數(shù)方法來進行支持線程的環(huán)境定制化。
對于POST請求+入?yún)⒉灰恢碌慕鉀Q:
對于這種需求,我們可以通過編寫 Lua 腳本的方式,在運行壓測命令時,通過參數(shù) --script 來指定 Lua 腳本,來滿足個性化需求。
wrk 對 Lua 腳本的支持
????wrk 支持在三個階段對壓測進行個性化,分別是啟動階段、運行階段和結束階段。每個測試線程,都擁有獨立的Lua 運行環(huán)境。
? ????? 啟動階段
????????????在腳本文件中實現(xiàn) setup 方法,wrk 就會在測試線程已經(jīng)初始化,但還沒有啟動的時候調用該方法。wrk會為每一個測試線程調用一次 setup 方法,并傳入代表測試線程的對象 thread 作為參數(shù)。setup 方法中可操作該 thread 對象,獲取信息、存儲信息、甚至關閉該線程。
????????運行階段
? ? ? ? ? ? ? ?* init(args): 由測試線程調用,只會在進入運行階段時,調用一次。支持從啟動 wrk 的命令中,獲取命令行參數(shù);
? ? ? ? ? ? ? ?* delay(): 在每次發(fā)送請求之前調用,如果需要定制延遲時間,可以在這個方法中設置;
? ? ? ? ? ? ? ?* request(): 用來生成請求,每一次請求都會調用該方法,所以注意不要在該方法中做耗時的操作;
? ? ? ? ? ? ? ?* response(status, headers, body): 在每次收到一個響應時被調用,為提升性能,如果沒有定義該方法,那么wrk不會解析 headers 和 body;
? ? ? ?結束階段
? ? ? ? ? ? ? ?done() 方法在整個測試過程中只會被調用一次,我們可以從給定的參數(shù)中,獲取壓測結果,生成定制化的測試報告。
6.2、lua腳本
首先下載lua腳本 把接口+入?yún)⑻鎿Q為對應值
-- example script that demonstratesuseof setup() to pass-- data toandfrom the threadslocalcounter =1localthreads = {}function setup(thread)thread:set("id", counter) -- 設置線程id table.insert(threads, thread) -- 用threads的表格存儲counter = counter +1-- 為避免id相同,使用counter來實現(xiàn)遞增endfunction init(args) -- 注意init中初始化的值都是針對一個線程而言的!即每個線程都是隔離的requests =0-- 初始化,注意這里的初始化的變量都將成為wrk的全局變量,即后續(xù)可以直接用responses =0localmsg ="thread %d created"print(msg:format(id))end-- 全局設定 設置請求格式--wrk.method ="POST"--wrk.body ="name=zhangsan&password=123456"wrk.headers["Content-Type"] ="application/json"wrk.headers["Accept"] ="*/*"wrk.headers["Accept-Encoding"] ="gzip, deflate, br"request = function()requests = requests +1-- 構造請求數(shù),因為init中定義了,所以這里可以直接使用uid = math.random(1,10000000)method ="POST"path ="/personal/bill/queryBillDeductDetailAll"body = string.format("uid=%s&serialNo=%s&version=%s&lastId=%s",uid,20220920000108360706,519,0)returnwrk.format(method,path,nil,body)end--function request()-- requests = requests +1-- 構造請求數(shù),因為init中定義了,所以這里可以直接使用--returnwrk.request()--endfunction response(status, headers, body)responses = responses +1-- 獲取響應數(shù),因為init中定義了,所以可以直接使用endfunction done(summary, latency, requests)forindex, thread in ipairs(threads)dolocalid = thread:get("id") -- 獲取線程idlocalrequests = thread:get("requests") -- 獲取構造請求數(shù)localresponses = thread:get("responses") -- 獲取響應數(shù)localmsg ="thread %d made %d requests and got %d responses"print(msg:format(id, requests, responses)) endend
然后控制臺輸入如下命令(需要修改ip地址)
wrk -t32 -c400 -d30s -s setup.lua --latency htt://10.72.240.219:8888

七、小技巧
????如何將某個參數(shù)變成隨機參數(shù)
????????-- 動態(tài)生成每個請求的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)
命令行里打印日志
????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))
八、注意事項
????????wrk本身不是依賴線程數(shù)來模擬并發(fā)數(shù)的所以線程數(shù)量設置在核心數(shù)左右最好,線程數(shù)多了測試系統(tǒng)消耗大,可能帶來反效果。之前測試跟核心數(shù)一致的線程數(shù)和兩倍核心數(shù)的線程數(shù),前者壓出的QPS更高。
? ? ? ? ? ? 特別提醒:關于線程數(shù),并不是設置的越大,壓測效果越好,線程設置過大,反而會導致線程切換過于頻繁,效果降低,一般來說,推薦設置成壓測機器 CPU 核心數(shù)的 2 倍到 4 倍就行了。
有問題歡迎隨時交流