前言
wrk 是一个开源的、热门的、古代的单机 HTTP 基准测试工具,目前在 github 开源平台累计了 26.9k 的 star 数目,足以可见 wrk 在 Http 基准测试畛域的热门水平。它联合了多线程设计和可扩大的事件告诉零碎,如 epoll 和 kqueue,能够在无限的资源下并收回极致的的负载申请。并且内置了一个可选的 LuaJIT 脚本执行引擎,能够解决简单的 HTTP 申请生成、响应解决以及自定义压测报告。
wrk 我的项目地址:https://github.com/wg/wrk
装置 wrk
mac 下装置:
brew install wrk
其余平台参考:https://github.com/wg/wrk/wiki
根底应用
wrk -t12 -c100 -d30s --latency http://localhost:8010/healthz
如上指令形容了采纳 12 个线程,100 个链接,针对 /healthz 接口服务,继续压测 30s。wrk 自身不是依赖线程数来模仿并发数的所以线程数量设置在外围数左右最好,线程数多了测试零碎耗费大,可能带来反成果。亲测外围数统一的线程数和两倍外围数的线程数,前者压出的 QPS 更高。压测后果如下:
Running 30s test @ http://localhost:8010/healthz(运行 30s 测试)12 threads and 100 connections(12 个线程 100 个连贯)Thread Stats Avg(均值) Stdev(规范差值) Max(最大值) +/- Stdev(正负规范差值)
Latency(提早) 1.39ms 668.10us 23.95ms 90.34%
Req/Sec(每秒申请数) 5.44k 545.23 10.27k 76.47%
Latency Distribution(提早直方图)
50% 1.32ms (50% 申请提早在 1.32ms 内)
75% 1.49ms (75% 申请提早在 1.49ms 内)
90% 1.72ms (90% 申请提早在 1.72ms 内)
99% 4.77ms (99% 申请提早在 4.77ms 内)
1952790 requests in 30.08s, 271.90MB read (共 1952790 次申请,用时 30s,传输了 271.9M 数据)
Requests/sec(每秒申请数): 64930.12
Transfer/sec(每秒传输数据): 9.04MB
wrk 的后果相比 ab 测试后果来说,多了一个延时直方图,有了这个直方图,咱们能够更清晰的看到提早的散布状况。这也是博主抉择 wrk 最重要的起因
罕用指令阐明
-c, --connections: 要放弃关上的 HTTP 连贯的总数,每个线程解决数 N = 连贯 / 线程
-d, --duration: 测试持续时间, 如 2s, 2m, 2h
-t, --threads: 测试线程总数
-s, --script: 指定加载 lua 测试扩大脚本
-H, --header: 增加申请头信息, 如 "User-Agent: wrk"
--latency: 打印提早直方图信息
--timeout: 如果在此工夫内没有收到响应,则记录超时.
- 结尾的指令为简写的,前面两个打印提早直方图和超时设置没有简写的,只能 – 结尾指定
高阶用法,lua 测试脚本
wrk 内置了全局变量,全局办法,以及五个测试申请发动流程的办法,还有一个模仿提早发送的办法,wrk 是内置对象,在 lua 测试脚本的每个办法内都能够间接应用
- 全局变量
-- 全局的变量
wrk = {
scheme = "http",
host = "localhost",
port = nil,
method = "GET",
path = "/",
headers = {},
body = nil,
thread = userdata,
}
- 全局办法
-- 返回申请字符串值,其中蕴含所传递的参数和来自 wrk 表的值。例如:返回 http://www.kailing.pub
function wrk.format(method, path, headers, body);
-- 获取域名的 IP 和端口,返回 table,例如:返回 `{127.0.0.1:80}`
function wrk.lookup(host, service)
-- 判断 addr 是否能连贯,例如:`127.0.0.1:80`,返回 true 或 false
function wrk.connect(addr)
- 申请过程办法
-- 申请前,对每个线程调用一次,并接管示意该线程的 userdata 对象。function setup(thread)
thread.addr = "http://www.kailing.pub" -- 设置申请的地址
thread:get("name") -- 获取全局变量的值
thread:set("name", "kl") -- 在线程的环境中设置全局变量的值
thread:stop() -- 进行线程
end
-- 初始化,每个线程执行一次
function init(args) --args 为从命令行传过来的额定参数
print(args)
end
-- 发动申请,每次申请执行一次,返回蕴含 HTTP 申请的字符串。每次构建新申请的开销都很大,在测试高性能服务器时,-- 一种解决方案是在 init() 中事后生成所有申请,并在 request() 中进行疾速查找。function request()
requests = requests + 1
return wrk.request()
end
-- 响应解决,每次申请执行一次
function response(status, headers, body)
responses = responses + 1
end
-- 申请实现,每次测试执行一次。done() 函数接管一个蕴含后果数据的表和两个统计数据对象,别离示意每个申请提早和每个线程申请速率。-- 持续时间和提早是微秒值,速率是以每秒申请数来度量的。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
整个脚本处理过程被分为筹备阶段、运行阶段、实现阶段。筹备阶段在指标 IP 地址被解析并且所有线程都曾经初始化但还没有启动之后开始。运行阶段从对 init() 的单个调用开始,而后对每个申请周期调用 request() 和 response()。init() 函数接管脚本的任何额定命令行参数,这些参数必须用“——”与 wrk 参数分隔开。
lua 测试脚本案例剖析
案例:咱们线上有一个带缓存场景的接口服务,依据 appId 的值的查问后果缓存,所以,如果单纯对指定的 appId 压测,就变成了测试缓存零碎的负载了,测试不出理论的服务性能,这个场景就须要测试工具发动每次申请的测试参数都是动静的。依据这个场景咱们定制了如下的 lua 测试脚本:
-- 测试指令:wrk -t16 -c100 -d5s -sreview_digress_list.lua --latency htt://127.0.0.1:8081
wrk.method ="GET"
wrk.path = "/app/{appId}/review_digress_list"
function request()
-- 动静生成每个申请的 url
local requestPath = string.gsub(wrk.path,"{appId}",math.random(1,10))
-- 返回申请的残缺字符串:http://127.0.0.1//app/666/review_digress_list
return wrk.format(nil, requestPath)
end