价值说明: 利用wrk 2分钟知晓你新开发的接口的基本性能
一、简介
wrk是一个开源的、热门的、现代的单机HTTP基准测试工具。它能够在单机多核 CPU 的条件下,使用系统自带的高性能 I/O 机制,如 epoll,kqueue 等,通过多线程和事件模式,对目标机器产生大量的负载。
PS: 其实,wrk 是复用了 redis 的 ae 异步事件驱动框架,准确来说 ae 事件驱动框架并不是 redis 发明的, 它来至于 Tcl 的解释器 jim, 这个小巧高效的框架, 因为被 redis 采用而被大家所熟知。
并且内置了一个可选的Lua JIT脚本执行引擎,可以处理复杂的HTTP请求生成、响应处理以及自定义压测报告。
二、跟其他压测工具优缺点对比
一些常用的性能测试工具,如 Apache ab, Apache JMeter (互联网公司用的较多),LoadRunner 等。
wrk 的优势:
*轻量级性能测试工具; wrk的结果相比ab测试结果来说,多了一个延时直方图,有了这个直方图,我们可以更清晰的看到延迟的分布情况。这也是博主选择wrk最重要的原因
* 安装简单(相对 Apache ab 来说);
* 学习曲线基本为零,几分钟就能学会咋用了;
*基于系统自带的高性能 I/O 机制,如 epoll, kqueue, 利用异步的事件驱动框架,通过很少的线程就可以压出很大的并发量;
劣势
wrk 目前仅支持单机压测,后续也不太可能支持多机器对目标机压测,因为它本身的定位,并不是用来取代 JMeter, LoadRunner 等专业的测试工具,wrk 提供的功能,对我们后端开发人员来说,应付日常接口性能验证还是比较友好的。
结论:应付日常接口性能验证还是比较友好的
三、Mac上安装
先安装Homebrew,安装方式参考官网 https://brew.sh
也可以执行下面命令 一键安装 > /bin/bash -c "$(curl -fsSLhttps://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
安装wrk: > brew install wrk
验证是否安装成功
> wrk -v
四、常用命令、注释和结果分析
命令: > wrk -t12 -c400 -d30shttp://www.baidu.com
这条命令表示,利用 wrk 对 www.baidu.com 发起压力测试,线程数为 12,模拟 400 个并发请求,持续 30 秒。
详细参数含义解析
常用指令说明
-c, --connections: 要保持打开的HTTP连接的总数,每个线程处理数N =连接/线程
-d, --duration: 测试持续时间, 如 2s, 2m, 2h
-t, --threads: 测试线程总数
-s, --script: 指定加载lua测试扩展脚本
-H, --header: 添加请求头信息, 如"User-Agent: wrk"
--latency: 打印延迟直方图信息
--timeout: 如果在此时间内没有收到响应,则记录超时.
-开头的指令为简写的,后面两个打印延迟直方图和超时设置没有简写的,只能--开头指定
命令行中输入 wrk --help, 可以看到支持以下子命令:
结果分析:
五、Get请求如何进行压测
-- 测试指令:wrk -t16 -c100 -d5s -s review_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
六、Post请求如何进行压测
6.1、Post请求
wrk默认是采用GET请求方式进行接口测试,如果需要使用POST请求就需要使用到lua脚本,通过加载编写好的lua脚本来进行定制化的请求。采用-s 或者 --script可以加载脚本文件。
wrk的请求构造过程主要是针对每一个线程的,所以对于压测的环境也提供了一些函数方法来进行支持线程的环境定制化。
对于POST请求+入参不一致的解决:
对于这种需求,我们可以通过编写 Lua 脚本的方式,在运行压测命令时,通过参数 --script 来指定 Lua 脚本,来满足个性化需求。
wrk 对 Lua 脚本的支持
wrk 支持在三个阶段对压测进行个性化,分别是启动阶段、运行阶段和结束阶段。每个测试线程,都拥有独立的Lua 运行环境。
启动阶段
在脚本文件中实现 setup 方法,wrk 就会在测试线程已经初始化,但还没有启动的时候调用该方法。wrk会为每一个测试线程调用一次 setup 方法,并传入代表测试线程的对象 thread 作为参数。setup 方法中可操作该 thread 对象,获取信息、存储信息、甚至关闭该线程。
运行阶段
* init(args): 由测试线程调用,只会在进入运行阶段时,调用一次。支持从启动 wrk 的命令中,获取命令行参数;
* delay(): 在每次发送请求之前调用,如果需要定制延迟时间,可以在这个方法中设置;
* request(): 用来生成请求,每一次请求都会调用该方法,所以注意不要在该方法中做耗时的操作;
* response(status, headers, body): 在每次收到一个响应时被调用,为提升性能,如果没有定义该方法,那么wrk不会解析 headers 和 body;
结束阶段
done() 方法在整个测试过程中只会被调用一次,我们可以从给定的参数中,获取压测结果,生成定制化的测试报告。
6.2、lua脚本
首先下载lua脚本 把接口+入参替换为对应值
-- example script that demonstratesuseof setup() to pass-- data toandfrom the threadslocalcounter =1localthreads = {}function setup(thread)thread:set("id", counter) -- 设置线程id table.insert(threads, thread) -- 用threads的表格存储counter = counter +1-- 为避免id相同,使用counter来实现递增endfunction init(args) -- 注意init中初始化的值都是针对一个线程而言的!即每个线程都是隔离的requests =0-- 初始化,注意这里的初始化的变量都将成为wrk的全局变量,即后续可以直接用responses =0localmsg ="thread %d created"print(msg:format(id))end-- 全局设定 设置请求格式--wrk.method ="POST"--wrk.body ="name=zhangsan&password=123456"wrk.headers["Content-Type"] ="application/json"wrk.headers["Accept"] ="*/*"wrk.headers["Accept-Encoding"] ="gzip, deflate, br"request = function()requests = requests +1-- 构造请求数,因为init中定义了,所以这里可以直接使用uid = math.random(1,10000000)method ="POST"path ="/personal/bill/queryBillDeductDetailAll"body = string.format("uid=%s&serialNo=%s&version=%s&lastId=%s",uid,20220920000108360706,519,0)returnwrk.format(method,path,nil,body)end--function request()-- requests = requests +1-- 构造请求数,因为init中定义了,所以这里可以直接使用--returnwrk.request()--endfunction response(status, headers, body)responses = responses +1-- 获取响应数,因为init中定义了,所以可以直接使用endfunction done(summary, latency, requests)forindex, thread in ipairs(threads)dolocalid = thread:get("id") -- 获取线程idlocalrequests = thread:get("requests") -- 获取构造请求数localresponses = thread:get("responses") -- 获取响应数localmsg ="thread %d made %d requests and got %d responses"print(msg:format(id, requests, responses)) endend
然后控制台输入如下命令(需要修改ip地址)
wrk -t32 -c400 -d30s -s setup.lua --latency htt://10.72.240.219:8888
七、小技巧
如何将某个参数变成随机参数
-- 动态生成每个请求的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)
命令行里打印日志
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))
八、注意事项
wrk本身不是依赖线程数来模拟并发数的所以线程数量设置在核心数左右最好,线程数多了测试系统消耗大,可能带来反效果。之前测试跟核心数一致的线程数和两倍核心数的线程数,前者压出的QPS更高。
特别提醒:关于线程数,并不是设置的越大,压测效果越好,线程设置过大,反而会导致线程切换过于频繁,效果降低,一般来说,推荐设置成压测机器 CPU 核心数的 2 倍到 4 倍就行了。
有问题欢迎随时交流