Skip to content

JMeter 断言与指标解读 ​

T2-014必学进阶约 60 分钟JDK 21测试专项 B:接口·UI·性能 · 性能测试

1. 本节定位 ​

《JMeter 压力测试建模》讲了怎么压;本节讲怎么判断压出来的是好数据还是坏数据:

text
压测跑完了,屏幕上一堆数字 ——
   哪些能信?哪些是假的?哪些指标会互相骗人?

三个最致命的问题:

问题后果
压测没带断言 ✗✗✗压的是 500 错误页,QPS 特别漂亮
只看平均值 ✗✗尾延迟被掩盖,用户实际等了 5 秒
失败请求算进 QPS ✗✗数字虚高,掩盖了故障

2. 学习目标 ​

  • 说出"压测必须带断言"的理由,并知道不带断言会得出什么错误结论 ★★★;
  • 能解释"为什么用分位数而不是平均值" ★★★;
  • 说出 P50 / P90 / P95 / P99 各自的含义与适用场景 ★★;
  • 说清"错误率与 QPS 的关系",以及"含失败的 QPS"为什么是粉饰 ✗✗;
  • 会读 JMeter 聚合报告的关键列;
  • 知道"自动化结论"不能替代"自己读数据" ★★★。

3. 前置知识检查 ​

  • 完成 T2-003 的压测实测了吗?(四档数据)✓
  • 知道分位数是什么吗?(T2-003 的 5.4 节)✓

4. 为什么需要这个知识 ​

先看一个真实的"漂亮报告":

text
压测结果:平均响应时间 12 ms,QPS 4200,错误率 0%
结论:系统性能优秀 ✓

而实际上:

被藏起来的事实怎么藏的
接口返回的是 500没加断言 → 只要"有响应"就算成功 ✗✗✗
有 3% 的请求超时超时的没算进去(只统计了"完成的")✗
P99 是 8 秒报告只有 Average ✗

这份报告不是"错的",而是"选出来的" ✗ ——这是压测最危险的失败模式 ✓✓

5. 核心概念 ​

5.1 压测必须带断言 ★★★ ​

这是本节的第一条铁律。

没有断言的压测:

text
JMeter 认为"收到 HTTP 响应 = 成功"          ← 默认行为 ★
所以:
  · 返回 500 → 也算成功 ✗✗✗
  · 返回业务错误码 → 也算成功 ✗✗
  · 返回空内容 / 错误页面 → 也算成功 ✗✗

后果:一个"所有请求都失败"的接口,压测报告可以非常漂亮 ✗

text
接口坏了 → 立刻返回 500(不做任何业务处理)→ 响应极快 → QPS 极高
压测报告:"QPS 8000,平均 1.2ms,错误率 0%"      ← 全都是假的 ✗✗✗

所以必须加断言(JMeter 里的 Response Assertion):

断言检查什么
响应码断言200 / 201(按接口约定)★
响应体断言含关键字,或 JSON 字段值正确 ✓
响应时间断言超过阈值直接判失败(压测里很有用)✓
响应大小断言防止"空响应也算成功" ✓

★ 一个简单有效的做法:

text
断言:"响应码 = 200 或 201" + "响应体包含 orderNo"

这样"接口坏了"会立刻表现为"错误率飙升" ✓ ——而不是"性能优秀" ✗

本项目的压测工具就是这么做的(LoadTestLesson.send):

java
if (response.statusCode() >= 400) {
    throw new IllegalStateException("HTTP " + response.statusCode());   // ★ 计入失败
}

5.2 为什么用分位数而不是平均值 ★★★ ​

先看一组数据:

text
100 个请求:99 个 10ms + 1 个 5000ms

Average = 59.9 ms      ← "还行"
P50     = 10 ms        ← 一半用户比这快
P95     = 10 ms        ← 95% 的用户很快 ✓
P99     = 5000 ms      ← ★ 1% 的用户等了 5 秒 ✗✗✗
Max     = 5000 ms

平均值的三个问题:

