第七章. 流量仿真:定时器与高阶控制器
第七章. 流量仿真:定时器与高阶控制器
Prorise第七章. 流量仿真:定时器与高阶控制器
摘要:在前面的章节中,我们的脚本像一个不知疲倦的机器人,以毫秒级的速度连续发送请求。但这与真实用户的行为背道而驰,且无法测试出“线程安全”等深层问题。本章我们将引入 定时器 来还原用户的“思考时间”,利用 同步定时器 制造绝对并发来检测 Spring Boot 的锁机制,并通过 高阶控制器 封装复杂的业务逻辑。
本章学习路径
我们将从“仿真”的角度出发,把脚本从“发包工具”升级为“用户模拟器”:
- 7.1 还原真实用户:定时器 (Timers)
- 7.1.1 并发数 $\neq$ 压力:思考时间的重要性
- 7.1.2 统一随机定时器:模拟自然波动
- 7.2 制造绝对洪峰:同步定时器
- 7.2.1 压力测试 vs 并发测试的区别
- 7.2.2 集合点实战:击穿数据库库存
- 7.2.3 避免死锁:Timeout 参数的最佳实践
- 7.3 业务视角封装:事务控制器
- 7.3.1 技术指标 vs 业务指标
- 7.3.2 “Generate parent sample” 深度解析
- 7.4 复杂逻辑编排:循环控制器
- 7.4.1 场景:轮询支付状态
- 7.4.2 线程循环 vs 控制器循环的区别
7.1. 还原真实用户:定时器 (Timers)
在上一章中,我们解决了配置冗余的问题。但在实际运行中,你可能会发现:明明设置了 100 个线程,为什么服务器的 CPU 瞬间就飙升到 100% 然后报错?而生产环境 1000 个在线用户却很平稳?
这是因为你忽略了 “思考时间” (Think Time)。
7.1.1. 核心概念:并发数与 TPS 的关系
在性能测试领域,有一个著名的公式:
$$TPS = \frac{并发用户数}{响应时间 + 思考时间}$$
- 机器人的行为:响应时间 100ms,思考时间 0ms。
$$TPS = 1 / 0.1 = 10$$ (单线程每秒发 10 个请求) - 真实用户的行为:响应时间 100ms,思考时间 3000ms(用户看完页面再点)。
$$TPS = 1 / 3.1 \approx 0.3$$ (单线程每 3 秒才发 1 个请求)
结论:如果不加定时器,100 个 JMeter 线程产生的压力,可能相当于 3000 个真实用户的压力。为了让测试结果具备参考价值,我们必须模拟这种“停顿”。
7.1.2. 实战:统一随机定时器 (Uniform Random Timer)
JMeter 提供了“固定定时器”,但在现实中,没有哪两个用户的思考时间是完全一秒不差的。为了模拟更自然的流量波动,我们推荐使用 统一随机定时器。
场景设计:用户登录成功后,浏览商品详情页,随机停留 1~3 秒,然后点击下单。
操作步骤:
- 展开 API_下单 请求。
- 右键点击 API_下单 -> 添加 -> 定时器 -> 统一随机定时器。
- 配置参数:
- Random Delay Maximum (随机延迟最大值):
2000 - Constant Delay Offset (固定延迟偏移):
1000
- Random Delay Maximum (随机延迟最大值):
原理解析:
$$总等待时间 = 固定偏移 + random(0, 随机最大值)$$
代入数值:$1000 + [0, 2000] = 1000ms \sim 3000ms$。这样的设置既保证了用户至少会看 1 秒(固定值),又模拟了手速的快慢差异(随机值)。
7.2. 制造绝对洪峰:同步定时器
在上一节,我们通过随机定时器让流量变得平滑。但在某些特殊场景——例如“秒杀”、“抢红包”——我们需要反其道而行之,制造瞬间的压力洪峰。
7.2.1. 压力测试 vs 并发测试
- 压力测试:考察系统在高负载下的稳定性(如 CPU 是否打满,GC 是否频繁)。
- 并发测试:考察系统对 共享资源 的抢占处理(如数据库锁、线程安全)。
普通的 JMeter 压测,线程是陆续启动的,很难在 微秒级 做到绝对同时请求。如果你的 Spring Boot 代码中有 stock = stock - 1 这种非原子操作,普通压测可能测不出 Bug,但上线就会超卖。
这时我们需要 同步定时器 (Synchronizing Timer),也就是 LoadRunner 中的 集合点 (Rendezvous Point)。
7.2.2. 实战:击穿库存
假设我们要测试 Spring Boot 的库存扣减逻辑。
操作步骤:
- 在 API_下单 的子节点下,添加 同步定时器。
- 配置参数:
- Number of Simulated Users to Group by:
50 - Timeout in milliseconds:
3000
- Number of Simulated Users to Group by:
执行逻辑:
- 线程启动后,运行到“下单”步骤时,会被定时器拦截并挂起。
- JMeter 会一直等待,直到积攒了 50 个被挂起的线程。
- 一旦达到 50 个,定时器瞬间释放,50 个请求在同一毫秒内涌向服务器。
- 这就构成了对数据库行锁的 绝对并发竞争。
7.2.3. 避免死锁:Timeout 的重要性
场景演示:假设你的线程组只设置了 30 个线程,但同步定时器要求集合 50 人。
- 结果:前 30 个线程到达集合点后开始等待第 31 人,但永远等不到。脚本会陷入 无限等待(死锁),进度条永远卡住。
最佳实践:永远不要把 Timeout 设置为 0(0 代表无限等待)。务必设置一个合理的超时时间(如 3000ms)。如果 3 秒内凑不齐 50 人,JMeter 会强制释放已到达的线程,继续执行后续步骤,避免脚本卡死。
7.3. 业务视角封装:事务控制器
在 JMeter 的默认报告中,我们看到的都是单个接口的耗时(登录 50ms,下单 80ms)。但产品经理通常会问:“用户完成一次购买流程需要多久?”。
简单的相加是不准确的,因为中间可能包含重定向、定时器等待等时间。我们需要 事务控制器 (Transaction Controller)。
7.3.1. 配置事务
操作步骤:
- 右键点击 线程组 -> 添加 -> 逻辑控制器 -> 事务控制器。
- 将 API_登录 和 API_下单(及其附属组件)全部拖拽到事务控制器内部。
- 关键配置:
- Generate parent sample (生成父样本):必须勾选。
- Include duration of timer (包含定时器耗时):根据需求勾选。
- 如果测的是 用户体验:勾选(用户觉得卡顿是包含了思考时间的)。
- 如果测的是 系统处理能力:不勾选(只统计服务器纯处理时间)。
7.3.2. 数据解读
运行测试后,查看聚合报告。
- 未勾选 Generate parent sample:你会看到三个条目——“API_登录”、“API_下单”、“事务控制器”。数据比较杂乱。
- 勾选 Generate parent sample:你会看到一个合并后的条目 “事务控制器”,原来的子接口被隐藏了。此时的 TPS 和 RT 指标,反映的就是完整的“购买业务”的处理能力。

