性能测试小工具WRK

       wrk 是一个比较先进的 HTTP 压力测试工具。wrk负载测试时可以运行在一个或者多核CPU,wrk结合了可伸缩的事件通知系统epoll和kqueue等多线程设计思想。目前wrk可以安装在Linux系统和Mac系统。 只有一个命令行, 就能做很多基本的 http 性能测试.

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

首先要说的一点是: wrk 只能运行在 Unix 类的系统上. 比如 linux, mac, solaris 等. 也只能在这些系统上编译.

这 里不得不说一下, 为什么很多人说 mac 是最好的开发环境. 不是因为使用 mac 逼格有多高. 而是你可以同时得到 windows 和 linux 的好处. 多数 linux 下的开发工具都可以在 mac 上使用. 很多都是预编译好的, 有些只需要编译一下就能用.

wrk 的一个很好的特性就是能用很少的线程压出很大的并发量. 原因是它使用了一些操作系统特定的高性能 io 机制, 比如 select, epoll, kqueue 等. 其实它是复用了 redis 的 ae 异步事件驱动框架. 确切的说 ae 事件驱动框架并不是 redis 发明的, 它来至于 Tcl的解释器 jim, 这个小巧高效的框架, 因为被 redis 采用而更多的被大家所熟知.

要用 wrk, 首先要编译 wrk.
你的机器上需要已经安装了 git 和基本的c编译环境. wrk 本身是用 c 写的. 代码很少. 并且没有使用很多第三方库. 所以编译基本不会遇到什么问题.

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

2. cd wrk

3. make


就 ok了.
make 成功以后在目录下有一个 wrk 文件. 就是它了. 你可以把这个文件复制到其他目录, 比如 bin 目录. 或者就这个目录下执行.

如果编译过程中出现:

1. src/wrk.h:11:25: fatal error: openssl/ssl.h: No such file or directory

2. #include <openssl/ssl.h>


是因为系统中没有安装openssl的库.

sudo apt-get install libssl-dev

sudo yum install openssl-devel

安装完成配置环境变量 /etc/profile

export PATH=$PATH:/letv/wrk

生效profile 文件

source /etc/profile

我们先来做一个简单的性能测试:

1.wrk -t2 -c10 -d30s -T30s  --latency http://10.58.100.90/jsondatedemo.txt

等待完成本次测试

Running 30s test @ http://10.58.100.90/jsondatedemo.txt
  2 threads and 10 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency   209.38ms  300.23ms   1.99s    90.36%
    Req/Sec    21.81     10.55    60.00     62.24%
  Latency Distribution
     50%  106.14ms
     75%  188.97ms
     90%  491.26ms
     99%    1.56s
  1218 requests in 30.04s, 1.76MB read
  Socket errors: connect 0, read 540, write 0, timeout 30
  Non-2xx or 3xx responses: 540
Requests/sec:     40.55
Transfer/sec:     60.02KB

------------------------------------------------------------------------------------------------

参数含义:

-t 线程模式 -c模拟并发数 -d 持续时间 m 分钟 s秒 -T 设置延时时间,默认1秒   --latency 显示百分之多少用户的响应时间

-------------------------------------------------------------------------------------------------

 -c  一般线程数不宜过多. 核数的2到4倍足够了. 多了反而因为线程切换过多造成效率降低. 因为 wrk 不是使用每个连接一个线程的模型, 而是通过异步网络 io 提升并发量. 所以网络通信不会阻塞线程执行. 这也是 wrk 可以用很少的线程模拟大量网路连接的原因. 而现在很多性能工具并没有采用这种方式, 而是采用提高线程数来实现高并发. 所以并发量一旦设的很高, 测试机自身压力就很大. 测试效果反而下降.

-T wrk 默认超时时间是1秒. 这个有点短. 我一般设置为30秒. 这个看上去合理一点.

Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency   209.38ms  300.23ms   1.99s    90.36%
    Req/Sec    21.81     10.55    60.00     62.24%

Latency: 可以理解为响应时间, 有平均值, 标准偏差, 最大值, 正负一个标准差占比.
Req/Sec: 每个线程每秒钟的完成的请求数, 同样有平均值, 标准偏差, 最大值, 正负一个标准差占比.

一般我们来说我们主要关注平均值和最大值. 标准差如果太大说明样本本身离散程度比较高. 有可能系统性能波动很大.

--latency

  Latency Distribution
     50%  106.14ms
     75%  188.97ms
     90%  491.26ms
     99%    1.56s