问题说明
掩盖尾部 ★★1 个坏样本被 99 个好样本"稀释" ✗
没有"用户视角"用户不关心平均,他关心"我这次快不快" ✓
不可比较两个系统可能平均值相同、分布完全不同 ⚠️

各分位的含义与用途:

分位含义用来看
P50(中位数)一半请求比它快"典型用户"的体验 ✓
P9090% 比它快一般慢用户
P95 ★★95% 比它快SLA 最常用(如"P95 < 200ms")✓
P99 ★★★99% 比它快尾延迟——最容易出问题的地方 ✓✓
Max最慢的一次可能是 GC / 网络抖动 / 冷启动 ⚠️

★ 为什么 P99 特别重要:

一个日活 10 万的系统,P99 就是每天有 1000 个用户遇到"最差体验" ✓

而且"最慢的请求"往往对应最复杂的业务场景(大订单、复杂查询)——恰恰是最重要的用户 ✓✓

本轮的实测数据正好印证(T2-003 第 7 节):

text
并发 64:QPS 68.8,P50 = 845 ms,P99 = 2011 ms

如果只报平均值:读者会以为"约 1 秒",而实际上"有 1% 的用户等了 2 秒" ✗

5.3 错误率与 QPS 的关系 ★★★ ​

先问一个问题:QPS 里的"请求",包不包括失败的?

口径数字后果
含失败数字好看 ✗掩盖故障 ✗✗✗
只算成功 ★数字诚实 ✓能反映真实可用吞吐 ✓

★ 关键认识:

失败的请求往往比成功的"更快" ——因为它没干活 ✗

所以"含失败的 QPS"会虚高 ——系统越坏,数字越漂亮 ✗✗✗

正确做法:

text
① QPS 与错误率分开报
② 用"成功 QPS"作为容量指标
③ 错误率超过阈值(如 0.1%)→ 这次压测的 QPS 数字作废 ✗

两个必须一起看的指标:

组合含义
QPS 上升 + 错误率 0健康 ✓
QPS 上升 + 错误率上升"虚高"——实际有效吞吐在下降 ✗✗
QPS 平台 + 延迟上升到拐点了 ★(T2-003 的 5.5)
QPS 下降 + 错误率上升过载,系统在崩溃 ✗✗✗

5.4 JMeter 聚合报告怎么读 ​

JMeter 的 Aggregate Report 字段:

字段含义注意
# Samples总请求数含失败 ⚠️
Average平均响应时间不要只看它 ✗
MedianP50✓
90% Line / 95% Line / 99% Line ★★分位延迟重点看这三列 ✓✓
Min / Max最小 / 最大Max 可能是异常值 ⚠️
Error % ★★错误率必须与 Throughput 一起看
Throughput每秒完成请求数(含失败)⚠️ 注意口径
Received KB/sec吞吐的带宽大响应体时是瓶颈 ✓

★ 读报告的顺序(这个顺序很重要):

text
① 先看 Error %          —— 错了就不必往下看了 ✗
② 再看 Throughput       —— 到多少
③ 再看 95%/99% Line     —— 用户感受到多慢
④ 最后看 Average/Max    —— 参考

为什么这个顺序:

错误率高的数据是"无效数据" ——先排除它 ✓

然后看吞吐(能扛多少),再看延迟(代价多大) ✓

平均值放最后——它只是一个参考 ✓

5.5 "自动解读"的陷阱 ★★★ ​

本节最值得记住的一条——它来自本轮实测的一个真实发现 ✓

我的压测工具在结尾自动打了一段"解读":

text
===== 3. 怎么读这几行数据 =====
  吞吐最高出现在并发 = 64(QPS=69)
  从 1 并发到 4 并发:QPS 上升,说明还有余量        ← ★ 这句话是错的 ✗✗
  继续加压如果 QPS 不再上升、而 P95 继续涨 —— 就到了「拐点」,系统开始排队

而实际数据是:

text
并发     QPS      P50
1       55.1     14
4       61.1     57       ← QPS 只涨 11%,而 P50 涨了 4 倍 ✗
16      68.7     213
64      68.8     845

