建议练习时可以使用FastApi或Flask等Web服务端框架在本地搭建一个HTTP服务。
wrk简介
wrk是一个用于HTTP协议的基准测试工具。基准测试是一种性能测试方法,它通过设计科学的测试方法、测试工具和测试系统,实现对一类测试对象的某项性能指标进行定量和可对比的测试。
GitHub地址:/wg/wrk
安装
Mac直接使用HomeBrew进行安装:
$ brew install wrk
命令行选项
在命令行中输入wrk查看命令行选项:
$ wrkUsage: wrk <options> <url>Options:-c, --connections <N> Connections to keep open# 设置总的 http 并发连接数-d, --duration <T> Duration of test# 设置测试持续时间-t, --threads<N> Number of threads to use# 设置开启的线程数(不能大于设置的并发请求数)-s, --script<S> Load Lua script file# 加载指定的 Lua 脚本-H, --header<H> Add header to request# 添加 http 请求头--latencyPrint latency statistics# 打印延迟统计情况(建议启用)--timeout<T> Socket/request timeout-v, --versionPrint version detailsNumeric arguments may include a SI unit (1k, 1M, 1G)Time arguments may include a time unit (2s, 2m, 2h)
基础示例
GET请求:
$ wrk -c100 -t10 -d10s --latency http://localhost:5000/test_get# 100 个请求分 10 个线程压 10 秒
控制台输出:
Running 10s test @ http://localhost:5000/test_get10 threads and 100 connectionsThread Stats AvgStdevMax +/- StdevLatency 201.71ms 58.06ms 451.62ms 79.16%Req/Sec 27.9410.18 70.0073.70%Latency Distribution50% 196.82ms75% 228.07ms90% 261.79ms99% 394.08ms2817 requests in 10.10s, 473.18KB readRequests/sec: 278.94Transfer/sec:46.86KB
POST请求:
$ wrk -c100 -t10 -d10s -s test_post.lua --latency http://localhost:5000/test_post# 使用 test_post.lua 脚本
Lua脚本:
wrk.method = "POST"wrk.headers["Content-Type"] = "application/json"wrk.body = '{"k1":1,"k2":2}'
控制台输出:
Running 10s test @ http://localhost:5000/test_post10 threads and 100 connectionsThread Stats AvgStdevMax +/- StdevLatency 188.49ms 60.76ms 352.02ms 67.50%Req/Sec 38.3523.71 101.0067.22%Latency Distribution50% 182.22ms75% 230.92ms90% 272.66ms99% 325.02ms3665 requests in 10.10s, 604.87KB readRequests/sec: 362.77Transfer/sec:59.87KB
注:GET请求同样可以使用Lua脚本。
进阶用法
全局变量
Lua脚本在压测时的全局变量(tabel)wrk。
全局变量:
wrk = {scheme = "http",host = "localhost",port = nil,method = "GET",path = "/",headers = {},body = nil,thread = <userdata>,}
全局方法:
function wrk.format(method, path, headers, body)--[[wrk.format 方法返回一个 HTTP 请求的字符串,包含传入参数与 wrk 变量的值的组合。]]--function wrk.lookup(host, service)--[[wrk.lookup 方法返回一个包含主机所有已知地址的 table 和服务对,这对应于 POSIX 的 getaddrinfo() 方法]]---- host:一个主机名或者地址串(IPv4的点分十进制串或者IPv6的16进制串)-- service:服务名可以是十进制的端口号,也可以是已定义的服务名称,如ftp、http等function wrk.connect(addr)--[[判断 addr 是否可以连接上,返回布尔值。addr 必须是一个 wrk.lookup() 方法的返回值。]]--
Lua脚本的生命周期钩子
启动阶段(Setup):启动阶段开始于请求目标IP地址已解析且所有线程已经初始化但还没启动的时候。
function setup(thread)--[[在脚本文件中实现 setup 方法,wrk 就会在目标 IP 已解析且测试线程初始化完成但还没有启动的时候调用该方法。wrk 会为每一个测试线程调用一次 setup 方法,并传入代表测试线程的对象 thread 作为参数。setup 方法中可操作该 thread 对象,获取信息、存储信息、甚至关闭该线程。setup 方法每个线程只执行一次。thread 对象提供了 1 个属性,3 个方法:thread.addr 获取或设置线程的服务器地址thread:get(name) 获取线程全局变量thread:set(name, value) 设置线程全局变量thread:stop() 终止线程]]--
运行阶段(Running):运行阶段开始于对init方法的单次调用,然后在每个请求周期都调用一次request和response方法。
function init(args)-- 每个线程仅调用 1 次,args 用于获取当前脚本命令行中所有传入的其它命令参数,例如 --env=prefunction delay()-- 每次发起请求前调用 1 次,在 request 方法前调用,单位为 msfunction request()-- 每次发起请求前调用 1 次,返回一个包含 HTTP 请求的字符串,wrk 会按该字符串的内容发起 HTTP 请求。-- 通常会在该方法中调用 wrk.format() 生成 HTTP 请求字符串。function response(status, headers, body)-- 每次请求返回时调用 1 次,wrk 通过传入 HTTP 响应码,响应头,响应体调用。
结束阶段(Done):
function done(summary, latency, requests)--[[done 方法接收一个包含测试结果数据的 table 变量 summary 以及两个分别表示每个请求的延迟时间和每个线程的 QPS 的统计对象。持续时间和延迟时间单位均为 ms。整个测试过程中仅被调用 1 次。summary = {duration = N, -- 运行持续时间,单位为 msrequests = N, -- 总共完成的请求书bytes = N, -- 总共接收的数据量errors = {connect = N, -- socket 连接总失败数read = N, -- socket 连接读取总失败数write = N, -- socket 连接写入总失败数status = N, -- total HTTP status codes > 399timeout = N -- total request timeouts}}latency.min -- 最小延时latency.max -- 最大延时latency.mean -- 平均延时latency.stdev -- 延时标准差latency:percentile(99.0) -- 99th percentile valuelatency(i)-- raw value and count]]--
调用过程示例
命令行语句:
$ wrk -c1 -t1 -d1s -s test_lua.lua http://localhost:5000
Lua脚本:
#!/usr/local/bin/luawrk.body = '{"k1":666}'path = "/test_post"function setup(thread)print("调用 setup()")print(thread.addr)endfunction init(args)print("调用 init()")endfunction delay()print("调用 delay() 等待 500 ms")return 500endfunction request()print("调用 resquest()")return wrk.format("POST", path)endfunction response(status, headers, body)print("调用 response()")if (status == 200)thenpath = "/test_get?k1=" .. bodyendendfunction done(summary, latency, requests)print("调用 done()")end
控制台输出:
调用 setup()127.0.0.1:5000调用 init()调用 resquest()Running 1s test @ http://localhost:50001 threads and 1 connections调用 delay() 等待 500 ms调用 resquest()调用 response()调用 delay() 等待 500 ms调用 resquest()调用 response()调用 delay() 等待 500 msThread Stats AvgStdevMax +/- StdevLatency1.08ms 135.06us 1.17ms 100.00%Req/Sec1.500.712.00 100.00%2 requests in 1.01s, 525.00B readNon-2xx or 3xx responses: 1Requests/sec:1.98Transfer/sec:519.09B调用 done()
HTTP请求日志:
127.0.0.1 - - [15/Nov/ 11:10:02] "POST /test_post HTTP/1.1" 200 -127.0.0.1 - - [15/Nov/ 11:10:03] "POST /test_get?k1={"k1":666} HTTP/1.1" 405 -
最后: 可以在公众号:伤心的辣条 ! 自行领取一份216页软件测试工程师面试宝典文档资料【免费的】。以及相对应的视频学习教程免费分享!,其中包括了有基础知识、Linux必备、Shell、互联网程序原理、Mysql数据库、抓包工具专题、接口测试工具、测试进阶-Python编程、Web自动化测试、APP自动化测试、接口自动化测试、测试高级持续集成、测试架构开发测试框架、性能测试、安全测试等。
学习不要孤军奋战,最好是能抱团取暖,相互成就一起成长,群众效应的效果是非常强大的,大家一起学习,一起打卡,会更有学习动力,也更能坚持下去。你可以加入我们的测试技术交流扣扣群:914172719(里面有各种软件测试资源和技术讨论)
喜欢软件测试的小伙伴们,如果我的博客对你有帮助、如果你喜欢我的博客内容,请 “点赞” “评论” “收藏” 一键三连哦!