30秒钟总共完成请求数1218和读取数据量30.04s.
然后是错误统计, 上面的统计可以看到, 540个读错误, 30个超时.
然后是所以线程总共平均每秒钟完成40.55个请求. 每秒钟读取60.02兆数据量.

  1218 requests in 30.04s, 1.76MB read
  Socket errors: connect 0, read 540, write 0, timeout 30
  Non-2xx or 3xx responses: 540
Requests/sec:     40.55
Transfer/sec:     60.02KB

wrk 支持lua语言脚本 ,在这个脚本里你可以修改 method, header, body, 可以对 response 做一下自定义的分析. 因为是 lua 脚本, 其实这给了你无限的可能. 但是这样一个强大的功能如果不谨慎使用, 会降低测试端的性能, 测试结果也受到影响.

一般修改method, header, body不会影响测试端性能, 但是操作 request, response 就要格外谨慎了.

我们通过一些测试场景在看看怎么使用 lua 脚本.

POST + header + body.

首先创建一个 post.lua 的文件:

1. wrk.method = "POST"

2. wrk.body = "devId=C80E7776704A"

3. wrk.headers["Content-Type"] = "application/x-www-form-urlencoded"


就这三行就可以了, 当然 headers 可以加入任意多的内容.
然后执行:

1. wrk -t12 -c100 -d30s -T30s --script=post.lua --latency http://h5.itv.cp21.ott.cibntv.net/api/lottery/draw.json?

---------------------------------------------------------------------------------------------------------------------------------------------

下面没有验证过:

wrk 提供的几个 lua 的 hook 函数:

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

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

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

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

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

1. function response(status, headers, body)

2. if status ~= 200 then

3. print(body)

4. wrk.thread:stop()

5. end

6. end



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

1. done = function(summary, latency, requests)

2. io.write("------------------------------\n")

3. for _, p in pairs({ 50, 90, 99, 99.999 }) do

4. n = latency:percentile(p)

5. io.write(string.format("%g%%,%d\n", p, n))

6. end

7. end



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

1. local counter = 1

2. local threads = {}

3.

4. function setup(thread)

5. thread:set("id", counter)

6. table.insert(threads, thread)

7. counter = counter + 1

8. end

9.

10. function init(args)

11. requests = 0

12. responses = 0

13.

14. local msg = "thread %d created"

15. print(msg:format(id))

16. end

17.

18. function request()

19. requests = requests + 1

20. return wrk.request()

21. end

22.

23. function response(status, headers, body)

24. responses = responses + 1

25. end

26.

27. function done(summary, latency, requests)

28. for index, thread in ipairs(threads) do

29. local id = thread:get("id")

30. local requests = thread:get("requests")

31. local responses = thread:get("responses")

32. local msg = "thread %d made %d requests and got %d responses"

33. print(msg:format(id, requests, responses))

34. end

35. end



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

1. counter = 1

2.

3. math.randomseed(os.time())

4. math.random(); math.random(); math.random()

5.

6. function file_exists(file)

7. local f = io.open(file, "rb")

8. if f then f:close() end

9. return f ~= nil

10. end

11.

12. function shuffle(paths)

13. local j, k

14. local n = #paths

15. for i = 1, n do

16. j, k = math.random(n), math.random(n)

17. paths[j], paths[k] = paths[k], paths[j]

18. end

19. return paths

20. end

21.

22. function non_empty_lines_from(file)

23. if not file_exists(file) then return {} end

24. lines = {}

25. for line in io.lines(file) do

26. if not (line == '') then

27. lines[#lines + 1] = line

28. end

29. end

30. return shuffle(lines)

31. end

32.

33. paths = non_empty_lines_from("paths.txt")

34.

35. if #paths <= 0 then

36. print("multiplepaths: No paths found. You have to create a file paths.txt with one path per line")

37. os.exit()

38. end

39.

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

41.

42. request = function()

43. path = paths[counter]

44. counter = counter + 1

45. if counter > #paths then

46. counter = 1

47. end

48. return wrk.format(nil, path)

49. end



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

1. function getCookie(cookies, name)

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

3.

4. if start == nil then

5. return nil

6. end

7.

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

9. end

10.

11. response = function(status, headers, body)

12. local token = getCookie(headers["Set-Cookie"], "token")

13.

14. if token ~= nil then

15. wrk.headers["Cookie"] = "token=" .. token

16. end

17. end


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

转:http://sanwen8.cn/p/1d4kAUJ.html

安装:224 /letv/wrk

猜你喜欢

转载自km-moon11.iteye.com/blog/2321879