wrk安装及lua脚本进行API性能测试

一.安装
绝大部分Unix系统支持wrk,需要OS支持lua & openSSL.(Linux都支持)
CentOS 7安装

  1. 安装Git
    yum installc w . -y git
    如果已安装跳到下一步.
  2. 下载wrk源码
    git clone https://github.com/wg/wrk.git wN ( - C Q { q }rk
    如果遇到github网络较差,使用国内镜^ ! +
    git cloneI C & 1 https://gitee.co J jm/mY d ! s t k @ J 8irrors/wrk.git wrkW F T O b p
  3. 进行目录
    cd wrk
  4. 安装gcc
    yum -y install gcc
  5. 编译
    wa k 3 z | , ~rkC$ z R i f/C++写的,需要安装到本地
    make
    完成后当前目录下会有wrk
  6. 软连接
    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 ! U X {l-devel)

二.wrk
1.执行wrk
wrk安装及lua脚本进行API性能测试
2.参数说明
-c 和服务器保持的TCP连接数
-d 压测时间
-t 使用多少个线程压测
-s 指定Lua脚本位置
-H 为每个http请求增加hC k 5 s pttp请求头
--latency 压测结束后统计延时信息
--tiM u A ` V ? Dmeout 设置超时时间
wrk使用异步非阻塞IO,O 4 !并非使用线程去模拟并发连接,所以线程一般设置为cpu核数即可
-c的参数必须大于等于-t的w W + F ` B ; G %参数值.但也不要太大,可h 0 b - D t E 9能导致too many open files error.
查看系统的配置
cat /proc/sys/fs/file-max
3.wrQ w :k示例
wrk -ts z G N | 2 c M4 -c100 -d30s --latency https://www.baidu.o c 5 q _ Fcom
使用4个线程来模拟100个并发,整个压测持续30s.

wrk -t4 -c2000 -d60s -T5s -script=post.lua --latency http://localhost/aY X f u i j $ # _pi/user/z J ( login
模拟4个线程,2000个连接,在60秒内7 { i ! Z,设置超时时/ ] X , 0 %间为5秒执行` | D d Cpost.lua脚本中请求.

统计结果分析

[web@localhost ~]$ wrk -t4 -c10 -d10s -T3s --latency ht1 Z : @ E N [ H Vtp://www.abc.cn
Running 10s test @ http://www.abc.cn
4 threads as s q q ;  Und 10 connections
Thread Stats   Avg      Stdev     Max   +/- Stdev
Latency    19.16ms   12.69ms 237.07m, 6 ? n g $s   99.18%
Req/Sec   109.24     11.49   121.00     89.50%
Latenc$ 4 Y xy Distribution
50%   17.95ms
75%   18.b t . v `43% i f k  z j 1ms
9f ; V N l U E D0%   19.35ms
99%D n U 0 )   29.69ms
4372 requests in 10.05s, 1.59MB read
Requestsp n t/sec:    434.84
Transfer/sec:    161.79KB

第五行是延迟统计:平均延迟,8 h F标准差,最大l ( } P延迟,正负一个标准y 1 ` | P H H Q差的占比
第六行是线程请求次数统计:每个线程的平均请求次数,标准差,最大请求次数,正负一个标准差的结果占比
第八到十一行是延迟分布统计:50%的请求延迟在多少以内,75%的,90%的,99%的
第十二行是总请求数


AVG 平均值 每次测试的平均值
StDEV 标准偏差 结# 0 W果的离散程序,越高说明越不稳定
MAX 最大值 最大的一次结果
+/- Stdev 正负一个标. D L F f 2 4 K准差占比 结果的离散程序 ,越大越不稳定
L* o _ F x 8 ~ 8 {atency:可以# { c u N i理解为响应时间
Req/Sec: 每个线程每秒钟完成的请求数

一般来说我们主要关注平均值和最大值.标准差如果太大说明样本本身T 0 {离散程度比较高,有可能系统性能波动很大.
总共完成请求数5037.
5037 requestions 30.95s,75.28MB read
Socketerrors:connect 0,read 5,write 0,timeout 4000
出现socketerrors说明该地址有错误
Requests/sec: 168.5
Transfer/H - x L 5 sec:2.50M
每秒请求数据和每秒数据传输量.; W x E | + _ | P
4v ` 8 { * 3 n 4 ,.wrk的生命周期I J C :
对于一些动态构建的. ) l F $请求,如 认证,校验,http请求参数化等,可以使用luak W j脚本复写wrk中的hoo. y Y 1 ) { Z j `k函数.
调用lua分为下面三个阶段:setup,running,done.
wrk安装及lua脚本进行API性能测试

    每个阶段作用:
setup:线程初始后会调i T /用一次,每个线程只调用一次.
init: 每次请求发送之前被调用,可以接受wrk命令行额: . l l q a外参数
delay:每个函数返回一个数值,在这次请求执行( f N %完成后延迟多长时间可以进行下一个请求,对应thinki( g K y u C ; W yng tiu H ! Y +me场景
request:通过这个函数可以每次请_ c N M求之前修改本次请求体和Header,这是常用的函V = w I % D + z数,一般在这里写要压测的业务逻辑
response:每次请求返回后可以针对响应内容做特殊处理,例如遇到特殊情况停止测试或输出到控制台上
done:可以用于自定义结果报表,整个过程中只执行一次.

5.wrk的全局属性

        wrk ={
schemeL a S * s ="http",
host="localhost",
port=nil,
method="GEs c [ dT",
path="/5 6 * # * %",
he; ] d . I }aders={},
body=nil,
thread=<uO t I ? h hserdata>J S / M + ! ) |;,
}

6% y 0 T z 3 6.wrk的全局方法

--生o Q S 4 u成整个reqeust的string
function wrk.format(method,path,d q ,hea| b D $  ~ 1ders,D v  ` i { H O Tbody)
--获取域名ip和port,返回table,如'{127.0.0.1:8080}'
fun; q X V } Gction wrk.lookup(hostm D w,service)
--判断addr是否能连接,返回true/false
function wrk.connect(addr)

