同 ab 这种单线程 HTTP 性能测试工具相比,wrk 是一个足够现代化的 HTTP 性能测试工具,最重要的特性是:它是可编程的,借助内嵌 lua,我们可以控制测试的全过程。
关于 wrk 中 lua 扩展的数据结构,可以参考官方源代码中的 wrk.lua 文件:
local wrk = {
scheme = "http",
host = "localhost",
port = nil,
method = "GET",
path = "/",
headers = {},
body = nil,
thread = nil,
}
此外,还有一些钩子方法可供使用:
多数情况下,我们只要关注 request 钩子方法即可,通过它我们可以自定义请求的各个参数,如果想要了解更多的用法,可以参考官方源代码的 scripts 目录。
让我们动手实战一下,假设一个网站,主要的请求有三种,分别是:
结合前面提到的 wrk 中 lua 扩展的相关知识,我们可以实现如下代码:
-- benchmark.lua
math.randomseed(os.time())
local config = {
{num=20, path="/a"},
{num=30, method="get", path="/b"},
{num=50, method="post", path="/c", body="foo=x&bar=y"},
}
local requests = {}
for i, request in ipairs(config) do
if request.method then
request.method = string.upper(request.method)
end
for _ = 1, request.num do
requests[#requests + 1] = i
end
end
local length = #requests
for _ = 1, length do
local m, n = math.random(length), math.random(length)
requests[m], requests[n] = requests[n], requests[m]
end
local count = 0
function request()
local i = (count % length) + 1
local request = config[requests[i]]
count = count + 1
return wrk.format(
request.method,
request.path,
request.headers,
request.body
)
end
代码逻辑很简单,无非就是根据配置信息生成一个大数组,然后把数据随机化一下,每个请求来的时候,根据计数器直接给一条数据即可。
我在我的笔记本上以此脚本为例实际跑了一个 100 并发的例子,这里有个题外话需要提一下,很多人做 benchmark 只关注 rps,却忽略了 latency,这是不严谨的,设想一个网站的 rps 数据很好,但是总有一定百分比的请求出现高 latency,依然是有问题的:
shell> wrk -c 100 -s ./benchmark.lua http://localhost
Running 10s test @ http://localhost
2 threads and 100 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 4.26ms 1.04ms 23.95ms 94.87%
Req/Sec 11.85k 662.52 13.08k 67.50%
235787 requests in 10.00s, 71.95MB read
Non-2xx or 3xx responses: 0
Requests/sec: 23573.71
Transfer/sec: 7.19MB
在测试的时候我顺手用 ngrep 监控了一下请求:
ngrep -W byline ” ‘dst port 80’
如图可见,wrk 随机发送了不同的请求,完美!