wrk
- github项目地址: https://github.com/wg/wrk
- 可以配合lua脚本使用
wrk参数
1 | Usage: wrk <options> <url> |
-c 表示并发连接数
-d 持续时间,如果不填写,默认为10s
-t 线程数
-s 指定lua脚本
-H 指定请求头
–latency 最终输出阶段打印出latency信息
–timeout 设置超时时间
-v 查看工具版本
wrk压测样例
1 | huangyisan@k3s-master:~/learn-lua/wrk$ wrk -t1 -c3 -d1s --timeout 1s --latency https://www.baidu.com |
输出解释:
- 第一行: 表示请求https://www.baidu.com 持续1s
- 第二行: 表示1个线程3个并发连接数
- 第三行:
- Thread Stats:表示类型字段
- Avg:单个线程的平均值
- Stdev:单个线程标准偏差
- Max:单个线程最大值
- +/- Stdev:标准偏差以百分数形式表示
- 第四行:表示请求延迟,越小越好
- 第五行:表示每秒请求数,越大越好
- 第六行:唯有在命令行加上–latency 参数才有
- 第七行至第十行:表示了延迟数据分布,50%的请求在81.64ms中完成,这里面包含了Coordinated Omission修正,所以延迟会比服务端打点来的大,因为其包含了用户请求发送的等待时间,具体可以参考:https://github.com/giltene/wrk2 https://zhuanlan.zhihu.com/p/43602062
- 第十一行:表示了在固定的1s内完成了25个请求,数据读取414.54KB
- 第十二行:表示所有请求的均值每秒数据,这是一个重要的指标。req/sec和request/sec的差别https://github.com/wg/wrk/issues/259
- 第十三行:每秒传输的数据量
wrk跟lua脚本配合使用
- 引入lua脚本,可以使压测的行为更加复杂,比如给每个线程创建不同的body,进行压测。
- 通过-s可以指定传入的脚本名称。
lua脚本中可以获取到wrk的变量
- scheme
- host
- port
- method
- path
- headers
- body
- thread
1 | function setup(thread) |
wrk运行阶段
wrk的运行可以分为三个阶段,在这三个阶段可以通过lua编写对应的方法,来达到”嵌入”行为的目的。
Step阶段,该阶段解析了被压测的地址,并且初始化好了所有的线程,处于等待开始阶段,所以这个阶段编写的脚本的执行开销,其实是不会算入结论输出里面的。在lua脚本定义setup函数,传入
thread
参数。thread也有自己的一些属性和方法
- thread.addr,获取当前thread访问的地址,以ip:port的形式
- thread:set(key, value),在线程里面设定key,value
- thread:get(key),指定key获取thread环境里的对应的value
- thread:stop(),暂停thread
1
2
3
function setup(thread)
end
Running阶段,该阶段又可以细分为四个阶段。
init阶段,该阶段可以接受wrk命令传入的额外参数,只需要将额外参数跟随在最末尾,空格分隔即可。在lua脚本定义init函数,传入args参数,当有额外参数传入的时候,args为一个table,可以使用pairs迭代获取其value。
1
2
3
4
5
6function init(args)
for i,v in pairs(args)
do
print(i,v)
end
end指定脚本后传入参数打印能看到如下输出,index为0的是压测地址,1为bb,2为cc
1
2
3
4
5
6
7
8
9
10
11huangyisan@k3s-master:~/learn-lua/wrk$ wrk -t1 -c1 -d1s --timeout 1s -s init.lua --latency https://www.baidu.com bb cc
0 https://www.baidu.com
1 bb
2 cc
Running 1s test @ https://www.baidu.com
1 threads and 1 connections
.....
....
...
..
.delay阶段,该阶段表示两次请求延迟间隔,函数返回一个毫秒为单位的数字。在lua脚本定义delay函数,返回数字,该数字单位为毫秒。
1
2
3function delay()
return 200
end脚本表示延迟间隔为200ms,当指定延迟脚本和不指定延迟脚本得到的结果很明显,指定了延迟脚本的结果Requests/sec明显下降了。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20huangyisan@k3s-master:~/learn-lua/wrk$ wrk -t1 -c2 -d2s https://www.baidu.com
Running 2s test @ https://www.baidu.com
1 threads and 2 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 85.89ms 25.21ms 185.38ms 77.78%
Req/Sec 23.17 7.68 40.00 83.33%
44 requests in 2.01s, 673.82KB read
Requests/sec: 21.91
Transfer/sec: 335.56KB
huangyisan@k3s-master:~/learn-lua/wrk$ wrk -t1 -c2 -d2s -s delay.lua https://www.baidu.com
Running 2s test @ https://www.baidu.com
1 threads and 2 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 53.43ms 8.35ms 81.61ms 92.86%
Req/Sec 7.00 2.33 10.00 62.50%
14 requests in 2.01s, 214.40KB read
Requests/sec: 6.96
Transfer/sec: 106.53KBrequest阶段,该阶段可以对请求进行构造,比如修改请求头,修改body等,最后需要返回wrk.format()**,建议将一些可以不在该阶段的修改代码放到init()方法里面,让request腾出更多的时间去完成压测请求。一个完整的wrk.format()函数传入的参数依次对应为
method, path, headers, body
。在lua脚本里面定义request**函数函数即可。如果在function里面并不是修改全局wrk的属性,则需要传入,否则可以不传入,比如下面wrk.method被修改了,则不需要传入,而path为函数内本地属性,需要传入。1
2
3
4
5function request()
path = "/?x"
wrk.method = "POST"
return wrk.format(nil,path)
endresponse阶段,该阶段可以处理请求返回的status,headers,body。在lua脚本中定义response函数,传入的参数分别为
status,headers,body
。1
2
3
4
5
6
7function response(status,headers,body)
for _,v in pairs(headers)
do
print(v)
end
print(status)
end
Done阶段,该阶段以table的形式接受结果,以及表示每个请求延迟和每线程请求率的两个统计对象,此时可以根据自己的算法需求来编写最后的输出结果。在lua脚本中定义done函数,传入参数分别为
summary, latency, requests
。其对象有如下这些:
latency.min 最小值
latency.max 最大值
latency.mean 平均值
latency.stdev 标准偏差
latency:percentile(num) 指定数字百分比的数值
latency(i) : raw value and count
summary:
1
2
3
4
5
6
7
8
9
10
11
12summary = {
duration = N, -- run duration in microseconds
requests = N, -- total completed requests
bytes = N, -- total bytes received
errors = {
connect = N, -- total socket connection errors
read = N, -- total socket read errors
write = N, -- total socket write errors
status = N, -- total HTTP status codes > 399
timeout = N -- total request timeouts
}
}自定义输出lua脚本
1
2
3
4
5
6
7
8
9
10
11function done(summary, latency, requests)
io.write("------------------------------\n")
for _, p in pairs({ 50, 90, 99, 99.999 }) do
n = latency:percentile(p)
io.write(string.format("%g%%,%dms\n", p, n/1000))
end
io.write("--------error requets count--------\n")
print(summary["errors"]["status"])
end1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23huangyisan@k3s-master:~/learn-lua/wrk$ wrk -t1 -c2 -d2s -s done.lua --latency https://www.baidu.com
Running 2s test @ https://www.baidu.com
1 threads and 2 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 86.47ms 17.21ms 130.38ms 79.07%
Req/Sec 22.26 8.53 40.00 78.95%
Latency Distribution
50% 86.63ms
75% 96.13ms
90% 101.42ms
99% 130.38ms
43 requests in 2.01s, 658.52KB read
Requests/sec: 21.39
Transfer/sec: 327.57KB
------------------------------
,86ms
,101ms
,130ms
99.999%,130ms
--------error requets count--------
0
自定义了输出,打印了50, 90, 90, 99.999百分比的标准偏差,同时打印了请求错误的数量。
wkr另外两个函数
function wrk.lookup(host, service)
返回主机地址和服务列表。
function wrk.connect(addr)
连接指定addr地址,如果能连接,则返回true
,反之则false
。
这两个函数并不在生命周期中,但可以在生命周期阶段被调用,例如:https://github.com/wg/wrk/blob/master/scripts/addr.lua
refer
https://medium.com/@felipedutratine/intelligent-benchmark-with-wrk-163986c1587f
https://programmer.ink/think/quick-start-for-the-http-pressure-tool-wrk.html