wrk是一款简单的HTTP压测工具,托管在Github上,https://github.com/wg/wrk.
wrk 的一个很好的特性就是能用很少的线程压出很大的并发量. 原因是它使用了一些操作系统特定的高性能 io 机制, 比如 select, epoll, kqueue 等. 其实它是复用了 redis 的 ae 异步事件驱动框架. 确切的说 ae 事件驱动框架并不是 redis 发明的, 它来至于 Tcl的解释器 jim, 这个小巧高效的框架, 因为被 redis 采用而更多的被大家所熟知.
git clone https://github.com/wg/wrk.git
cd wrk
make
如果报错:
src/wrk.h:11:25: fatal error: openssl/ssl.h: No such file or directory
#include
则需要安装openssl,使用sudo apt-get install libssl-dev或 sudo yum install openssl-devel安装即可,最后编辑etc/profile配置环境变量。由于笔者使用的是阿里云centos7,相关依赖都已经存在了,所以可以直接使用。
wrk -t12 -c100 -d30s http://www.baidu.com
[root@jerrik /]# wrk -t12 -c100 -d30s http://www.baidu.com
Running 30s test @ http://www.baidu.com
12 threads and 100 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 211.76ms 304.92ms 1.97s 88.17%
Req/Sec 72.93 68.72 797.00 90.97%
23725 requests in 30.05s, 347.47MB read
Socket errors: connect 0, read 48, write 0, timeout 50
Requests/sec: 789.57
Transfer/sec: 11.56MB
[root@jerrik /]#
一般线程数不宜过多. 核数的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:
wrk -t12 -c100 -d30s --latency http://www.baidu.com
[root@jerrik ~]# wrk -t12 -c100 -d30s --latency http://www.baidu.com
Running 30s test @ http://www.baidu.com
12 threads and 100 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 204.30ms 287.90ms 1.97s 88.61%
Req/Sec 71.43 67.59 810.00 89.77%
Latency Distribution
50% 14.76ms
75% 296.79ms
90% 545.03ms
99% 1.40s
23676 requests in 30.03s, 346.84MB read
Socket errors: connect 0, read 42, write 0, timeout 46
Requests/sec: 788.29
Transfer/sec: 11.55MB
说明有50%的请求在14.76ms之内,90%在545.03ms之内。
wrk可以结合lua来做,通过wrk提供的几个lua函数来对请求进行修改,结果输出、设置延迟等操作。下面来看看wrk提供的几个lua函数:
function response(status, headers, body)
if status ~= 200 then
print(body)
wrk.thread:stop()
end
end
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实例:
-- example script that demonstrates use of setup() to pass
-- data to and from the threads
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
[root@jerrik wrk]# wrk -t 4 -c 100 -d 20s --latency -s scripts/setup.lua https://www.baidu.com
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脚本。
wrk作为http压测还是非常简便的,但是要想应对更多复杂场景,就需要多熟悉lua的使用,深入了解wrk提供的那几个函数。其它http压测工具,jmeter,apache ab,siege也可以了解一下。
function done(summary, latency, requests)
The done() function receives a table containing result data, and two
statistics objects representing the per-request latency and per-thread
request rate. Duration and latency are microsecond values and rate is
measured in requests per second.
latency.min -- minimum value seen
latency.max -- maximum value seen
latency.mean -- average value seen
latency.stdev -- standard deviation
latency:percentile(99.0) -- 99th percentile value
latency(i) -- raw value and count
summary = {
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
}
}
-----------------------
print("latency.min:%s",latency.min)
print("latency.max:%s",latency.max)
print("latency.mean:%s",latency.mean)
print("latency.stdev:%s",latency.stdev)
print("latency:percentile(99.0):%s",latency:percentile(99.0))
print("requests.min:%s",requests.min)
print("requests.max:%s",requests.max)
print("requests.mean:%s",requests.mean)
print("requests.stdev:%s",requests.stdev)
print("requests:percentile(99.0):%s",requests:percentile(99.0))
print("summary['duration']:%s",summary["duration"])
print("summary['requests']:%s",summary["requests"])
print("summary['bytes']:%s",summary["bytes"])
print("summary['errors'].connect:%s",summary['errors'].connect])
print("summary['errors'].read:%s",summary['errors'].read])
print("summary['errors'].write:%s",summary['errors'].write])
print("summary['errors'].status:%s",summary['errors'].status])
print("summary['errors'].timeout:%s",summary['errors'].timeout])
-c, --connections(连接数): total number of HTTP connections to keep open with each thread handling N = connections/threads
-d, --duration(测试持续时间): duration of the test, e.g. 2s, 2m, 2h
-t, --threads(线程): total number of threads to use
-s, --script(脚本): LuaJIT script, see SCRIPTING
-H, --header(头信息): HTTP header to add to request, e.g. "User-Agent: wrk"
--latency(响应信息): print detailed latency statistics
--timeout(超时时间): record a timeout if a response is not received within this amount of time.
Running 30s test @ http://127.0.0.1:8080/index.html
threads and 400 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 635.91us 0.89ms 12.92ms 93.69%
Req/Sec 56.20k 8.07k 62.00k 86.54%
requests in 30.00s, 17.76GB read
Requests/sec: 748868.53
Transfer/sec: 606.33MB
结果解读如下:
Latency: 响应信息, 包括平均值, 标准偏差, 最大值, 正负一个标准差占比。
Req/Sec: 每个线程每秒钟的完成的请求数,同样有平均值,标准偏差,最大值,正负一个标准差占比。
30秒钟总共完成请求数为22464657,读取数据量为17.76GB。
线程总共平均每秒钟处理748868.53个请求(QPS),每秒钟读取606.33MB数据量。