简介
wrk 是一款针对 Http/Https 协议的基准测试工具,它能够在单机多核 CPU 的条件下,使用系统自带的高性能 I/O 机制,如 epoll,kqueue 等,通过多线程和事件模式,对目标机器产生大量的负载。
优势:
1, 安装简单,没什么依赖;
2,相比于ab,webbench ,jmeter等工具,wrk 更轻量;
3,学习曲线基本为零,简单了解一下使用参数就可以上手干活了;
4, 基于系统自带的高性能 I/O 机制,如 epoll, kqueue, 利用异步的事件驱动框架,通过很少的线程就可以压出很大的并发量;
5, 支持https (ab、webbench不支持);
6, 可以通过lua脚本定制测试过程,和测试配置,灵活性高。
劣势:
wrk 目前仅支持单机压测,后续也不太可能支持多机器对目标机压测,因为它本身的定位,并不是用来取代 JMeter, LoadRunner 等专业的测试工具,wrk 提供的功能,对我们后端开发人员来说,应付日常接口性能验证还是比较友好的。
总结:
wrk是一个轻量级的性能测试工具,上手简单,非常适合我们开发人员日常开发过程中的压测工作。
安装
注意:wrk 通常用在linux 类环境中,通过命令行操作;所以本文档也仅测试linux。
wrk 官方没有提供release包下载,所以推荐源码安装:
#安装编译环境(如果已经准备过,可以跳过)
sudo yum install -y git make gcc unzip
git clone https://github.com/wg/wrk.git (或者 https://gitee.com/geekyu/wrk.git )
cd wrk
make
编译完成后,在当前当前目录下,生成可执行文件 wrk:
可以将该执行文件复制到系统应用目录,即完成安装
sudo cp wrk /usr/sbin/
基本测试
参数解释:
-t 12 : 指明采用12个线程;
-c 200 : 设置整个测试过程,维持200个连接;
-d 30s : 设置整个测试过程,持续30秒钟。
注意: wrk采用的是多路IO复用的模型,所以线程不宜过多(推荐是cpu核数、线程数的2倍即可);过多的线程反而有很大的系统调度开销,极限情况下,影响测试结果。
输出结果解释:
12 threads and 200 connections:
总共是12个线程,200个连接(不是一个线程对应一个连接)。
latency和Req/Sec:
代表单个线程的统计数据,latency代表延迟时间,Req/Sec代表单个线程每秒完成的请求数。它们都具有平均值, 标准偏差, 最大值, 正负一个标准差占比。通常,我们关注平均值和最大值;标准差则反应了请求的波动性,在测试负载均衡、缓存命中等有很好的统计参考意义。
14544 requests in 30.09s, 149.58MB read:
在30秒之内总共完成14544个请求,总共读取149.58MB的数据。
Socket errors: connect 0, read 0, write 0, timeout 8
连接统计,有8次超时。
Requests/sec: 483.30
所有线程,平均每秒钟完成483.3个请求(QPS, 通常是反映服务端服务能力的一个重要指标)。
Transfer/sec: 4.97MB
所有线程,平均每秒钟读取4.97MB数据量。
通过脚本定制测试过程
先看一个官方的脚本例子 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
该脚本的用途和执行过程:
1, setup函数,在线程初始化时调用;向该线程运行空间中,添加一个本线程的标识ID;将当前线程object 记录到线程table中;增加线程数目统计。
2,init 函数,在线程执行时调用;初始化线程内request 统计和response统计;并log一下当前执行线程的标识ID。
3, requst函数,每一次请求调用一次,获取默认的请求数据;然后增加一次请求统计。
4, response函数,每次请求应答到来时调用一次;增加一次应答统计。
5, done函数,整个测试过程最后执行一次;输出各个线程的统计数据。
使用该脚本执行测试:
关于wrk脚本的生命周期和hook函数说明见官方文档:
wrk/SCRIPTING at master · wg/wrk · GitHub
个人总结而言,wrk脚本的生命周期,可以表示为:
同时,在整个测试过程中,系统提供了一个wrk 的全局对象,可以在hook函数中访问和配置:
// wrk 的默认值,是依据命令行参数的解析而设置的
// 在测试过程中,可以修改其中的内容,而达到全局修改的目的
wrk = {
scheme = "http",
host = "localhost",
port = nil,
method = "GET",
path = "/",
headers = {},
body = nil,
thread = <userdata>,
}
// wrk.format 方法,通常在测试流程的request中调用,用于生成一个预设的http请求;
// 为了提高测试效率,在需要动态生成请求或请求内容的测试中,建议在init阶段先预生成所有请求,通过table维护起来
// 测试过程中只需要顺序读取即可,以避免因为本地的逻辑运算,影响测试结果
function wrk.format(method, path, headers, body)
// wrk.lookup 主要用户预先解析服务端地址
// 域名通常存在缓存,如果在init阶段完成域名解析,可以大幅降低测试阶段的域名解析损耗(这个过程可能会很慢,达到秒级)
function wrk.lookup(host, service)
// 测试服务的连通性
function wrk.connect(addr)
enjoy!