searchusermenu
  • 发布文章
  • 消息中心
点赞
收藏
评论
分享
原创

性能测试工具之wrk

2023-07-31 13:07:59
35
0

简介

wrk 是一个类似 ab(apache bench)、jmeter 的 压力测试工具,底层基于 epoll 和 kqueue 实现,能充分利用 cpu 资源,降低测试工具本身性能开销对测试结果准确性的影响。支持使用 lua 脚本自定义测试逻辑,使用上非常简单,但功能足够强大。

用法

# GET 方法(默认),12 线程、400 连接、持续 30秒
$ wrk -t12 -c400 -d30s http://127.0.0.1:8080/test

说明

Usage: wrk <options> <url>
Options:
   -c, --connections <N> Connections to keep open
   -d, --duration   <T> Duration of test
   -t, --threads     <N> Number of threads to use
​
   -s, --script     <S> Load Lua script file
   -H, --header     <H> Add header to request
       --latency         Print latency statistics
       --timeout     <T> Socket/request timeout
   -v, --version         Print version details
​
 # 数量、时间可指定单位
Numeric arguments may include a SI unit (1k, 1M, 1G)
Time arguments may include a time unit (2s, 2m, 2h)

Lua 脚本测试

# 指定脚本 myscript.lua
$ wrk -t12 -c400 -d30s -s myscript.lua http://127.0.0.1:8080/test

POST 请求

-- post.lua
​
-- example HTTP POST script which demonstrates setting the
-- HTTP method, body, and adding a header
​
wrk.method = "POST"
wrk.body   = "foo=bar&baz=quux"
wrk.headers["Content-Type"] = "application/x-www-form-urlencoded"

API 说明

The public Lua API consists of a global table and a number of global functions.

全局变量

wrk 是一个内置的全局 table 类型变量,不需要定义可以直接使用,修改 wrk 变量的值,会对所有请求都生效。

wrk = {
  scheme  = "http",
  host    = "localhost",
  port    = nil,
  method  = "GET",
  path    = "/",
  headers = {},
  body    = nil,
  thread  = <userdata>
}

全局函数

wrk.format

生成请求 request

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.lookup

查询可用地址列表

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.

wrk.connect

测试连接状态

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().

示例

local addrs = nil

function setup(thread)
if not addrs then
addrs = wrk.lookup(wrk.host, wrk.port or "http")
for i = #addrs, 1, -1 do
if not wrk.connect(addrs[i]) then
table.remove(addrs, i)
end
end
end

thread.addr = addrs[math.random(#addrs)]
end

function init(args)
local msg = "thread addr: %s"
print(msg:format(wrk.thread.addr))
end

生命周期回调函数

生命周期 3 个阶段:启动、运行、结束。

启动阶段

setup 每个线程初始化时执行一次。

function setup(thread)

setup 方法会传入一个 thread 对象,可以修改或设置 thread 相关参数,也可以终止线程执行,这里一般做一些初始化的工作,例如读取配置文件,加载到内存(不要每次请求的时候读取一遍,这样对测试准确性影响很大)

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,终止线程
执行阶段

init 每个线程开始启动时执行一次。

  function init(args)
function delay()
function request()
function response(status, headers, body)
结束阶段

done 返回结果时执行,整个测试过程只执行一次,可以生成自定义测试报告,如果没有特别需求,一般不重写这个方法。

function done(summary, latency, requests)
应用示例
local counter = 1
local threads = {}

-- 开始阶段
function setup(thread)
thread:set("id", counter)
table.insert(threads, thread)
counter = counter + 1
end

-- 执行阶段
function init(args)
requests = 0
responses = 0

local msg = "thread %d created"
print(msg:format(id))
end

function request()
requests = requests + 1
return wrk.request()
end

function response(status, headers, body)
responses = responses + 1
end

-- 结束阶段(可自定义测试报告)
function done(summary, latency, requests)
for index, thread in ipairs(threads) do
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))
end
end

测试示例

addr.lua

地址随机轮询

-- example script that demonstrates use of setup() to pass
-- a random server address to each thread

