背景:wrk
是当今最流行的 HTTP
压测工具,用于模拟高并发情况下的 HTTP
请求。wrk
使用 Lua
作为脚本语言,可以通过编写 Lua
脚本来自定义请求的参数和逻辑。
它支持多线程并发请求,并提供了丰富的统计信息和报告,可以帮助你评估服务器的性能和承受能力。本贴致力于最快速让你上手wrk
。看完本贴,你将学会使用 wrk
对 http
接口进行压测, 并计算其 TPS
指标。
安装 wrk(需要在 linux 系统上)
命令行输入一下命令下载 wrk
源码
git clone https://github.com/wg/wrk.git
随后进入 wrk
目录并进行编译
cd wrk
make
随后将生成一个可执行的 wrk
文件,我们可以把这个文件拷贝到我想要的地方,或者直接拷贝到 bin
目录:
cp wrk /usr/local/bin/
新建工程
创建新目录 wrk_demo
在该目录下打开命令行输入:
go mod init wrk
go mod tidy
随后创建各目录与文件如下:
-- wrk_demo
-- main.go http 服务端启动文件
-- tps.lua wrk 请求参数与结果统计脚本
-- wrk wrk 可执行文件, 由 git 仓库 make 而来
-- go.mod --go.sum
main.go
先来看 服务端 代码, 非常简单,注册了 个 hello
路由, 然后简单校验了下 请求方法, header
, 和 body
内容, 若没问题则返回一个 "hello from hel
lo hand
ler
" 字符串。
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
脚本中我们定义了请求的各种参数, 使用wrk.method
, wrk.body
, wrk.headers
可以很轻易的设置请求方法, 请求体和请求参数。
在 WRK
中,我们是可以设置发起请求的线程数的,要统计 TPS
, 就得统计每一个线程收到的 成功返回的数量,然后将全部线程的成功返回数量相加,再除以总的响应时间, 就是TPS
。
在 wrk
中可以通过 setup
函数对每一个线程设置一些初始变量,比如我定义了一个 success_counter
用于统计成功的返回数, 初始化为0. 随后将这个 success_counter
注册到了 这个线程中,使用一个全局变量 threads
用于存储所有 线程(实际上只关注线程里面的 success_counter
)
在 wrk
中还可以通过 response
函数对每个 thread
的每一个请求的 response
进行获取,比如我根据 body
是否等于某个值来判断是否返回成功的结果, 若成功返回则进行 success_counter+1
. 事实上 setup
函数与 response
函数是 同一个 thread
里面的, 所以这个 success_counter
变量可以跨函数共享, 所以在 response
函数中+1, 整个 thread
中的 thread
变量也会+1. 并且 thread.set
操作的值是一个 指针, 所以无需再次调用thread:set("success_counter", success_counter)
即可完成 对 thread
对象的赋值操作。
在所有请求结束以后, wrk
会调用 done
函数获取到运行的结果。这里我们遍历了所有 threads
, 将里面成功的返回相加再除以总的响应时间, 即为接口的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
还内置别的函数 如 init
, request
, delay
, 这些都可以对每一个线程, 每一个请求做出更加细致的操作,有兴趣的同学可以自行查找如何使用。
运行起来
首先我们启动服务端, 确保在 wrk_demo
目录下运行
go run main.go
随后启动 wkr
:
./wrk -t 2 -c 4 -d 1s -s tps.lua http://localhost:9000/hello
-t 2
参数表示使用 2 个线程, -c 4
参数 表示总的并发连接数为4, 每一个 线程连接数 = 总的并发连接/线程数量, -d 1s
表示请求1秒, -s tps.lua
表示请求 1秒, 最后的 http://localhost:9000/hello
为 请求的 URL
运行结果如下:
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个并发, 每个 线程的平均延迟时间为 169.17us, 平均每秒请求数为10.65k, 还可以看到这两个指标的最大值, 标准差和正负标准差之间的比例。
总共在 1.01 秒内完成了 21167 次请求, 收到了服务端3.02MB的返回数据。平均每秒请求数为 20975.47, 每秒收到的服务端数据为 3.02MB(也称吞吐量)。总的成功返回数为 21167, TPS
为 20975.472956435。
现在我也找了很多测试的朋友,做了一个分享技术的交流群,共享了很多我们收集的技术文档和视频教程。
如果你不想再体验自学时找不到资源,没人解答问题,坚持几天便放弃的感受
可以加入我们一起交流。而且还有很多在自动化,性能,安全,测试开发等等方面有一定建树的技术大牛
分享他们的经验,还会分享很多直播讲座和技术沙龙
可以免费学习!划重点!开源的!!!
qq群号:485187702【暗号:csdn11】
最后感谢每一个认真阅读我文章的人,看着粉丝一路的上涨和关注,礼尚往来总是要有的,虽然不是什么很值钱的东西,如果你用得到的话可以直接拿走! 希望能帮助到你!【100%无套路免费领取】