一.安装
绝大部分Unix系统都支持wrk,需要OS支持lua & openSSL.(Linux都支持)
CentOS 7安装
- 安装Git
yum install -y git
如果已安装跳到下一步. - 下载wrk源码
git clone https://github.com/wg/wrk.git wrk
如果遇到github网络较差,使用国内镜像
git clone https://gitee.com/mirrors/wrk.git wrk - 进行目录
cd wrk - 安装gcc
yum -y install gcc - 编译
wrkC/C++写的,需要安装到本地
make
完成后当前目录下会有wrk -
软连接
ln -s ~/wrk/wrk /usr/local/bin如遇到编译错误:fatalerror:openssl/ssl.h:Nosuchfile or directory yum -y install openssl-devel (Ubuntu中 apt-get install -y openssl-devel)
二.wrk
1.执行wrk
2.参数说明
-c 和服务器保持的TCP连接数
-d 压测时间
-t 使用多少个线程压测
-s 指定Lua脚本位置
-H 为每个http请求增加http请求头
--latency 压测结束后统计延时信息
--timeout 设置超时时间
wrk使用异步非阻塞IO,并非使用线程去模拟并发连接,所以线程一般设置为cpu核数即可
-c的参数必须大于等于-t的参数值.但也不要太大,可能导致too many open files error.
查看系统的配置
cat /proc/sys/fs/file-max
3.wrk示例
wrk -t4 -c100 -d30s --latency https://www.baidu.com
使用4个线程来模拟100个并发,整个压测持续30s.
wrk -t4 -c2000 -d60s -T5s -script=post.lua --latency http://localhost/api/user/login
模拟4个线程,2000个连接,在60秒内,设置超时时间为5秒执行post.lua脚本中请求.
统计结果分析
[web@localhost ~]$ wrk -t4 -c10 -d10s -T3s --latency http://www.abc.cn
Running 10s test @ http://www.abc.cn
4 threads and 10 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 19.16ms 12.69ms 237.07ms 99.18%
Req/Sec 109.24 11.49 121.00 89.50%
Latency Distribution
50% 17.95ms
75% 18.43ms
90% 19.35ms
99% 29.69ms
4372 requests in 10.05s, 1.59MB read
Requests/sec: 434.84
Transfer/sec: 161.79KB
第五行是延迟统计:平均延迟,标准差,最大延迟,正负一个标准差的占比
第六行是线程请求次数统计:每个线程的平均请求次数,标准差,最大请求次数,正负一个标准差的结果占比
第八到十一行是延迟分布统计:50%的请求延迟在多少以内,75%的,90%的,99%的
第十二行是总请求数
AVG 平均值 每次测试的平均值
StDEV 标准偏差 结果的离散程序,越高说明越不稳定
MAX 最大值 最大的一次结果
+/- Stdev 正负一个标准差占比 结果的离散程序 ,越大越不稳定
Latency:可以理解为响应时间
Req/Sec: 每个线程每秒钟完成的请求数
一般来说我们主要关注平均值和最大值.标准差如果太大说明样本本身离散程度比较高,有可能系统性能波动很大.
总共完成请求数5037.
5037 requestions 30.95s,75.28MB read
Socketerrors:connect 0,read 5,write 0,timeout 4000
出现socketerrors说明该地址有错误
Requests/sec: 168.5
Transfer/sec:2.50M
每秒请求数据和每秒数据传输量.
4.wrk的生命周期
对于一些动态构建的请求,如 认证,校验,http请求参数化等,可以使用lua脚本复写wrk中的hook函数.
调用lua分为下面三个阶段:setup,running,done.
每个阶段作用:
setup:线程初始后会调用一次,每个线程只调用一次.
init: 每次请求发送之前被调用,可以接受wrk命令行额外参数
delay:每个函数返回一个数值,在这次请求执行完成后延迟多长时间可以进行下一个请求,对应thinking time场景
request:通过这个函数可以每次请求之前修改本次请求体和Header,这是常用的函数,一般在这里写要压测的业务逻辑
response:每次请求返回后可以针对响应内容做特殊处理,例如遇到特殊情况停止测试或输出到控制台上
done:可以用于自定义结果报表,整个过程中只执行一次.
5.wrk的全局属性
wrk ={
scheme ="http",
host="localhost",
port=nil,
method="GET",
path="/",
headers={},
body=nil,
thread=,
}
6.wrk的全局方法
--生成整个reqeust的string
function wrk.format(method,path,headers,body)
--获取域名ip和port,返回table,如'{127.0.0.1:8080}'
function wrk.lookup(host,service)
--判断addr是否能连接,返回true/false
function wrk.connect(addr)
setup 阶段:线程创建之后,启动之前
function setup(thread)
--thread提供一个属性,3个方法
--thread.addr设置请求需要打到的ip
--thread:get(name)获取线程全局变量
--thread.set(name,value)设置线程全局变量
--thread:stop()终止线程
Running阶段
function init(args)
--每个线程仅调用一次,args用于获取命令行中传入的参数,如--env=pre
function delay()
--每个线程调用多次,发送下一个请求之前的延迟,单位为ms
function request()
--每个线程调用多次,返回http请求
function response(status,headers,body)
--每个线程调用多次,返回http响应
Done阶段:可用于自定义结果报表,整个过程只执行一次.
function done(summary,latency,requests)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
}
}
三.wrk编写脚本
使用lua脚本编写GET/POST API测试
`urimap = {
"/api/login/authenticationUser",
"/api/enquiry/list",
"/api/enquiry/technical/list",
"/api/technical/enquiry/list/",
"/api/taskorder/history/list",
}
methodmap = {
"POST",
"GET",
"GET",
"GET",
"GET",
}
--双括号里面不转义
params = {
"loginname=fuwushang&password=123456",
"pageNo=1&pageSize=5",
"",
"pageNo=1&pageSize=5",
"pageNo=1&pageSize=5&taskOrderId=f3e2d93a489542b88cc09e6462c3268b",
}
math.randomseed(os.time())
setup=function()
end
init = function()
local r = {}
local path = "" --局部变量(不加local是全局变量)
local method = "get" --default get
--header
wrk.headers["Hash"] = "hashcode"
wrk.headers["Token"] = "uuidtoken"
for k, v in pairs(urimap) do --k 从1开始,非0
path = v --path
method = methodmap[k] --method
if method == "POST" then
wrk.headers["Content-Type"] = "application/x-www-form-urlencoded; charset=UTF-8"
wrk.headers["User-Agent"] = "wrk"
wrk.headers["Connection"] = "keep-alive"
wrk.body = params[k]
end
if method == "GET" and params[k] ~= "" then
wrk.headers["Authorization1"] = "Bearer 0deca37bc1ed4a4487c6a7fbb186d9e5"
wrk.headers["User-Agent"] = "wrk"
wrk.headers["Connection"] = "keep-alive"
path = v .. "?" .. params[k]
end
io.write(method, "---", params[k], "---", path, "\n") -- 打印请求方式(1个线程会打印一次),参数,路径(不含域名)
r[k] = wrk.format(method, path)
end
req = table.concat(r)
end
request = function()
return req
end
response = function(status, headers, body)
if status ~= 200 then
print("status:", status)
print("error:", body)
wrk.thread:stop()
else
print("body:", body)
end
end
done = function(sumarry, latency, requests)
local durations = sumarry.duration / 1000000 --执行时间,单位是秒
local errors = sumarry.errors.status --http status不是200,300开头的
local requests = sumarry.requests --总请求数
local valid = requests - errors --有效请求数=总请求数-error请求数
io.write("Durations: " .. string.format("%.2f", durations) .. "s" .. "\n")
io.write("Requests: " .. sumarry.requests .. "\n")
io.write("Avg RT: " .. string.format("%.2f", latency.mean / 1000) .. "ms" .. "\n")
io.write("Max RT: " .. (latency.max / 1000) .. "ms" .. "\n")
io.write("Min RT: " .. (latency.min / 1000) .. "ms" .. "\n")
io.write("Error requests: " .. errors .. "\n")
io.write("Valid requests: " .. valid .. "\n")
io.write("QPS: " .. string.format("%.2f", valid / durations) .. "\n")
io.write("--------------------------\n")
end
`
四.wrk命令测试
wrk -t4 -c100 -d20s -s apiPressTest.lua --latency http://192.168.1.202/