Apache Bench(ab)压力测试概述-从0到1涵盖各大使用场景

2022-07-20 12:19:20 浏览数 (1)

前言

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。

代码语言:shell复制
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:

代码语言:shell复制
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]

代码语言:shell复制
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压测方案概述(亮色版)

0 人点赞