wrk压力测试
GitHub - wg/wrk: Modern HTTP benchmarking tool
wrk 是一个比较先进的 HTTP 压力测试工具,当在单个多核 CPU 上运行时,能够产生大量负载。它结合了多线程设计和可扩展的事件通知系统,例如 epoll 和 kqueue。
可选的 LuaJIT 脚本可以执行 HTTP 请求生成,响应处理和自定义报告。
1. wrk工具编译 1 2 3 4 # https://github.com/tsliwowicz/go-wrk git clone https://github.com/wg/wrk.git cd wrk make
2. wrk参数说明 wrk -h
1 2 3 4 5 6 7 8 -c, --connections <N> 跟服务器建立并保持的TCP连接数量 -d, --duration <T> 压测时间 -t, --threads <N> 使用多少个线程进行压测,压测时,是有一个主线程来控制我们设置的n个子线程间调度 -s, --script <S> 指定Lua脚本路径 -H, --header <H> 为每一个HTTP请求添加HTTP头 --latency 在压测结束后,打印延迟统计信息 --timeout <T> 超时时间 -v, --version 打印正在使用的wrk的详细版本信
3. wrk使用 有关 ab 命令的使用,我们可以通过帮助命令进行查看。如下:
1 2 3 4 5 6 7 8 9 10 wrk -t8 -c400 -r10m http://localhost:8080/index.html Making 10000000 requests to http://localhost:8080/index.html 8 threads and 400 connections Thread Stats Avg Stdev Max +/- Stdev Latency 439.75us 350.49us 7.60ms 92.88% Req/Sec 61.13k 8.26k 72.00k 87.54% 10000088 requests in 19.87s, 3.42GB read Requests/sec: 503396.23 Transfer/sec: 176.16MB
一般线程数不宜过多. 核数的2到4倍足够了. 多了反而因为线程切换过多造成效率降低. 因为 wrk 不是使用每个连接一个线程的模型, 而是通过异步网络 io 提升并发量. 所以网络通信不会阻塞线程执行. 这也是 wrk 可以用很少的线程模拟大量网路连接的原因. 而现在很多性能工具并没有采用这种方式, 而是采用提高线程数来实现高并发. 所以并发量一旦设的很高, 测试机自身压力就很大. 测试效果反而下降.
参数解释:
12 threads and 100 connections
: 总共是12个线程,100个连接(不是一个线程对应一个连接)
latency
和Req/Sec
: 代表单个线程的统计数据,latency
代表延迟时间,Req/Sec
代表单个线程每秒完成的请求数,他们都具有平均值, 标准偏差, 最大值, 正负一个标准差占比。一般我们来说我们主要关注平均值和最大值. 标准差如果太大说明样本本身离散程度比较高. 有可能系统性能波动很大.
23725 requests in 30.05s, 347.47MB read
在30秒之内总共有23725个请求,总共读取347.47MB的数据
Socket errors: connect 0, read 48, write 0, timeout 50
总共有48个读错误,50个超时.
Requests/sec和Transfer/sec
所有线程平均每秒钟完成了789.57个请求,每秒钟读取11.56MB数据量
如果想看看响应时间的分布,可以增加--latency
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 wrk -t12 -c100 -d30s --latency http: Running 30 s test @ http: 8 threads and 200 connections (共8 个测试线程,200 个连接) Thread Stats Avg Stdev Max +/- Stdev (平均值) (标准差)(最大值)(正负一个标准差所占比例) Latency 46.67 ms 215.38 ms 1.67 s 95.59 % (延迟) Req/Sec 7.91 k 1.15 k 10.26 k 70.77 % (处理中的请求数) Latency Distribution (延迟分布) 50 % 2.93 ms 75 % 3.78 ms 90 % 4.73 ms 99 % 1.35 s (99 分位的延迟:%99 的请求在1.35 s以内) 1790465 requests in 30.01 s, 684.08 MB read (30.01 秒内共处理完成了1790465 个请求,读取了684.08 MB数据) Requests/sec: 59658.29 (平均每秒处理完成59658.29 个请求) Transfer/sec: 22.79 MB (平均每秒读取数据22.79 MB)
说明有50%的请求在2.93ms之内,90%在4.73ms之内。
高级用法 wrk可以结合lua来做,通过wrk提供的几个lua函数来对请求进行修改,结果输出、设置延迟等操作。下面来看看wrk提供的几个lua函数:
setup 函数 这个函数在目标 IP 地址已经解析完, 并且所有 thread 已经生成, 但是还没有开始时被调用. 每个线程执行一次这个函数. 可以通过thread:get(name), thread:set(name, value)设置线程级别的变量.
init 函数 每次请求发送之前被调用. 可以接受 wrk 命令行的额外参数. 通过 -- 指定.
delay函数 这个函数返回一个数值, 在这次请求执行完以后延迟多长时间执行下一个请求. 可以对应 thinking time 的场景.
request函数 通过这个函数可以每次请求之前修改本次请求的属性. 返回一个字符串. 这个函数要慎用, 会影响测试端性能.
response函数 每次请求返回以后被调用. 可以根据响应内容做特殊处理, 比如遇到特殊响应停止执行测试, 或输出到控制台等等.
1 2 3 4 5 6 7 8 9 10 11 12 wrk.method = "POST" wrk.headers["S-COOKIE2" ]="a=2&b=Input&c=10.0&d=20191114***" wrk.body = "recent_seven=20191127_32;20191128_111" wrk.headers["Host" ]="api.shouji.**.com" function response (status, headers, body) if status ~= 200 then print (body) wrk.thread:stop() end end
done函数 在所有请求执行完以后调用, 一般用于自定义统计结果.
1 2 3 4 5 6 7 done = function (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%%,%d\n" , p, n)) end end
wrk官网提供的setup.lua实例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 local counter = 1 local threads = {}function setup (thread) thread:set("id" , counter) table .insert (threads, thread) counter = counter + 1 end function init (args) requests = 0 responses = 0 local msg = "thread %d created" print (msg:format (id)) end function request () requests = requests + 1 return wrk.request() end function response (status, headers, body) responses = responses + 1 end function done (summary, latency, requests) for index, thread in ipairs (threads) do 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)) end end
使用setup.lua:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 [root@jerrik wrk] thread 1 created thread 2 created thread 3 created thread 4 created Running 20s test @ https://www.baidu.com 4 threads and 100 connections Thread Stats Avg Stdev Max +/- Stdev Latency 251.75ms 336.19ms 2.00s 86.89% Req/Sec 138.51 69.90 690.00 71.23% Latency Distribution 50% 215.74ms 75% 401.87ms 90% 664.84ms 99% 1.54s 11021 requests in 20.02s, 162.82MB read Socket errors: connect 0, read 3, write 0, timeout 50 Requests/sec: 550.62 Transfer/sec: 8.13MB thread 1 made 2945 requests and got 2919 responses thread 2 made 2831 requests and got 2807 responses thread 3 made 2772 requests and got 2747 responses thread 4 made 2573 requests and got 2548 responses [root@jerrik wrk]
将每个线程的请求数和响应数输出来了。其它更多使用可以参考github script目录下的lua脚本。
4.Post请求 编写lua脚本,填写post的数据, 如 post.lua
1 2 3 4 5 6 wrk.method = "POST" wrk.body = '{"userId": "10001","coinType": "GT","type": "2","amount": "5.1"}' wrk.headers["Content-Type" ] = "application/json" function request () return wrk.format ('POST' , nil , nil , body) end
执行wrk,开始压力测试:
1 wrk -t 16 -c 100 -d 30s --latency --timeout 5s -s post.lua http://localhost:8021/m/zh/order/new