wrk http性能测试工具

wrk 是一个很简单的 http 性能测试工具. 也可以叫做 http benchmark 工具. 只有一个命令行, 就能做很多基本的 http 性能测试

wrk的代码在 github 上. https://github.com/wg/wrk

安装

mac

brew install wrk

Ubuntu/Debian (clean box)

sudo apt-get install build-essential libssl-dev git -y
git clone https://github.com/wg/wrk.git wrk
cd wrk
sudo make
# move the executable to somewhere in your PATH, ex:
sudo cp wrk /usr/local/bin

CentOS / RedHat / Fedora

sudo yum groupinstall 'Development Tools'
sudo yum install -y openssl-devel git 
git clone https://github.com/wg/wrk.git wrk
cd wrk
make
# move the executable to somewhere in your PATH
sudo cp wrk /somewhere/in/your/PATH

命令参数

用法: 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
                            Lua脚本
    -H, --header      <H>  Add header to request
                            添加请求的header
        --latency          Print latency statistics
                            打印响应分布的统计
        --timeout     <T>  Socket/request timeout
                            设置响应超时(2s, 2m, 2h)
                            wrk 默认超时时间是1s
    -v, --version          Print version details
                            答应wrk版本信息
  Numeric arguments may include a SI unit (1k, 1M, 1G)
  Time arguments may include a time unit (2s, 2m, 2h)

使用方法

wrk -t12 -c400 -d30s --latency http://127.0.0.1:8000

上面这句话的意思为:12个进程模拟400个连接,持续时间为30秒,并且打印出响应时间的分布

运行结果:

Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency    42.10ms   98.36ms   2.00s    98.33%
    Req/Sec     1.06k   284.48     4.20k    74.23%
  Latency Distribution
     50%   26.65ms
     75%   41.39ms
     90%   62.64ms
     99%  374.95ms
  376093 requests in 30.07s, 60.62MB read
  Socket errors: connect 0, read 0, write 0, timeout 81
Requests/sec:  12508.83
Transfer/sec:      2.02MB

解释:

进程状态        平均值     标准差     最大值   正负一个标准差占比
    响应时间    42.10ms   98.36ms   2.00s   98.33%
    请求/秒     1.06k   284.48     4.20k   74.23%
  响应时间分布:
     50%   26.65ms   50% 在26.65毫秒完成
     75%   41.39ms   75% 在41.39毫秒完成
     90%   62.64ms   90% 在62.64毫秒完成
     99%  374.95ms   99% 在374.95毫秒完成
  30.07s 共发送 376093 个请求, 共读取 60.62MB 数据
  Socket 错误: 连接 0, 读 0, 写 0, 超市 81
请求/秒:  12508.83
获取数据/s:  2.02MB

Lua脚本的使用

POST + header + body

编写Lua脚本

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

使用wrk加载脚本并执行

wrk -t12 -c400 -d30s --latency --script=post.lua --latency http://127.0.0.1:8000

wrk 对象的修改全局只会执行一次

wrk的全局对象如下所示:

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

用 lua 脚本测试复杂场景

wrk 提供了几个 hook 函数,可以用 lua 来编写一些复杂场景下的测试:

setup

这个函数在目标 IP 地址已经解析完,并且所有 thread 已经生成,但是还没有开始时被调用,每个线程执行一次这个函数。可以通过 thread:get(name), thread:set(name, value) 设置线程级别的变量。

init

每次请求发送之前被调用。可以接受 wrk 命令行的额外参数,通过 -- 指定。

delay

这个函数返回一个数值,在这次请求执行完以后延迟多长时间执行下一个请求,可以对应 thinking time 的场景。

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

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

request

通过这个函数可以每次请求之前修改本次请求的属性,返回一个字符串,这个函数要慎用, 会影响测试端性能。

response

每次请求返回以后被调用,可以根据响应内容做特殊处理,比如遇到特殊响应停止执行测试,或输出到控制台等等。

function response(status, headers, body)  
   if status ~= 200 then  
      print(body)  
      wrk.thread:stop()  
   end  
end

done

在所有请求执行完以后调用, 一般用于自定义统计结果

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

Lua例子

下面是 wrk 源代码中给出的完整例子:

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

测试复合场景

可以通过 lua 实现访问多个 url. 例如这个复杂的 lua 脚本, 随机读取 paths.txt 文件中的 url 列表, 然后访问

counter = 1  

math.randomseed(os.time())  
math.random(); math.random(); math.random()  

function file_exists(file)  
  local f = io.open(file, "rb")  
  if f then f:close() end  
  return f ~= nil  
end  

function shuffle(paths)  
  local j, k  
  local n = #paths  
  for i = 1, n do  
    j, k = math.random(n), math.random(n)  
    paths[j], paths[k] = paths[k], paths[j]  
  end  
  return paths  
end  

function non_empty_lines_from(file)  
  if not file_exists(file) then return {} end  
  lines = {}  
  for line in io.lines(file) do  
    if not (line == '') then  
      lines[#lines + 1] = line  
    end  
  end  
  return shuffle(lines)  
end  

paths = non_empty_lines_from("paths.txt")  

if #paths <= 0 then  
  print("multiplepaths: No paths found. You have to create a file paths.txt with one path per line")  
  os.exit()  
end  

print("multiplepaths: Found " .. #paths .. " paths")  

request = function()  
    path = paths[counter]  
    counter = counter + 1  
    if counter > #paths then  
      counter = 1  
    end  
    return wrk.format(nil, path)  
end

有些时候我们需要模拟一些通过 cookie 传递数据的场景. wrk 并没有特殊支持, 可以通过 wrk.headers["Cookie"]="xxxxx"实现. 下面是在网上找的一个例子, 取 Response的cookie作为后续请求的cookie

function getCookie(cookies, name)  
  local start = string.find(cookies, name .. "=")  

  if start == nil then  
    return nil  
  end  

  return string.sub(cookies, start + #name + 1, string.find(cookies, ";", start) - 1)  
end  

response = function(status, headers, body)  
  local token = getCookie(headers["Set-Cookie"], "token")  

  if token ~= nil then  
    wrk.headers["Cookie"] = "token=" .. token  
  end  
end

总结

wrk 本身的定位不是用来替换 loadrunner 这样的专业性能测试工具的. 其实有这些功能已经完全能应付平时开发过程中的一些性能验证了.

results matching ""

    No results matching ""