★ "还有余量"是错的——真实情况是"并发 1 时就已经接近饱和" ✓✓

为什么会写错:那段解读是"写死的模板文案" ✗ ——它只判断"QPS 有没有涨",没看"涨了多少、代价是什么" ✗

这条教训极其重要:

工具的"自动结论"是最容易骗人的东西 ✗✗

因为:

  • 它的判断逻辑是固定的(写死在代码里)
  • 它不知道你的业务上下文(11% 的提升算不算"有余量"?取决于代价)
  • 它会给人"我已经读过数据了"的错觉 ⚠️

所以:工具只该负责"呈现数据",结论必须自己下 ✓✓

正确的读法(对比着看三个量):

该看什么本轮数据结论
QPS 涨幅 vs 并发涨幅 ★并发 ×64,QPS ×1.25严重不成比例 → 已饱和 ✗
延迟涨幅 vs 并发涨幅 ★★并发 ×64,P99 ×36延迟爆炸 → 加并发是有害的 ✗✗
错误率0%没崩,只是慢 ✓

★ 教学价值:这段错误的自动解读我保留在代码里了 ✓ ——它比任何"正确示范"更能说明问题 ✓

5.6 压测报告该写什么 ​

一份可用的压测报告(缺任何一项,报告就不可信):

项内容
① 环境 ★★机器配置、部署方式、压测机与被测服务是否分离、网络
② 被测目标哪个接口、什么数据、是否带参数
③ 压测模型并发、时长、思考时间、Ramp-up(T2-003 四要素)
④ 断言 ★★凭什么判定"这次请求成功"
⑤ 结果成功 QPS + P50/P95/P99 + 错误率(四件套)✓
⑥ 资源水位 ★服务端 CPU / 内存 / GC(T2-016)
⑦ 结论与有效范围 ★★"在什么环境下、什么负载区间内,这个结论成立"

★ 第 ⑦ 条最容易被忽略:

性能结论是"有条件的" ✓ ——"P95 < 200ms" 必须附上"在 50 QPS 以内" ✓

脱离条件的性能数字没有意义 ✗

6. 递进示例 ​

6.1 最小示例:加一条断言 ​

text
JMeter → HTTP 请求 → 添加 → 断言 → 响应断言
  Field to Test: Response Code
  Pattern: 200|201

6.2 业务示例:读一份报告 ​

见 5.4 的"读报告顺序" ✓

6.3 反例:只报平均值 + 无断言 ​

见第 4 节 ✓

7. 实测:把"错误解读"与"正确解读"摆在一起 ​

实测工具:labs/web-basics/src/main/java/cn/kb/learn/web/perf/LoadTestLesson.java

它的自动解读(错的):

text
从 1 并发到 4 并发:QPS 上升,说明还有余量        ← ✗ 与数据不符

数据本身(对的):

text
并发       总请求        QPS          P50(ms)    P95(ms)    P99(ms)
1        40         55.1         14         39         55
4        160        61.1         57         119        187
16       640        68.7         213        393        517
64       2560       68.8         845        1594       2011

正确的解读(三句话):

#结论依据
①吞吐上限约 69 QPS并发 16 → 64,QPS 68.7 → 68.8(零增长)★
②超出后延迟爆炸P99 从 55ms → 2011ms(36 倍)✗✗
③系统没有崩溃,只是排队错误率全程 0% ✓

★ 把"工具的结论"与"数据本身的结论"摆在一起,就是本节最好的教材 ✓✓

8. Debug 观察点 ​

  1. 故意让接口返回 500(改一行代码),不加断言的压测 → 观察 QPS 飙升、错误率 0% ✗✗ ——亲眼看一次"漂亮的假数据" ★★★;
  2. 加上断言再压一次 → 观察错误率变成 100% ✓ ——这才是真相 ✓;
  3. 对比"含失败的吞吐"与"成功吞吐":在过载时差距会很大 ✓;
  4. 把 P99 与 Max 单独打印:观察 Max 往往是 P99 的 2~3 倍(异常值)⚠️。

9. 常见错误 ​

