前言
Apache Bench(ab)主要用来为HTTP服务提供性能压力测试,以下总结了ab在日常使用中用到的压测方法。
ab比较贴近实用主义,没有像其他工具一样包罗大量使用场景很少的参数,当看到man或者--help帮助文档展示的密密麻麻的参数时,难免会劝退一部分人。
本文偏向生产、日常使用,不会将每个参数都演练一遍,当你翻看这篇文章时,想必是带着解决问题的需求来的,尽量能让你用最少的时间成本,对业务性能、负载能力有一个大致完整的评估。
一、如何安装ab
1.Linux
建议直接从从软件源安装,默认会安装好各个组件需要的依赖,而编译安装需要手动安装好依赖,每个发行版依赖组件的命名可能也会不一样,途中也可能会遇到各种报错,就像是一个递归,你要深陷进去,因此我更偏向于实用与简约主义,除非软件源自带的版本太老不满足需求,或者翻看每个版本的Changelog后确定要指定某一个安装版本,那么你可以选择编译安装。安装不是本文的重点,尽量一笔带过。
发行版 | 安装命令 |
---|---|
Arch | pacman -Sy apache |
CentOS | yum install -y httpd-tools |
Debian/Ubuntu | apt install -y apache2-utils |
2.Windows
Windows客户端见此链接,下载压缩文件后解压,将bin目录设置为环境变量或cmd进入到bin目录下,之后使用ab命令即可。
二、压测机参数优化
使用压测工具进行压力测试时,都会受限于工具性能或机器本身性能,如果受限于机器本身硬件性能,则建议使用多台客户端压测,同时单机本身也有一些网络相关的内核参数可以优化。
1.tcp_fin_timeout
MSL即:Maximum Segment Lifetime,它是定义任何报文允许生存的最长时间,超过则丢弃。
那么tcp_fin_timeout
就是用来设置MSL的,默认为60s。
cat /proc/sys/net/ipv4/tcp_fin_timeout
2.tcp_tw_reuse
linux分配给各个用户的文件句柄数是有限的,连接断开后,端口及相关资源不会立即释放,而是进入time_wait
状态,等待2MSL后才进入CLOSE
状态,这时候连接才被释放,可以参照RFC793的定义:
显然,在压测场景下,我们需要短时间内让压测机尽可能建立更多的TCP连接,而time_wait
状态如果一直保持2MSL(即2分钟),那么这2MSL里对应的time_wait
占用的句柄一旦到达上限,无法新增新的TCP连接。
我们需要把tcp_tw_reuse
参数打开,让系统开启time_wait
状态复用,允许将处于该状态的资源重新用于新的TCP连接。
临时打开即可:
代码语言:shell复制sysctl -w sysctl net.ipv4.tcp_tw_reuse = 1
或者:
代码语言:txt复制echo 1 > /proc/sys/net/ipv4/tcp_tw_reuse
如果需要永久打开,则需要写入到/etc/sysctl.conf
,并且执行sysctl -p
生效。
3.tcp_tw_recycle
顾名思义,此参数用于回收time_wait
状态的资源,一般会和tcp_tw_reuse
配合使用。
临时打开:
代码语言:shell复制sysctl -w sysctl net.ipv4.tcp_tw_recycle = 1
或者:
代码语言:shell复制echo 1 > /proc/sys/net/ipv4/tcp_tw_recycle
如果需要永久打开,则需要写入到/etc/sysctl.conf
,并且执行sysctl -p
生效。
4.ip_local_port_range
此参数表示本地向外部连接能够使用的端口范围,参数默认值是32768 60999
,也就是单机器能够同时建立一共28231个连接:
根据压测场景,适当调整此参数。
比如多增加1000个端口,可以是:
代码语言:shell复制echo '32768 61999' > /proc/sys/net/ipv4/ip_local_port_range
5.统计各个状态的连接数
最后,统计各个状态的连接数,可以是:
代码语言:shell复制netstat -an|awk '/tcp/{print $6}'|sort |uniq -c
netstat -an|awk '/tcp/{s[$6] }END{for(i in s)print i,s[i]}'
三、开始压力测试之旅
使用--help
可以看到参数支持列表:
压测格式为:ab [options] [http[s]]://]hostname[:port]/path/
注意:options不要放在hostname后面,不支持这么用。
1.压测短连接(-m/-c/-n)
使用HTTP GET请求,每次最高并发1000,压测数量10000次,:
代码语言:shell复制ab -m GET -c 1000 -n 10000 http://hostname/
注意:目标后面一定要接路径,如果是根路径则带上"/",比如上面的hostname/,否则会报错。
释义:
参数 | 释义 |
---|---|
-m | 指定HTTP请求方法,如GET/POST/HEAD/PUT等,不指定此参数默认发GET。 |
-c | 同一时间最大请求次数,即并发请求数。 |
-n | 总请求次数。 |
输出信息:
代码语言:shell复制Server Software: nginx
Server Hostname: xxx
Server Port: 80
Document Path: /
Document Length: 231 bytes #返回的分页大小
Concurrency Level: 1000 #并发连接数1000
Time taken for tests: 0.457 seconds #总耗时
Complete requests: 10000 #总完成请求
Failed requests: 0 #失败请求
Total transferred: 4560000 bytes #总传输字节数
HTML transferred: 2310000 bytes #html传输字节数
Requests per second: 21896.07 [#/sec] (mean) #每秒平均请求数
Time per request: 45.670 [ms] (mean #每次并发请求时间
Time per request: 0.046 [ms] (mean, across all concurrent requests) #每个请求实际运行平均时间。
Transfer rate: 9750.59 [Kbytes/sec] received #传输速率
Connection Times (ms) #压力测试时的连接处理时间
min mean[ /-sd] median max
Connect: 1 20 2.7 20 28
Processing: 7 24 5.0 23 43
Waiting: 1 17 4.8 16 33
Total: 22 43 4.1 43 57
Percentage of the requests served within a certain time (ms) #在一定时间内的请求响应时间占比
50% 43 #请求数完成一半后,统计的平均响应时间,下面的以此类推
66% 44
75% 46
80% 47
90% 48
95% 50
98% 52
99% 55
100% 57 (longest request)
对应的抓包:
不难看出,ab每一次GET都会和目的端握手,不会复用同一个TCP连接,在很小的时间内发送了10000次GET请求,并建立10000次,产生10000个会话。
如果需要更大剂量,如:
代码语言:shell复制ab -m GET -c 10000 -n 1000000 http://hostname/
-c 10000
,每次最多发起10000个请求,因为并发量较大,可能会超出单个用户打开文件最大的数量,此时需要设置下ulimit:
ulimit -n 65535
ulimit 是一种 Linux 系统的内键功能,它具有一套参数集,用于为由它生成的 shell进程及其子进程的资源使用设置限制。 设置用户open files(用户可以打开文件的最大数目)。
查看当前用户已经打开的文件数量,可以使用如下命令:
代码语言:shell复制lsof -u `whoami` |wc -l
2.压测长连接(-k)
代码语言:shell复制ab -m GET -k -c 1000 -n 100000 http://hostname/
参数 | 释义 |
---|---|
-m | 指定HTTP请求方法,如GET/POST/HEAD/PUT等,不指定此参数默认发GET。 |
-c | 同一时间最大请求次数,即并发请求数。 |
-n | 总请求次数。 |
-k | keepalive,使用长连接,尽量复用一个TCP连接。 |
可以明显看到长连接模式压测,QPS有明显提升,因为每次GET资源都尽量复用一个连接,不像短连接一样每次GET都要握手再GET。
同时从HTTP头部字段的Connetction:keep-alive
可以看出-k参数压测模式就是在HTTP头部加入此字段:
为什么压测过程中对端都没有出现超时或响应时间明显变长?
至少可以说明一点,此时客户端请求量级<=服务器业务层处理量级,所以只用一台客户端压测服务端,如果对端没有达到性能瓶颈,那么可以考虑增大压测并发或多个客户端同时压测。
下面以一台外网web测试机为展示:
上图可以看到服务端已经处于高负载模式下,无法正常响应请求并返回502状态码。
使用htop观测服务端的负载及内存占用情况,CPU、内存均满载:
统计服务端已经建立的连接:
代码语言:shell复制lsof -i :80|awk '/ESTABLISHED/{S =1}END{print S}'
这1000个连接即为ab -c 1000
指定的数量。
如果想统计指定IP已经建立的连接,可以是:
代码语言:shell复制lsof -i :80|awk '/ESTABLISHED/&&/ipaddress/{S =1}END{print S}'
3.指定超时时间(-s)
-s s 用于指定超时时间,单位秒。从man帮助文档可以看出此参数在2.4.4之后版本可用,且默认是30s超时。
用法也很简单,配合其他压测参数一起使用即可。
长连接模式下,每次GET请求最多5s视为超时请求:
代码语言:shell复制ab -m GET -s 3 -k -c 1000 -n 10000 http://hostname/
短连接模式下,每次POST请求最多5s视为超时请求:
代码语言:shell复制ab -m POST -s 5 -c 1000 -n 10000 'http://hostname/?username=xxx&passwd=xxx'
4.指定TLS版本压测(-f)
-f 指定TLS协议版本:
如何知道对端支持什么协议版本?
代码语言:shell复制openssl s_client -connect hostname:443 -tls1/-tls1_2/-tls1_3 #注意这里的"/"不能直接写进去,每次只入参一个tls版本,多个则用循环遍历下即可
或者使用在线网站查询下支持的TLS协议版本。
示例:
指定TLS1.1版本:
代码语言:shell复制ab -f TLS1.1 -m GET -k -c 1000 -n 10000 https://hostname/
抓包可以清晰看到,客户端在TLS握手包里向服务端声明自己使用TLS1.1版本:
HTTPS场景下,不同版本的TLS、cipher套件,加解密强度不一致,损耗的性能也会不一致,因此也会直观体现在QPS上。
5.插入cookies(-C)
-C
用于指定cookies信息,格式类似于键值对:
当需要压测某些需要登录的场景下,通过插入cookies来保持登陆状态:
代码语言:shell复制ab -m GET -k -c 500 -n 2000 -C 'cookie-name=value' http://hostname/
通过插入cookies,可以压测只有登陆后才能访问的任意资源。
6.指定代理服务(-X)
通过代理来压测,格式: -X proxy[:port]
ab -m GET -X proxyip:port -k -c 500 -n 2000 http://hostname/
类似于nginx的七层反向代理,客户端请求发给代理服务器,由代理服务器去请求真实服务器并返回给客户端。
7.设置HTTP头部(-H)
-H
自定义HTTP头部内容
指定HOST:
代码语言:shell复制ab -m GET -H 'Host:test.com' -c 1000 -n 10000000 -k http://hostname/
同时指定HOST、UA信息等:
代码语言:shell复制ab -m GET -H 'Host:domain' -H 'User-Agent: RokasYang/1.0 -c 1000 -n 10000000 -k http://hostname/
任何HTTP头部都能通过-H
参数指定。
8.指定压测总时间(-t)
-t
用于限定压测的总时长,类似于timeout命令,单位为秒,默认不指定则没有时间限制。
限定最多压测5s结束:
代码语言:shell复制ab -m GET -t 5 -c 1000 -n 10000000 http://hostname/
同时也可以用capinfos
命令来查看报文中的首尾包时间,间隔5s:
9.用HAED请求替代GET(-i)
此参数其实也可以用-m HEAD来替代,效果一样,都是发HEAD请求。
代码语言:shell复制ab -i -k -c 1000 -n 10000000 http://hostname/
10.输出到文件(-g/-e)
-g
将每个结果的测量值输出到文件中。
-e
记录所有请求在百分比进度条(1%-100%)中消耗的时间,后面一般接csv格式文件,即Excel格式文件。
记录每次请求的时间:
代码语言:shell复制ab -k -c 1000 -n 10000 -g output http://hostname/
因此1万次请求就会记录一万次请求(除去第一行banner信息)。
记录所有请求在不同进度下的消耗时间:
代码语言:shell复制ab -k -c 1000 -n 10000 -e output.csv http://hostname/
四、总结
如果需要体现业务侧偏向最真实或者说最高瓶颈的QPS性能,长连接场景更适用,多个请求复用一个TCP连接,节省了大量短连接新建连接的操作,也正由于此,长连接QPS一直都会比短连接高。
如果客户端数量较少,对端业务情况能正常承载响应并且没有任何5XX HTTP状态码的话,可以增大压测的客户端数,想要拿捏服务最真实的QPS性能,就需要找到业务正常到业务不正常/无法响应的QPS临界点。
如果用不同客户端压测同一个服务端,QPS参差不齐,这也是正常的,不同客户端硬件性能不一样的情况下,收发包性能是有限的,且QPS和网络延时也有密不可分的关系,就算是几ms的差距,也可能带来倍数级别的QPS差距,特别是小包场景下,每个连接需要交互的包量很多,这种场景下延时差距带来的QPS差距不容小嘘。
附带PDF版本:
Apache Bench压测方案概述
Apache Bench压测方案概述(亮色版)