Wrk压测

Wrk压测

下载安装

1
2
git clone https://github.com/wg/wrk
make

mac

1
brew install wrk

命令查看

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Usage: wrk <options> <url>                            
Options:
-c, --connections <N> 连接数
-d, --duration <T> 持续时间
-t, --threads <N> 线程数

-s, --script <S> 制定lua脚本
-H, --header <H> 添加请求头
--latency 打印延迟分布信息
--timeout <T> 设置请求超时
-v, --version 打印版本信息

<N>表示数字参数,支持国际单位 (1k, 1M, 1G)
<T>表示时间参数,支持国际单位 (2s, 2m, 2h)

压测结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Running 30s test @ http://www.bing.com (压测时间30s)
8 threads and 200 connections (共8个测试线程,200个连接)
Thread Stats Avg Stdev Max +/- Stdev
(平均值) (标准差)(最大值)(正负一个标准差所占比例)
Latency 46.67ms 215.38ms 1.67s 95.59%
(延迟)
Req/Sec 7.91k 1.15k 10.26k 70.77%
(处理中的请求数)
Latency Distribution (延迟分布)
50% 2.93ms
75% 3.78ms
90% 4.73ms
99% 1.35s (99分位的延迟)
1790465 requests in 30.01s, 684.08MB read (30.01秒内共处理完成了1790465个请求,读取了684.08MB数据)
Requests/sec: 59658.29 (平均每秒处理完成59658.29个请求)
Transfer/sec: 22.79MB (平均每秒读取数据22.79MB)

Lua 脚本

启动阶段运行阶段结束阶段

启动阶段

1
function setup(thread)

在脚本文件中实现setup方法,wrk就会在测试线程已经初始化但还没有启动的时候调用该方法。
wrk会为每一个测试线程调用一次setup方法,并传入代表测试线程的对象thread作为参数。
setup方法中可操作该thread对象,获取信息、存储信息、甚至关闭该线程。

1
2
3
4
thread.addr             - get or set the thread's server address
thread:get(name) - get the value of a global in the thread's env
thread:set(name, value) - set the value of a global in the thread's env
thread:stop() - stop the thread

运行阶段

1
2
3
4
function init(args)
function delay()
function request()
function response(status, headers, body)
  • init: 由测试线程调用,只会在进入运行阶段时,调用一次。支持从启动wrk的命令中,获取命令行参数;
  • delay: 在每次发送request之前调用,如果需要delay,那么delay相应时间;
  • request: 用来生成请求;每一次请求都会调用该方法,所以注意不要在该方法中做耗时的操作;
  • reponse: 在每次收到一个响应时调用;为提升性能,如果没有定义该方法,那么wrk不会解析headers和body;

结束阶段

1
function done(summary, latency, requests)

该方法在整个测试过程中只会调用一次,可从参数给定的对象中,获取压测结果,生成定制化的测试报告。

自定义脚本中可访问的变量和方法
变量:wrk

1
2
3
4
5
6
7
8
9
10
wrk = {
scheme = "http",
host = "localhost",
port = nil,
method = "GET",
path = "/",
headers = {},
body = nil,
thread = <userdata>,
}

一个table类型的变量wrk,是全局变量,修改该table,会影响所有请求。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function wrk.format(method, path, headers, body)

wrk.format returns a HTTP request string containing the passed parameters
merged with values from the wrk table.
根据参数和全局变量wrk,生成一个HTTP rquest string。

function wrk.lookup(host, service)

wrk.lookup returns a table containing all known addresses for the host
and service pair. This corresponds to the POSIX getaddrinfo() function.
给定host和service(port/well known service name),返回所有可用的服务器地址信息。

function wrk.connect(addr)

wrk.connect returns true if the address can be connected to, otherwise
it returns false. The address must be one returned from wrk.lookup().
测试与给定的服务器地址信息是否可以成功创建连接

使用POST METHOD

1
2
3
wrk.method = "POST"
wrk.body = "foo=bar&baz=quux"
wrk.headers["Content-Type"] = "application/x-www-form-urlencoded"

通过修改全局变量wrk,使得所有请求都使用POST方法,并指定了bodyContent-Type头。

为每次request更换一个参数

1
2
3
4
5
request = function()
uid = math.random(1, 10000000)
path = "/test?uid=" .. uid
return wrk.format(nil, path)
end

通过在request方法中随机生成1~10000000之间的uid,使得请求中的uid参数随机。

每次请求之前延迟10ms

1
2
3
function delay()
return 10
end

每个线程要先进行认证,认证之后获取token以进行压测

1
2
3
4
5
6
7
8
9
10
11
12
13
14
token = nil
path = "/authenticate"

request = function()
return wrk.format("GET", path)
end

response = function(status, headers, body)
if not token and status == 200 then
token = headers["X-Token"]
path = "/resource"
wrk.headers["X-Token"] = token
end
end

在没有token的情况下,先访问authenticate认证。认证成功后,读取token并替换pathresource

压测支持HTTP pipeline的服务

1
2
3
4
5
6
7
8
9
10
11
12
init = function(args)
local r = {}
r[1] = wrk.format(nil, "/?foo")
r[2] = wrk.format(nil, "/?bar")
r[3] = wrk.format(nil, "/?baz")

req = table.concat(r)
end

request = function()
return req
end

通过在init方法中将三个HTTP request请求拼接在一起,实现每次发送三个请求,以使用HTTP pipeline

上传文件

1
2
3
4
5
6
7
8
9
10
11
12
13
wrk.method = "POST"
wrk.headers["Content-Type"] = "multipart/form-data;boundary=------WebKitFormBoundaryX3bY6PBMcxB1vCan"

file = io.open("path/to/fake.jpg", "rb")

-- 拼装form-data
form = "------WebKitFormBoundaryX3bY6PBMcxB1vCan\r\n"
form = form .. "Content-Disposition: form-data; name="file"; filename="fake.jpg"\r\n"
form = form .. "Content-Type: image/jpeg\r\n\r\n"
form = form .. file:read("*a")
form = form .. "\r\n------WebKitFormBoundaryX3bY6PBMcxB1vCan--"

wrk.body = form