7.4. 复杂逻辑编排:循环控制器
并不是所有的业务都是“线性”的(登录 -> 下单 -> 结束)。在支付场景中,往往存在“轮询”机制:前端每隔 1 秒查询一次后端状态,直到支付成功或超时。
7.4.1. 实战:轮询支付状态
我们需要在脚本中模拟:“下单成功后,每隔 1 秒查询一次订单状态,共查询 5 次”。
操作步骤:
- 在 线程组 中添加 逻辑控制器 -> 循环控制器 (Loop Controller)。
- 配置参数:
- Loop Count (循环次数):
5。
- Loop Count (循环次数):
- 在循环控制器内部添加:
- HTTP 请求:
GET /api/order/status。 - 固定定时器:
1000ms。
- HTTP 请求:
7.4.2. 线程循环 vs 控制器循环
这是初学者最容易混淆的概念:
- 线程组的 Loop Count:决定了 整个剧本 演多少遍。
- 如果设为 10,意味着“登录 -> 下单 -> 轮询”这全套流程做 10 遍。
- 循环控制器的 Loop Count:决定了 剧本中某一个小节 重复多少遍。
- 如果设为 5,意味着在每一遍剧本中,“查询状态”这个动作要重复 5 次。
通过组合使用,我们可以构建出非常复杂的业务模型:1次登录 -> 5次浏览 -> 1次下单 -> 3次查询状态。
7.5. 本章小结
本章我们为脚本注入了“灵魂”,使其从简单的接口调用工具进化为复杂的用户行为模拟器。
核心要点:
- 思考时间:必须使用 定时器 模拟用户停顿,否则压测结果中的 TPS 会虚高,无法代表真实负载。
- 作用域:定时器挂在谁下面,就只影响谁。切忌在线程组层级随意添加定时器。
- 绝对并发:使用 同步定时器 模拟秒杀场景,这是检测 Spring Boot 线程安全问题的杀手锏,但切记设置 Timeout 防止死锁。
- 事务统计:使用 事务控制器 聚合多个接口,勾选 Generate parent sample 获取清晰的业务级性能报告。
速查配置:
- 统一随机定时器:Offset = 固定等待,Max = 随机波动。
- 同步定时器:Timeout 不要设为 0。
下一步计划:至此,我们已经彻底掌握了 JMeter 的 GUI 组件(配置、请求、断言、定时器、控制器)。但在面对一些极度复杂的场景(如:RSA 动态签名、自定义 Redis 操作、复杂的数据清洗)时,GUI 界面已经无法满足需求了。下一章,我们将解锁 JMeter 的终极能力——Groovy 脚本编程,真正实现“为所欲为”的测试。








