wrk用lua脚本构建复杂的http压力测试

前言:

      以前一说到http的压力测试工具,多数人会说 ab、webbench、siege。 但这些工具不管是性能还是复杂度已经不能满足我们的需求了。 其实我从16年就开始接触wrk压力测试工具,先前使用的方法比较简单粗暴,那你可以想象的出来,用wrk只是为了更好的获取吞吐。

     apache httpd 的ab有什么缺点么?  性能 !!!!  wrk 使用了相当成熟的redis的ae事件库,避免了造轮子,事件库可以减少ab的大量线程的创建开销和调度开销.  另外他的httpparser也相当的优秀.  吹捧完了,总之,用wrk就对了。

     为什么这次又突然谈起了wrk,  因这周要上线一个cdn的管理系统,我临时使用了python写了个测试脚本,发现压力上不去,作为压力脚本输出不给力,cpu吃的倒是挺多….  这时候又突然想起了wrk这个暴力压测利器。 wrk有个更牛叉的特性就是支持自定义的lua脚本,像往常的压力测试工具,我们只是发起固定的请求,请求体是无法改变的。 但是wrk lua脚本不仅是可以做鉴权签名,可以改变请求体,模拟延迟,通过请求的返回值做些收尾。

该文章后续仍在不断更新中, 请移步到原文地址  http://xiaorui.cc/?p=5098

使用说明

wrk的安装我就不多说了,直接git拉下来,make就完事了。 wrk 已经不需要指定luajit了,默认会集成一份的。  show下wrk的用法,单纯从参数来说,确实没有ab那么丰富。

wrk
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)

wrk的生命周期

wrk 可以在lua脚本里添加下面的Hook函数,你可以想象成生命周期,每个生命周期做的事情都不一样, 但是生命周期是有时间顺序的。我们常用一般是 request 和 delay 周期. 

setup

线程出事后支持会调用一次。

init

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

delay

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

request

通过这个函数可以每次请求之前修改本次请求体和Header,我们可以在这里写一些要压力测试的逻辑。

response

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

wrk的使用案例

下面放一个我用到的构建 request 例子,针对一个接口压力测试,请求体的某个字段不能重复,要随机。

# xiaorui.cc

counter = 0

charset = {}  do -- [0-9a-zA-Z]
    for c = 48, 57  do table.insert(charset, string.char(c)) end
    for c = 65, 90  do table.insert(charset, string.char(c)) end
    for c = 97, 122 do table.insert(charset, string.char(c)) end
end

function randomString(length)
    if not length or length <= 0 then return '' end
    math.randomseed(os.clock()^5)
    return randomString(length - 1) .. charset[math.random(1, #charset)]
end

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

request = function()
    counter = counter + 1
    local task_id = string.format("%d-%s", os.time()*10000+math.random(1, 10000), randomString(10))

    local data = [[{
        "task_timeout": 30,
        "task_id": "%s",
        "task_body": {
            "url_list": [
            {
                "subtask_id": "1000000022511092",
                "url": "http://zhihu.com/upic/2017/08/24/15/kk.webp",
                "force": true
            },
            {
                "subtask_id": "1000000022511093",
                "url": "http://zhihu.com/upic/2017/08/24/15/zz.jpg",
                "force": true
            }
            ]
        },
        "ip_list": [],
        "ip_list_flag": false,
        "task_type": "refresh",
        "retry_failed": false,
        "working_layer": "polymerization"
    }]]

    wrk.method = "POST"
    wrk.body   = string.format(data, tostring(task_id))
    wrk.headers["Content-Type"] = "application/json"
    -- return wrk.format("POST", "/v1/engine/task")
    return wrk.format()

end

那么response hook怎么用? wrk每次通过request构建请求,当http返回报文时,wrk会回调 response hook方法。

# xiaorui.cc

token = nil
path  = "/auth"

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

中间遇到一个wrk的报错信息,  incomplete request at 1:1 ,这个问题是由于request方法没有return wrk引起的。

wrk官方也提供了一些例子https://github.com/wg/wrk/tree/master/scripts,   但都很简短,能不能来点说明,敢不敢来点文档。

END


大家觉得文章对你有些作用! 如果想赏钱,可以用微信扫描下面的二维码,感谢!
另外再次标注博客原地址  xiaorui.cc