wrk压测

简单介绍

基于系统自带的高性能 I/O 机制,如 epoll, kqueue, 利用异步的事件驱动框架,通过很少的线程就可以压出很大的并发量
wrk 目前仅支持单机压测

基本使用与介绍

  1. WSL
  2. /mnt/ 目录下 是对应的windows 相应的磁盘
  3. /mnt/e/gitproject/wrk
  4. E:\gitproject\wrk
  5. 安装 make 工具
  6. 安装 gcc编译环境
  7. sudo apt-get install make
  8. wrk安装
  9. 首先在某个目录下 使用 git clone https://github.com/wg/wrk.git 下载
  10. cd wrk目录
  11. 然后 进入该目录 使用sudo make
  12. # 将可执行文件移动到 /usr/local/bin 位置
  13. sudo cp wrk /usr/local/bin
  14. 验证wrk 是否安装成功
  15. wrk -v

参数介绍

  1. wrk -t12 -c400 -d30s http://127.0.0.1:8080/index.html
  2. 简单说一下wrk里面各个参数什么意思?
  3. -t 需要模拟的线程数
  4. -c 需要模拟的连接数
  5. --timeout 超时的时间
  6. -d 测试的持续时间
  7. 结果:
  8. Latency:响应时间
  9. Req/Sec:每个线程每秒钟的完成的请求数
  10. Avg:平均
  11. Max:最大
  12. Stdev:标准差
  13. +/- Stdev 正负一个标准差占比

测试用例

测试环境本机:win10 AMD R7 2700
测试数据:hello world!
测试方法:http get

win10:fastapi+uvicorn

  1. fastapi and uvicorn main:app --reload
  2. wrk -t16 -c100 -d30s http://127.0.0.1:8000
  3. Running 30s test @ http://127.0.0.1:8000
  4. 16 threads and 100 connections
  5. Thread Stats Avg Stdev Max +/- Stdev
  6. Latency 84.37ms 7.59ms 151.44ms 86.58%
  7. Req/Sec 71.21 15.35 121.00 78.49%
  8. 34119 requests in 30.05s, 4.62MB read
  9. Requests/sec: 1135.41
  10. Transfer/sec: 157.46KB
  11. Running 30s test @ http://127.0.0.1:8000
  12. 16 threads and 100 connections
  13. Thread Stats Avg Stdev Max +/- Stdev
  14. Latency 80.47ms 7.81ms 145.23ms 79.20%
  15. Req/Sec 74.68 17.46 121.00 81.42%
  16. 35786 requests in 30.05s, 4.74MB read
  17. Requests/sec: 1191.01
  18. Transfer/sec: 161.67KB
  19. Running 30s test @ http://127.0.0.1:8000
  20. 16 threads and 1000 connections
  21. Thread Stats Avg Stdev Max +/- Stdev
  22. Latency 861.56ms 57.32ms 1.10s 97.23%
  23. Req/Sec 72.09 39.02 210.00 65.57%
  24. 33991 requests in 30.07s, 4.51MB read
  25. Socket errors: connect 0, read 0, write 1963, timeout 0
  26. Requests/sec: 1130.22
  27. Transfer/sec: 153.42KB

win10:doker+fastapi+uvicorn

  1. docker:
  2. Running 30s test @ http://127.0.0.1:8000
  3. 16 threads and 100 connections
  4. Thread Stats Avg Stdev Max +/- Stdev
  5. Latency 9.28ms 3.95ms 44.69ms 71.35%
  6. Req/Sec 652.48 80.30 0.91k 68.48%
  7. 312592 requests in 30.10s, 61.11MB read
  8. Requests/sec: 10385.30
  9. Transfer/sec: 2.03MB
  10. Running 30s test @ http://127.0.0.1:8000
  11. 12 threads and 200 connections
  12. Thread Stats Avg Stdev Max +/- Stdev
  13. Latency 17.27ms 7.15ms 66.18ms 70.56%
  14. Req/Sec 0.93k 87.26 1.26k 68.50%
  15. 334625 requests in 30.10s, 65.42MB read
  16. Requests/sec: 11117.33
  17. Transfer/sec: 2.17MB

