背景: wrk 是當今最流行的 HTTP 壓測工具,用于模擬高并發(fā)情況下的 HTTP 請求。wrk 使用 Lua 作為腳本語言,可以通過編寫 Lua 腳本來自定義請求的參數(shù)和邏輯。它支持多線程并發(fā)請求,并提供了豐富的統(tǒng)計信息和報告,可以幫助你評估服務(wù)器的性能和承受能力。本貼致力于最快速讓你上手wrk??赐瓯举N,你將學會使用 wrk 對 http 接口進行壓測, 并計算其 TPS 指標。
安裝 wrk(需要在 linux 系統(tǒng)上)
命令行輸入一下命令下載 wrk 源碼
git clone https://github.com/wg/wrk.git
隨后進入 wrk 目錄并進行編譯
cd wrk
make
隨后將生成一個可執(zhí)行的 wrk 文件,我們可以把這個文件拷貝到我想要的地方,或者直接拷貝到 bin 目錄:
cp wrk /usr/local/bin/
新建工程
創(chuàng)建新目錄 wrk_demo 在該目錄下打開命令行輸入:
go mod init wrk
go mod tidy
隨后創(chuàng)建各目錄與文件如下:
-- wrk_demo
-- main.go http 服務(wù)端啟動文件
-- tps.lua wrk 請求參數(shù)與結(jié)果統(tǒng)計腳本
-- wrk wrk 可執(zhí)行文件, 由 git 倉庫 make 而來
-- go.mod --go.sum
main.go
先來看 服務(wù)端 代碼, 非常簡單,注冊了 個 hello 路由, 然后簡單校驗了下 請求方法, header, 和 body 內(nèi)容, 若沒問題則返回一個 "hello from hello handler" 字符串。
package main
import (
"fmt"
"io"
"net/http"
)
func main() {
http.HandleFunc("/hello", helloHandler)
errChan := make(chan error)
go func() {
errChan <- http.ListenAndServe(":9000", nil)
}()
err := <-errChan
if err != nil {
fmt.Println("Server stop running.")
}
}
func helloHandler(w http.ResponseWriter, r *http.Request) {
// check method
if r.Method != http.MethodPost {
w.Write([]byte("{"msg":"method error"}"))
return
}
// check header
if r.Header.Get("user_token") != "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJVc2VySUQiOjEsIlVzZXJuYW1lIjoiVG9tIiwiR3JhbnRTY29wZSI6InJlYWRfdXNlcl9pbmZvIiwiaXNzIjoiQXV0aF9TZXJ2ZXIiLCJzdWIiOiJUb20iLCJhdWQiOlsiQW5kcm9pZF9BUFAiLCJJT1NfQVBQIl0sImV4cCI6MTY4MDk1MDQ4OSwibmJmIjoxNjgwOTQ2ODkwLCJpYXQiOjE2ODA5NDY4ODksImp0aSI6IkR6ZzlNZ1NlUFIifQ.7Yi42Ur2Yivh5dpmMY-CxpQ5kR0IoIAh7F8xNLjdAcM" {
w.Write([]byte("{"msg":"userToken error"}"))
return
}
// check body
body_bytes, err := io.ReadAll(r.Body)
if err != nil {
w.Write([]byte("{"msg":"read body error"}"))
return
}
if string(body_bytes) != "{"username":"Tom"}" {
w.Write([]byte("{"msg":"body content error"}"))
return
}
// return ok
w.Write([]byte("{"msg":"hello from hello handler"}"))
return
}
tps.lua
在 lua 腳本中我們定義了請求的各種參數(shù), 使用 wrk.method, wrk.body, wrk.headers 可以很輕易的設(shè)置請求方法, 請求體和請求參數(shù)。
在 WRK中,我們是可以設(shè)置發(fā)起請求的線程數(shù)的,要統(tǒng)計 TPS, 就得 統(tǒng)計每一個線程收到的 成功返回的數(shù)量,然后將全部線程的成功返回數(shù)量相加,再除以總的響應(yīng)時間, 就是TPS。
在 wrk 中可以通過 setup 函數(shù)對每一個線程設(shè)置一些初始變量,比如我定義了一個 success_counter 用于統(tǒng)計成功的返回數(shù), 初始化為0. 隨后將這個 success_counter 注冊到了 這個 線程中,使用一個全局變量 threads 用于存儲所有 線程(實際上只關(guān)注線程里面的 success_counter)
在 wrk 中還可以通過 response 函數(shù)對每個 thread 的每一個請求的 response 進行獲取,比如我根據(jù) body 是否等于某個值來判斷是否返回成功的結(jié)果, 若成功返回則進行 success_counter+1. 事實上 setup 函數(shù)與 response 函數(shù)是 同一個 thread 里面的, 所以 這個 success_counter 變量可以跨函數(shù)共享, 所以在 response 函數(shù)中+1, 整個 thread 中的 thread 變量也會+1. 并且 thread.set 操作的值是一個 指針, 所以 無需再次調(diào)用 thread:set("success_counter", success_counter)即可完成 對 thread 對象的賦值操作。
在所有請求結(jié)束以后, wrk 會調(diào)用 done 函數(shù)獲取到運行的結(jié)果。這里我們遍歷了所有 threads, 將里面成功的返回相加再除以總的響應(yīng)時間, 即為接口的TPS。由于 summary.duration是微妙, 所以計算時需要乘以 1000*1000.
wrk.method = "POST"
wrk.body = '{"username":"Tom"}'
wrk.headers["user_token"] = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJVc2VySUQiOjEsIlVzZXJuYW1lIjoiVG9tIiwiR3JhbnRTY29wZSI6InJlYWRfdXNlcl9pbmZvIiwiaXNzIjoiQXV0aF9TZXJ2ZXIiLCJzdWIiOiJUb20iLCJhdWQiOlsiQW5kcm9pZF9BUFAiLCJJT1NfQVBQIl0sImV4cCI6MTY4MDk1MDQ4OSwibmJmIjoxNjgwOTQ2ODkwLCJpYXQiOjE2ODA5NDY4ODksImp0aSI6IkR6ZzlNZ1NlUFIifQ.7Yi42Ur2Yivh5dpmMY-CxpQ5kR0IoIAh7F8xNLjdAcM"
-- thread table
local threads = {}
-- set up some variable for each thread
function setup(thread)
success_counter=0
thread:set("success_counter", success_counter)
table.insert(threads, thread)
end
-- record successful response
function response(status, headers, body)
if body == "{"msg":"hello from hello handler"}" then
success_counter = success_counter + 1
end
end
-- calculate tps
function done(summary, latency, requests)
--sum up successful response from each thread
total_success_counter = 0
for _, thread in ipairs(threads) do
total_success_counter = total_success_counter +thread:get("success_counter")
end
print("total_success_counter = " .. total_success_counter )
print("TPS = " .. 1000*1000*total_success_counter/summary.duration)
end
此外 wrk 還內(nèi)置別的函數(shù) 如 init, request, delay, 這些都可以對每一個線程, 每一個請求做出更加細致的操作,有興趣的同學可以自行查找如何使用。
運行起來
首先我們啟動服務(wù)端, 確保在 wrk_demo 目錄下運行
go run main.go
隨后啟動 wkr:
./wrk -t 2 -c 4 -d 1s -s tps.lua http://localhost:9000/hello
-t 2參數(shù)表示使用 2 個線程, -c 4參數(shù) 表示總的并發(fā)連接數(shù)為4, 每一個 線程連接數(shù) = 總的并發(fā)連接/線程數(shù)量, -d 1s 表示請求1秒, -s tps.lua 表示請求 1秒, 最后的 http://localhost:9000/hello 為 請求的 URL
運行結(jié)果如下:
2 threads and 4 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 169.17us 64.88us 1.04ms 81.37%
Req/Sec 10.65k 689.25 12.12k 70.00%
21167 requests in 1.01s, 3.05MB read
Requests/sec: 20975.47
Transfer/sec: 3.02MB
total_success_counter = 21167
TPS = 20975.472956435
可以看到 啟動了 2給線程,4個并發(fā), 每個 線程的平均延遲時間為 169.17us, 平均每秒請求數(shù)為10.65k, 還可以看到這兩個指標的最大值, 標準差和正負標準差之間的比例。 總共在 1.01 秒內(nèi)完成了 21167 次請求, 收到了服務(wù)端3.02MB的返回數(shù)據(jù)。 平均每秒請求數(shù)為 20975.47, 每秒收到的服務(wù)端數(shù)據(jù)為 3.02MB(也稱吞吐量)。總的成功返回數(shù)為 21167, TPS 為 20975.472956435。
巨人的肩膀
- github.com/wg/wrk
- q474818917.github.io/2017/05/17/…
最后: 下方這份完整的軟件測試視頻教程已經(jīng)整理上傳完成,需要的朋友們可以自行領(lǐng)取【保證100%免費】
軟件測試面試文檔
我們學習必然是為了找到高薪的工作,下面這些面試題是來自阿里、騰訊、字節(jié)等一線互聯(lián)網(wǎng)大廠最新的面試資料,并且有字節(jié)大佬給出了權(quán)威的解答,刷完這一套面試資料相信大家都能找到滿意的工作。文章來源:http://www.zghlxwxcb.cn/news/detail-775434.html
文章來源地址http://www.zghlxwxcb.cn/news/detail-775434.html
這些都在我的軟件測試學習交流群里:902061117 自取
到了這里,關(guān)于使用 wrk 對 http 接口進行壓測并 計算其 TPS的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!