各个公司或多或少都在推出自己的压力测试工具, 形形色色, 种类繁多; 其实, 在开源世界已经有了一个经典成熟的压力测试工具 —— apache benchmark;
小巧, 简单, 基于 http 的普适性, 这些都是 apache benchmark 被广泛使用的原因;
apache benchmark, 其对应的命令被简称为 ab, 是 httpd-tools 里使用最广泛的工具;
httpd-tools 安装
直接用 yum install ab 是找不到软件包的, 因为 ab 并没有单独提供, 而是封装在 httpd-tools 里面;1
sudo yum install -y httpd-tools
安装完后, httpd-tools 提供的系列工具如下:1
2
3
4
5
6
7apache benchmark
htdbm
htdigest
htpassword
httxt2dbm
logresolve
后面的 5 个工具, 都是与 apache 服务器相关的辅助工具, 一旦脱离了 apache server 则作用有限; 但是 apache benchmark 却不一样, 虽然它本来的设计目的也是为了压测 apache server, 但既然是 http 请求, 那么大部分 web server 都可以使用其作压力测试;
ab 的使用方法
1 | # 最简单使用: -c concurrency 并发数; -n requests 请求数量 |
ab 的输出分析
顺利完成测试任务的输出
1 | # 部分 version, licene 信息已省略 |
这里有一个需要注意的地方: failed requests;
ab 请求失败的原因分为几类:
- Connect: tcp 连接错误, 属于网路问题;
- Length: http response 的 Content-Length 与第一次接收的值不一致, 这种属于业务问题, 并不能严格将其归为失败;
- Exceptions: 服务器端异常, 一般 status code 为 5XX;
不过, 如果返回客户端错误 4XX, ab 不会判定为 failed requests, 这里需要注意;
1 | Failed requests: 89 (Connect: 0, Receive: 0, Length: 89, Exceptions: 0) |
如上所示, 像 baidu.com 这种属于动态网页, 每次返回的内容长度不固定, 所以大量请求被 ab 判定为 failed requests;
类似这种情况的场景很多, 所以这就提醒我们在使用 ab 作压力测试时, 构造的 mock 接口一定要能够返回固定长度的内容, 而被测试接口的返回内容校验, 应该放在 mock 接口内部逻辑中实现; 一旦校验失败, 不要在返回内容上作标记, 而是直接抛出异常, 以让 ab 识别为 failed requests;
1 | Write errors: 0 |
传输各个阶段的统计指标:1
2
3
4
5
6
7Connection Times (ms)
# 最短时间 平均时间 标准差 中位数时间 最长时间
min mean [+/-sd] median max
Connect: 36 38 0.8 38 39
Processing: 113 154 14.5 152 198
Waiting: 37 39 1.0 39 42
Total: 150 192 14.7 190 237
最后是 rt 响应时间的分位数统计:1
2
3
4
5
6
7
8
9
10Percentage of the requests served within a certain time (ms)
50% 190
66% 192
75% 194
80% 195
90% 198
95% 230
98% 234
99% 237
100% 237 (longest request)
测试被迫终止
以上是一个顺利完成压测任务的输出; 有的时候压力测试并不会很顺利得结束, 如果压得比较猛, 可能待测服务会发生各种问题, 这时 ab 就有可能被迫提前终止任务;
情况一 tcp connection error1
2apr_socket_recv: Connection timed out (110)
apr_socket_recv: Connection reset by peer (104)
当 ab 遇到 connection error 时默认会直接 exit; 此时可以使用 -r 选项:
-r Don’t exit on socket receive errors.
这样 ab 就不会在遇到 connection error 时退出了; 但这有时并不能从根本上解决问题, 比如在某些高并发环境下, 待测服务所在的系统内核开启了 SYN flood 攻击保护, 故意拖慢了请求速度, 造成 connection error, 这可能会导致所有的请求都失败, 从而失去了压测意义; 这种情况下, 往往需要调整内核参数, 关闭一些安全保护:1
2# 关闭洪流攻击保护
net.ipv4.tcp_syncookies = 0
情况二 tcp read/write error1
apr_pollset_poll: The timeout specified has expired (70007)
以上输出反映了 ab 在测试途中遇到了 read/write timeout, 请求服务超过了选项 -s 设置的 timeout 时间; 这其实是一个不合理的设计, 一个请求超时是很正常的事情, 但是它一遇到超时就直接 exit, 这就让使用者不爽了, 它完全可以放到最后的统计里面的;
但是除非你修改 ab 的 source code 重新编译, 否则对于这种错误你也只能是修改 -s 增大超时时间了;
一个典型的使用实践
光说不练肯定是不行的, 这里正好有一个比较系统性地压力测试报告, 其将 apache benchmark 作为了一个主要的分析工具, 可以分享一下: berkeley db 7.x 压力测试报告;
其他类似工具
有一个与 ab 类似的开源 http 压力测试工具: wrk - a HTTP benchmarking tool, 在 github 上也获得了 1.7 万的 stars;
wrk 与 ab 的类似之处在于它们都是基于 http 的命令行工具, 通过命令选项控制压测条件; 但是 wrk 在某种程度上只能算是 ab 的一个子集:
wrk 只能通过给定一个确定的压测时间限制, 在给定的时间内以给定的并发度请求, 当时间到了, 压力测试结束, 其并不能设置请求多少次后结束测试;
另外, wrk 的输出与 ab 也不太相同:1
2
3
4
5
6
7
8
9
10
11Running 30s test @ http://127.0.0.1:8080/index.html
12 threads and 400 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 635.91us 0.89ms 12.92ms 93.69%
Req/Sec 56.20k 8.07k 62.00k 86.54%
22464657 requests in 30.00s, 17.76GB read
Requests/sec: 748868.53
Transfer/sec: 606.33MB
相比 ab 的报告略显简洁, 只能说是见仁见智吧;