ApacheBench 请求基线、Keep-Alive 与输出解释手册
ab 的优势是简单,限制也写在官方文档里
ApacheBench,命令名 ab,属于 Apache HTTP Server 的配套程序。它能用固定请求总量和并发快速验证一个 HTTP 端点,适合低并发冒烟、短连接与 Keep-Alive 对照、已有历史基线复测。它不是协议完整性工具,也不是现代多协议负载平台。Apache 官方明确列出固定缓冲、HTTP/1.x 实现不完整和客户端自身可能成为性能瓶颈等限制。
简单工具最容易制造过度结论。Complete requests 不等于业务成功,Failed requests 不包含所有非 2xx 语义,两个同名 Time per request 又使用不同公式。ab 默认不启用 Keep-Alive,忘记 -k 会把 TCP/TLS 建连大量混入结果。它可以提供稳定小基线,前提是报告不把它能看到的数字扩写成它没有测量的事实。
安装身份跟随 Apache HTTP Server 工具链
Debian 与 Ubuntu 常把 ab 放在 apache2-utils,Fedora 与 RHEL 系常见于 httpd-tools。软件包版本、发行方补丁和 TLS 链接库共同决定能力,不能只登记“机器上有 ab”。
sudo apt-get update
sudo apt-get install apache2-utils
ab -V
ab -h另一类发行版使用:
sudo dnf install httpd-tools
ab -V
rpm -q httpd-tools本文以 Apache HTTP Server 2.4 当前文档解释参数,不硬编码一个会过期的补丁号。团队镜像应锁定实际包版本、仓库、镜像 digest 和 ab -V 输出。HTTPS 测试还要记录 TLS 库与协商结果;同一个 ab 版本字符串链接不同密码库时,能力和性能可能不同。
升级先在独立工具镜像中运行固定 localhost 契约,再比较生产近似环境。回退不是重新安装一个“差不多的 2.4”,而是恢复原镜像 digest、包仓库快照和发压机规格。
-n、-c 和 -t 不能互相代替
ab -n 3000 -c 20 -k -s 2 \
-H "Accept: application/json" \
-H "X-Load-Test: ab-baseline" \
https://perf.example.com/health-n 3000 是计划完成的请求总量,-c 20 是同时推进的请求数,-k 开启 HTTP Keep-Alive,-s 2 是 socket 超时。并发二十不表示每秒二十次,也不表示二十条连接会从头到尾不变;具体连接取决于 Keep-Alive、服务端响应和错误。
-t 30 设置最多运行时长,并在内部隐含 -n 50000。它不是无限请求的精准三十秒恒定速率,更不是目标 QPS。请求先达到隐含上限或错误提前终止时,实际持续时间会不同。报告使用 Time taken for tests,不能用命令参数冒充真实窗口。
| 参数 | 语义 | 容易造成的错误结论 |
|---|---|---|
-n | 整个会话的请求总量 | 被当成每秒请求数 |
-c | 同时执行的请求数量 | 被当成固定到达率 |
-t | 最长测试时限,并隐含请求上限 | 被当成严格持续时间模型 |
-s | socket 等待超时 | 被当成会话硬超时 |
-k | 请求服务端保持连接 | 被误以为默认开启 |
ab 仍是闭环工具:有限并发槽等待响应后继续。服务变慢时,下一轮请求变晚,实际入口速率下降。需要固定外部到达率时,选择支持开放模型的工具,不从 -c 推导 RPS。
Keep-Alive 决定你是否把建连成本压进每个请求
ab 默认短连接。对 HTTPS 端点不带 -k,可能让每批请求频繁建立 TCP 与 TLS,测到的主要是 accept、握手、证书和临时端口。带 -k 后,并发槽可以复用连接,更接近多数现代客户端。两种结果都可能有价值,但它们回答不同问题。
对照实验只改变 -k:
ab -n 1000 -c 10 https://perf.example.com/health
ab -n 1000 -c 10 -k https://perf.example.com/health报告同步记录服务端新建连接、活跃连接、TLS handshakes 和业务成功数。若第二轮吞吐更高,不能写成“应用优化有效”,因为改变的是连接策略。短连接结果可用于暴露入口与握手能力,长连接结果可用于稳定业务请求基线。
服务端不接受 Keep-Alive 或主动限制每连接请求数时,-k 也不保证一条连接覆盖整个会话。ab 输出的 Keep-Alive requests 和服务端连接指标负责证明实际复用,而不是命令行开关本身。
固定方法、body 和认证语义
POST 使用文件与内容类型:
ab -n 100 -c 5 -p payload.json -T application/json \
-H "X-Load-Test: ab-write-baseline" \
https://perf.example.com/api/itemsPUT 对应 -u,自定义方法可使用当前 2.4 文档中的 -m。请求体、Content-Type、Host、Accept、Cookie、重定向与压缩必须先由单请求客户端验证。ab 不会替团队判断返回 JSON 中的业务字段是否成功,服务端应按测试标记维护独立业务成功计数。
-A username:password 发送 Basic Auth,内容只是 Base64,必须叠加 TLS。命令行凭证会出现在历史、录屏或进程信息中,不适合共享环境。-E 可以提供包含证书链和私钥的 PEM 客户端证书,该文件由短期密钥流程注入,权限最小化并在实验后销毁。密码、Cookie、Bearer Token 与私钥都不能写入仓库或原始报告。
ab 提供 -X 代理参数。经代理会改变 DNS、连接复用、TLS 终止、限流与缓存路径,必须作为独立实验。企业证书或 mTLS 失败时先修正信任链,不通过跳过证书验证得到一份不可比较的成功输出。
长度失败是证据,不是一个需要消掉的红字
ab 以首个成功响应的文档长度为基准。后续响应长度不同,可能进入 Failed requests 的 length 分类。动态时间戳、请求 ID 或压缩变化会造成合法差异,截断、错误模板和内容协商错误也会造成危险差异。
-l 让 ab 不因响应长度变化报错,只能在抽样确认变化属于合法业务后使用。为了让失败数归零而直接加 -l,会把截断或错误响应藏起来。更可靠的顺序是保存小规模响应样本,核对状态码、Content-Type、body 结构与服务端业务计数,再决定长度是否应当恒定。
curl -fsS -D headers-1.txt https://perf.example.com/api/items -o body-1.json
curl -fsS -D headers-2.txt https://perf.example.com/api/items -o body-2.json
wc -c body-1.json body-2.json若响应本来就应固定,长度变化应作为回归继续失败。若响应包含合法动态字段,-l 只解除这个特定检查,状态码、读取错误、业务字段和服务端计数仍需独立门禁。
本地实验把 Keep-Alive 和长度检查做成可见事实
下面的服务只监听回环,记录请求与连接,并提供固定长度和变化长度两条路径:
LAB="$(mktemp -d "${TMPDIR:-/tmp}/ab-lab-XXXXXXXX")"
chmod 700 "$LAB"
cat > "$LAB/server.mjs" <<'JS'
import http from 'node:http';
let requests = 0;
let connections = 0;
const server = http.createServer((req, res) => {
if (req.url === '/stats') return res.end(JSON.stringify({ requests, connections }));
requests++;
const body = req.url === '/vary' ? `ok-${requests}\n` : 'ok\n';
res.writeHead(200, { 'content-type': 'text/plain', 'content-length': Buffer.byteLength(body) });
res.end(body);
});
server.on('connection', () => connections++);
server.listen(0, '127.0.0.1', () => console.log(`PORT=${server.address().port}`));
JS
node "$LAB/server.mjs" > "$LAB/server.log" 2>&1 &
PID=$!
for _ in $(seq 1 50); do grep -q '^PORT=' "$LAB/server.log" && break; sleep 0.1; done
PORT="$(sed -n 's/^PORT=//p' "$LAB/server.log")"
BASE="http://127.0.0.1:$PORT"先做 Keep-Alive:
ab -n 100 -c 5 -k "$BASE/fixed"
curl -fsS "$BASE/stats"稳定证据是 Complete requests 为一百、Failed requests 为零,服务端请求增加一百,而新连接远少于一百。随后不带 -k 重跑,同样一百次业务成功,却新增接近请求量的连接。连接数还包含读取 stats 的 curl,不应死记一个精确值。
变化长度路径应触发 length 失败:
ab -n 20 -c 2 "$BASE/vary" || true
ab -n 20 -c 2 -l "$BASE/vary"
kill "$PID" 2>/dev/null || true
wait "$PID" 2>/dev/null || true
rm -rf -- "$LAB"第二条命令不报告长度错误,只证明 -l 改变了判定,并不证明每个 body 都业务正确。实验必须把两次输出和服务端计数一起保存。
Complete、Failed 与 Non-2xx 分属不同语义
Complete requests 是收到完整响应的数量。Failed requests 进一步按 connect、receive、length、exception 等类别展示。Write errors 是发送阶段问题。Non-2xx responses 单独统计非 2xx 状态,所有响应为 2xx 时这一行可能根本不出现。
因此 Failed requests: 0 不能证明状态码全对。一个快速 401 可能完整接收、不触发长度异常,却完全没有进入业务成功路径。报告应把 Non-2xx、服务端状态码分布和业务成功计数并列。
Requests per second 等于完成请求数除以测试总时间。它不是计划到达率,也没有排除错误捷径。Transfer rate 同时受响应大小影响;优化后 body 变小,吞吐增加可能来自减少传输,而不是应用计算更快。
两个 Time per request 使用不同公式
ab 输出的第一项 Time per request 包含并发因子,公式是 concurrency × time × 1000 / completed;第二项写着 across all concurrent requests,公式是 time × 1000 / completed。两个值名称相同,不能随手复制其中一个并标成 P95 或用户延迟。
百分比分布表更接近延迟分位,但仍要说明 ab 的采样与当前请求模型。-e 可以导出按百分比聚合的 CSV,-g 输出 gnuplot 数据。原始输出和导出文件都应绑定完整命令、工具版本与实验窗口。
平均值与 median 偏离很大时,ab 会给出相关警告;-S 可以关闭中位数、标准差和警告,不应为了报告简洁而默认使用。尾部离散正是容量拐点的重要信号。
官方已提醒可能测到的是 ab 自身
Apache 文档明确指出 ab 实现和解析路径可能成为性能瓶颈。并发提高后 QPS 不再增长,而服务端 CPU、连接池与下游仍有余量,先检查发压机单核 CPU、网卡、文件描述符、临时端口和 write/read errors。把客户端饱和写成服务端容量上限是最常见的误判之一。
同样协议与响应大小下,先对本地静态服务建立 ab 生成能力基线。正式实验中用另一台相同规格发压机复测;新增生成器后总吞吐继续增长,说明旧 ab 已经是瓶颈。需要高连接吞吐时,转向 wrk;需要现代场景、开放模型或分布式执行时,转向 k6、JMeter 或专门平台。
ab 没有完整实现 HTTP/1.x,也不是 HTTP/2 工具。涉及 chunked、特殊响应头、协议升级或现代多路复用时,先验证兼容性,不能把解析失败归咎于服务器。协议正确性由专门客户端与集成测试承担。
安全边界不能交给 -c 和 -n
目标必须来自显式 allowlist,并由 runner 网络 ACL 和入口规则限制来源。共享或生产近似环境要批准目标、方法、请求量、并发、时限、测试账号、停止条件和 owner。-n 100 很小也可能调用删除接口,低并发不等于低风险。
写请求使用隔离租户、幂等键和可按测试标记清理的数据。目标侧保留独立流量开关;错误率、尾延迟、池、队列、数据库或业务告警越线时,停止不依赖操作者终端。结束后关闭入口规则、撤销凭证、清理数据并验证服务指标恢复。
证据目录保存 ab -V、包身份、链接库、脱敏命令、发压机规格、DNS/IP、TLS、Keep-Alive、原始输出、服务端连接与业务计数。基线条件改变时创建新系列,不用一个 Requests per second 覆盖历史。
参数、输出公式和已知限制继续以 ApacheBench 2.4 官方文档为准。需要 HTTP/2 或简单 worker 限速时进入 hey 手册。