#错误说明
1压测不带断言 ✗✗✗压的是错误响应(5.1)✗✗✗
2只报平均值 ✗✗掩盖尾延迟(5.2)✗
3把失败的算进 QPS 且不报错误率 ✗✗粉饰(5.3)✗
4拿"工具的自动结论"当结论 ✗✗见 5.5 ★★
5报告不写环境与压测参数不可复现、不可比较 ✗
6拿一次压测的数字当"永久容量"结论有条件、有有效期 ⚠️(代码/数据量变化后要重测)

10. 动手实验 ​

目标:亲手制造一次"漂亮的假数据",再看清真相。

步骤:

  1. 改 RawServlet 让它返回 500(临时),跑压测 → 观察 QPS 与错误率 ✗;
  2. 给压测工具加上"响应码断言"(其实已有),再跑一次 → 观察错误率 100%、QPS 虚高的对比 ★★;
  3. 改回来,跑出正常数据 ✓;
  4. 手算一遍:从四档数据里,算"每提高 1 QPS 需要付多少 P99"(性价比)★;
  5. 写一份完整报告(按 5.6 的七项)✓。

验收方式:能说出第 1、2 步两次压测的差别在哪、为什么。

扩展任务:给压测工具加"分档停止条件":

  • 当 P95 > 500ms 或 错误率 > 1% 时立即停止加压 ✓;
  • 说明:为什么"自动停止"比"压满所有档"更接近真实压测工具的做法(提示:避免把测试环境打到崩溃 ——T2-015)✓

11. 练习题 ​

  1. 判断:JMeter 默认会把返回 500 的请求算作失败。(对 / 错)
  2. 填空:报告里必须给的四件套是 、、、。
  3. 简答:为什么"含失败的 QPS"会虚高?
  4. (代码阅读)某报告:Samples=10000, Average=15ms, 99%Line=4200ms, Error=0.2%, Throughput=1200/s,这份数据有什么问题?
  5. (编程)给 LoadTestLesson 的输出加上"成功率"与"成功 QPS"两列,并说明在过载时这两列与总数差多少。
参考答案(要点)
  1. 错。JMeter 默认"收到响应即成功" ✗ ——500 也算成功 ✓ ——必须自己加断言 ✓★★
  2. 成功 QPS、P50/P95/P99、错误率、环境说明 ✓
  3. 因为失败的请求没干活(不查库、不算业务)→ 往往更快 ✗ ——系统越坏,含失败的 QPS 越高 ✗✗
  4. 问题:Average 15ms 与 99%Line 4200ms 差距 280 倍 ✗ ——说明有 1% 的请求极慢 ✓(如果只报 Average 就会得出"性能很好"的错误结论)✗ 另外 Error 0.2% 虽然不高,但在 1200 QPS 下 = 每秒 2.4 个用户看到错误 ⚠️ ——要看业务能否接受 ✓
  5. 要点:过载时"成功 QPS"可能只有总 QPS 的一部分 ✓ ——这正是"虚高"的量化体现 ✓

12. 掌握标准 ​

  • [ ] 知道 JMeter 默认"有响应即成功",压测必须加断言 ★★★;
  • [ ] 能解释"为什么用分位数而不是平均值" ★★★;
  • [ ] 知道 P50/P95/P99 各自的含义与用途 ✓;
  • [ ] 知道"含失败的 QPS"会虚高,报告必须给成功 QPS + 错误率 ★★★;
  • [ ] 会按"错误率 → 吞吐 → 分位 → 平均"的顺序读报告 ★;
  • [ ] 不盲信工具的自动结论 ★★★;
  • [ ] 能写出含"环境 + 参数 + 断言 + 四件套 + 有效范围"的报告 ✓。

13. 扩展资源 ​

14. 下一步学习 ​

《JMeter 参数化与关联》:让每个虚拟用户用不同的数据、并能"顺着上一个响应"发下一个请求。

后续知识

暂无

进度只保存在本机浏览器,可随时清除

个人学习项目:AI 生成内容均经人工复核,第三方资源仅作索引