win10:go http

  1. win10
  2. wrk -t16 -c1000 -d30s http://127.0.0.1:8001
  3. Running 30s test @ http://127.0.0.1:8001
  4. 16 threads and 100 connections
  5. Thread Stats Avg Stdev Max +/- Stdev
  6. Latency 1.10ms 705.16us 14.69ms 87.90%
  7. Req/Sec 5.72k 363.31 9.16k 76.72%
  8. 2737464 requests in 30.10s, 336.77MB read
  9. Requests/sec: 90946.16
  10. Transfer/sec: 11.19MB
  11. Running 30s test @ http://127.0.0.1:8001
  12. 10 threads and 100 connections
  13. Thread Stats Avg Stdev Max +/- Stdev
  14. Latency 1.15ms 745.51us 14.42ms 87.68%
  15. Req/Sec 8.87k 755.39 24.24k 80.25%
  16. 2649469 requests in 30.10s, 325.95MB read
  17. Requests/sec: 88018.88
  18. Transfer/sec: 10.83MB

使用lua脚本进行复杂测试

使用post 请求并带有参数 可以使用带有lua的脚本来指定 通过参数--script指定lua脚本
wrk 支持在三个阶段对压测进行个性化,分别是启动阶段、运行阶段和结束阶段。每个测试线程,都拥有独立的Lua 运行环境

启动阶段

  1. function setup(thread)
  2. 在脚本文件中实现 setup 方法,wrk 就会在测试线程已经初始化,但还没有启动的时候调用该方法。
  3. wrk会为每一个测试线程调用一次 setup 方法,并传入代表测试线程的对象 thread 作为参数。
  4. setup 方法中可操作该 thread 对象,获取信息、存储信息、甚至关闭该线程
  5. thread.addr - get or set the thread's server address
  6. thread:get(name) - get the value of a global in the thread's env
  7. thread:set(name, value) - set the value of a global in the thread's env
  8. thread:stop() - stop the thread

运行阶段

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

    结束阶段

  1. function done(summary, latency, requests)

done() 方法在整个测试过程中只会被调用一次,我们可以从给定的参数中,获取压测结果,生成定制化的测试报告。

自定义 Lua 脚本中可访问的变量以及方法

wrk变量

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

以上定义了一个 table 类型的全局变量,修改该 wrk 变量,会影响所有请求。
方法:

  • wrk.fomat
  • wrk.lookup
  • wrk.connect
  1. function wrk.format(method, path, headers, body)
  2. wrk.format returns a HTTP request string containing the passed parameters
  3. merged with values from the wrk table.
  4. # 根据参数和全局变量 wrk,生成一个 HTTP rquest 字符串。
  5. function wrk.lookup(host, service)
  6. wrk.lookup returns a table containing all known addresses for the host
  7. and service pair. This corresponds to the POSIX getaddrinfo() function.
  8. # 给定 host 和 service(port/well known service name),返回所有可用的服务器地址信息。
  9. function wrk.connect(addr)
  10. wrk.connect returns true if the address can be connected to, otherwise
  11. it returns false. The address must be one returned from wrk.lookup().
  12. # 测试给定的服务器地址信息是否可以成功创建连接

通过 Lua 脚本压测示例

  • 调用 POST 接口:

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

    注意: wrk 是个全局变量,这里对其做了修改,使得所有请求都使用 POST 的方式,并指定了 body 和 Content-Type头

  • 自定义每次请求的参数:

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

    在 request 方法中,随机生成 1~10000000 之间的 uid,并动态生成请求 URL.

  • 每次请求前,延迟 10ms:

    1. function delay()
    2. return 10
    3. end
  • 请求的接口需要先进行认证,获取 token 后,才能发起请求,咋办?

  1. token = nil
  2. path = "/auth"
  3. request = function()
  4. return wrk.format("GET", path)
  5. end
  6. response = function(status, headers, body)
  7. if not token and status == 200 then
  8. token = headers["X-Token"]
  9. path = "/test"
  10. wrk.headers["X-Token"] = token
  11. end
  12. end

上面的脚本表示,在 token 为空的情况下,先请求 /auth 接口来认证,获取 token, 拿到 token 以后,将 token 放置到请求头中,再请求真正需要压测的/test接口。

  • 压测支持 HTTP pipeline 的服务: ```lua 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请求拼接在一起,实现每次发送三个请求,以使用 HTTP pipeline。