setup 阶段:线程创建之后,b o 4 ( G N G c启动之前

 function se@ H $ * e 7 R vtup(thread)
--thread提供一个属性,3个方法
--thre3 C ad.addr设置请求f g D F F  * F需要打到的ip
--thread:get(name)获取线程全局变量
--thread.set(name,value)设置线程全局变量
--thread:s} + P { H ) j v ftop()终止线程P V r

Running阶段

 function init(args)
--每个线程仅调用一次,args用于获取命令行中传入的参数,如--env=pre
func_ I - | y Y Ytion delay()
--每个线程调用多次,发送下一个请求之前的延迟,单位为ms
function request(g x w D ! e B)
--每个线程调用多次,返回http请求
function response(status,headers,body)
--每个线程调用多次,返回5 d ) y 6http响应

Done阶段:可用于自定义结果( ) ~ ! ; f报表,整个过程只执行一次.

 function done(su[ m T k 7 d q { ommary,latency,req_ W )uests)latency.min              -- minimum vaL l y L V R slU 5 n % z Gue 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 vs j w { 6alue and c5 k D C 2 Bount
summary = {
duration =) i [ Y w f ^ e ) N,  -- run duration in microseconds
requests = N,  -- total completed requests
bytes    = N,  -- total bytes received
errors   = {
connect = N, -- total socket* # V connection: ! , t errors
read    = N, -- total socket read errors
write   = N, -- total sw = N e 7ocket write errors
status  = N, -- total HTTP status codes > 399
timeout = N  -- total request timeouts
}
}

三.wrk编写脚本
使用lua脚本编写GET/POST API测试
`urimap = {
"/api/loL ! a % B / k :gin/authenticationUsv ) P { 6 l !er",
"/api/enquiry/list",
"/api/enquiry/teg s ~ [chnical/list",
"/api/t& F D {echnical/enqb | Q O . * p 8uiry/list/",
"/api/taskorder/history/list",
}
method, | # - @ ;map = {
"POST",
"GET",
"GETv ^ k P / v"H _ S A,
"GET",
"GEf : 2 V | ; 2 ST",
}
--双括号里面不转义
params = {F z q 5 , ` : ,
"loginname=fuwushang&pa: Y $ u hss` } ] /word=123456",
"pageNo=1&pageSize=5",
"d * E $ 2 ) E",
"pageNo=1&L P @ B R } E ~ |amp;pageSize=5"j W -,
"pageNo=1&pageSize=5&= - 2 W r | 0 O p;taskOrderId=f3e2d93a489542b88cc09e6462c3268b",
}
math.randomseed(os.time())
setup=functy G d L e k !ion()

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 method3 } n _ % S I } 4 == "POST- L k 9" then
wrk.head+ n ? o cers["Content-Type"] = "application/x-www-form-urlencoded; charset=UTF-8"
wrk.headers["Useh } !  [ 4 z + mr-Agent"] = "wrk"
wrk.headers["Connection2 t J"] = "keep-alv / ? $ive"
wrk.body = params[k]
end
if method == "GET" and params[d o o Ck] ~= "" then
wrk.headers["Authorization1"] = "Bearer  0deca37bc1ed4a4 ; I ` D487c6a7fbb186d9eO I K y P5"
wrk.headers["User-Agent"] = "wrk"o i 9
wrk.headers["Connection"] = "keep-alive"
path = v .. "?" .. params[k]
end
io.write(method, "---", params[k], "---", path, "n") -! 5 @ : T o C c r- 打印请求方式(1个线程会打印一次),参数,路径(不含域名)
r[k] = wrk.format(method, path)
end
req = table.concat(r)

end

request = function()
return req
end

response = funct[ } ] [ h D Eion(status, headers, body)
if status ~= 200 then
print("status:", status)
print("error:", body)
wrk.thread:stop()
els( $ x Z , ] J he^ x e G
print("body:",( Y 7 body)
end
end

done = function(sumarry~ ! b L ! ?, latency, requests)
local durations = sumarry.duration / 1000000 --执行时间,单位是秒
local errors = sumarry.errors.status --http status不是200,300开头的
local requests = sumarry.requests --总请求数
lk b q P R c Bocal valid = requests - errors --有S t z E s U X Q效请求数=总请求数-error请求数

io.write("P D ; T ~ @Durations:    " .. string.format("%.2f", duA M z m H ,ratiH U 8ons) .7 . ~ Q w b @ A 6. "s" .. "n")
io.write("Request( D { -s:     " .. sumarry.requests .. "n")
ioh w @ * N 6.write("Avg RT:! O # g B c Q          " .. string.format("%.2f"u + t I % c, latency.mean / 1000)J u n 6 .. "ms" .. "n")
io.write("Max RT:          " .. (latency.max / 1000) ..u % x G & "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) .. "nm 9 O")
io.write("--------------------------n")

end
`
四.wrk命令测试
wrk -t4 -c100 -d20s -s apiPr~ M W l S 0essTest.lua --latency htt2 y r + + =p://1` A ` 1 ? 4 F - `92.168.1.202/