local addrs = nil

function setup(thread)
if not addrs then
addrs = wrk.lookup(wrk.host, wrk.port or "http")
for i = #addrs, 1, -1 do
if not wrk.connect(addrs[i]) then
table.remove(addrs, i)
end
end
end

thread.addr = addrs[math.random(#addrs)]
end

function init(args)
local msg = "thread addr: %s"
print(msg:format(wrk.thread.addr))
end
auth.lua

登录前置请求

-- example script that demonstrates response handling and
-- retrieving an authentication token to set on all future
-- requests

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
counter.lua

动态路径

-- example dynamic request script which demonstrates changing
-- the request path and a header for each request
-------------------------------------------------------------
-- NOTE: each wrk thread has an independent Lua scripting
-- context and thus there will be one counter per thread

counter = 0

request = function()
path = "/" .. counter
wrk.headers["X-Counter"] = counter
counter = counter + 1
return wrk.format(nil, path)
end
delay.lua

请求等待延迟

-- example script that demonstrates adding a random
-- 10-50ms delay before each request

function delay()
return math.random(10, 50)
end
pipeline.lua

流水线/串行请求

-- example script demonstrating HTTP pipelining

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
post.lua

POST 方法请求

-- example HTTP POST script which demonstrates setting the
-- HTTP method, body, and adding a header

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

打印测试报告

-- example reporting script which demonstrates a custom
-- done() function that prints latency percentiles as CSV

done = function(summary, latency, requests)
io.write("------------------------------\n")
for _, p in pairs({ 50, 90, 99, 99.999 }) do
n = latency:percentile(p)
io.write(string.format("%g%%,%d\n", p, n))
end
end
stop.lua

结束测试

-- example script that demonstrates use of thread:stop()

local counter = 1

function response()
if counter == 100 then
wrk.thread:stop()
end
counter = counter + 1
end

参考

0条评论
作者已关闭评论
萧****秀
5文章数
0粉丝数
萧****秀
5 文章 | 0 粉丝
萧****秀
5文章数
0粉丝数
萧****秀
5 文章 | 0 粉丝
原创

性能测试工具之wrk

2023-07-31 13:07:59
35
0

简介

wrk 是一个类似 ab(apache bench)、jmeter 的 压力测试工具,底层基于 epoll 和 kqueue 实现,能充分利用 cpu 资源,降低测试工具本身性能开销对测试结果准确性的影响。支持使用 lua 脚本自定义测试逻辑,使用上非常简单,但功能足够强大。

用法

# GET 方法(默认),12 线程、400 连接、持续 30秒
$ wrk -t12 -c400 -d30s http://127.0.0.1:8080/test

说明

Usage: wrk <options> <url>
Options:
   -c, --connections <N> Connections to keep open
   -d, --duration   <T> Duration of test
   -t, --threads     <N> Number of threads to use
​
   -s, --script     <S> Load Lua script file
   -H, --header     <H> Add header to request
       --latency         Print latency statistics
       --timeout     <T> Socket/request timeout
   -v, --version         Print version details
​
 # 数量、时间可指定单位
Numeric arguments may include a SI unit (1k, 1M, 1G)
Time arguments may include a time unit (2s, 2m, 2h)

Lua 脚本测试

# 指定脚本 myscript.lua
$ wrk -t12 -c400 -d30s -s myscript.lua http://127.0.0.1:8080/test

POST 请求

-- post.lua
​
-- example HTTP POST script which demonstrates setting the
-- HTTP method, body, and adding a header
​
wrk.method = "POST"
wrk.body   = "foo=bar&baz=quux"
wrk.headers["Content-Type"] = "application/x-www-form-urlencoded"

API 说明

The public Lua API consists of a global table and a number of global functions.

全局变量

wrk 是一个内置的全局 table 类型变量,不需要定义可以直接使用,修改 wrk 变量的值,会对所有请求都生效。

wrk = {
  scheme  = "http",
  host    = "localhost",
  port    = nil,
  method  = "GET",
  path    = "/",
  headers = {},
  body    = nil,
  thread  = <userdata>
}

全局函数

wrk.format

生成请求 request

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.lookup

查询可用地址列表

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.

wrk.connect

测试连接状态

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().

示例

local addrs = nil

function setup(thread)
if not addrs then
addrs = wrk.lookup(wrk.host, wrk.port or "http")
for i = #addrs, 1, -1 do
if not wrk.connect(addrs[i]) then
table.remove(addrs, i)
end
end
end

thread.addr = addrs[math.random(#addrs)]
end

function init(args)
local msg = "thread addr: %s"
print(msg:format(wrk.thread.addr))
end

生命周期回调函数

生命周期 3 个阶段:启动、运行、结束。

启动阶段

setup 每个线程初始化时执行一次。

function setup(thread)

setup 方法会传入一个 thread 对象,可以修改或设置 thread 相关参数,也可以终止线程执行,这里一般做一些初始化的工作,例如读取配置文件,加载到内存(不要每次请求的时候读取一遍,这样对测试准确性影响很大)

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,终止线程
执行阶段

init 每个线程开始启动时执行一次。

  function init(args)
function delay()
function request()
function response(status, headers, body)
结束阶段

done 返回结果时执行,整个测试过程只执行一次,可以生成自定义测试报告,如果没有特别需求,一般不重写这个方法。

function done(summary, latency, requests)
应用示例
local counter = 1
local threads = {}

-- 开始阶段
function setup(thread)
thread:set("id", counter)
table.insert(threads, thread)
counter = counter + 1
end

-- 执行阶段
function init(args)
requests = 0
responses = 0

local msg = "thread %d created"
print(msg:format(id))
end

function request()
requests = requests + 1
return wrk.request()
end

function response(status, headers, body)
responses = responses + 1
end

-- 结束阶段(可自定义测试报告)
function done(summary, latency, requests)
for index, thread in ipairs(threads) do
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))
end
end

测试示例

addr.lua

地址随机轮询

-- example script that demonstrates use of setup() to pass
-- a random server address to each thread

local addrs = nil

function setup(thread)
if not addrs then
addrs = wrk.lookup(wrk.host, wrk.port or "http")
for i = #addrs, 1, -1 do
if not wrk.connect(addrs[i]) then
table.remove(addrs, i)
end
end
end

thread.addr = addrs[math.random(#addrs)]
end

function init(args)
local msg = "thread addr: %s"
print(msg:format(wrk.thread.addr))
end
auth.lua

登录前置请求

-- example script that demonstrates response handling and
-- retrieving an authentication token to set on all future
-- requests

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
counter.lua

动态路径

-- example dynamic request script which demonstrates changing
-- the request path and a header for each request
-------------------------------------------------------------
-- NOTE: each wrk thread has an independent Lua scripting
-- context and thus there will be one counter per thread

counter = 0

request = function()
path = "/" .. counter
wrk.headers["X-Counter"] = counter
counter = counter + 1
return wrk.format(nil, path)
end
delay.lua

请求等待延迟

-- example script that demonstrates adding a random
-- 10-50ms delay before each request

function delay()
return math.random(10, 50)
end
pipeline.lua

流水线/串行请求

-- example script demonstrating HTTP pipelining

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
post.lua

POST 方法请求

-- example HTTP POST script which demonstrates setting the
-- HTTP method, body, and adding a header

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

打印测试报告

-- example reporting script which demonstrates a custom
-- done() function that prints latency percentiles as CSV

done = function(summary, latency, requests)
io.write("------------------------------\n")
for _, p in pairs({ 50, 90, 99, 99.999 }) do
n = latency:percentile(p)
io.write(string.format("%g%%,%d\n", p, n))
end
end
stop.lua

结束测试

-- example script that demonstrates use of thread:stop()

local counter = 1

function response()
if counter == 100 then
wrk.thread:stop()
end
counter = counter + 1
end

参考

文章来自个人专栏
文章 | 订阅
0条评论
作者已关闭评论
作者已关闭评论
0
0