为什么压测工具本身也会影响性能测试结果
2026/6/1 19:11:53 网站建设 项目流程

一、前言

很多人在刚接触性能测试时,容易形成一个直觉:

JMeter 只是负责发请求,真正消耗性能的是服务端。

因此会认为:

  • 压测工具不会影响测试结果
  • 压测结果天然等于服务端真实能力
  • 断言、日志、结果树只是附加功能

但在真实项目中,这种理解并不准确。

实际上:

压测工具本身同样是一个运行中的系统,也会消耗 CPU、内存、网络、磁盘 IO 等资源。

如果压测工具自身先达到瓶颈,那么最终测试结果可能已经不再反映服务端真实能力,而是反映压测工具自身的极限。

因此,理解“压测机瓶颈”与“压测结果失真”的关系,是性能测试中非常重要的一部分。


二、压测工具本质上也是一个系统

以 Apache JMeter 为例,其本质上是一个运行中的 Java 程序。

它不仅仅是在“发送请求”,还需要完成很多工作,包括:

  • 创建和维护线程
  • 建立 TCP / HTTP 连接
  • 发送请求数据
  • 接收响应数据
  • 执行断言
  • 执行脚本逻辑
  • 记录日志
  • 生成统计结果

因此:

JMeter 本身同样需要消耗大量系统资源。

尤其是在:

  • 高并发
  • 大响应体
  • 长时间压测
  • 大文件下载
  • 深度断言

等场景下,压测工具自身的资源消耗会明显增加。


三、为什么压测工具会影响测试结果

3.1 性能测试的本质

性能测试的目标通常包括:

  • 测试系统最大吞吐能力
  • 验证系统稳定性
  • 分析性能瓶颈
  • 评估系统容量

因此:

压测工具的核心职责是“持续稳定地产生压力”。

只有当压测工具能够稳定发出足够请求时,服务端才会真正达到极限状态。


3.2 什么叫“压测机成为瓶颈”

假设:

服务端真实能力:10000 TPS

但压测机由于:

  • CPU 已满
  • Full GC 频繁
  • 日志输出过多
  • 复杂断言过重
  • 脚本执行过慢

最终只能稳定发出:

3000 TPS

那么:

即使服务端还能继续承受更高压力,压测工具也已经无法继续制造更多请求。

最终测试报告中看到的:

TPS = 3000

实际上并不是服务端极限,而是压测工具自身极限。

这就是:

压测机成为瓶颈。


本地临时端口耗尽(Ephemeral Ports Exhaustion)

除了 CPU 和内存,网络资源也是压测机非常容易出现瓶颈的地方。

压测机向服务端发起 HTTP 请求时,每一个 TCP 连接都需要占用本地一个临时端口(Ephemeral Port)。

Linux 默认临时端口范围通常类似于:

32768 ~ 60999

也就是说:

压测机能够同时使用的本地连接端口数量本身就是有限的。

如果:

  • 未开启 Keep-Alive 长连接复用
  • 请求频繁创建短连接
  • TIME_WAIT 状态连接过多

那么大量端口会长时间无法释放。

最终可能导致:

java.net.BindException: Address already in use

此时并不是服务端达到瓶颈,而是压测机已经没有可用端口继续建立连接。


什么是 Keep-Alive 长连接复用

HTTP Keep-Alive 的本质是:

多个 HTTP 请求复用同一个 TCP 连接。

例如:

不使用 Keep-Alive:

请求1 -> 创建TCP连接 -> 关闭 请求2 -> 再创建TCP连接 -> 再关闭

而开启 Keep-Alive 后:

创建一次TCP连接 多个HTTP请求重复使用 最后统一关闭

这样可以显著减少:

  • TCP 三次握手开销
  • TCP TIME_WAIT 数量
  • 本地端口消耗
  • 网络连接建立成本

在 JMeter 中:

HTTP Request 默认通常已经开启:

Use KeepAlive = true

正式压测时通常建议保持开启状态。


3.3 为什么压测机性能瓶颈引发测试结果失真

很多人会疑惑:

JMeter 运行在客户端,为什么会影响服务端性能测试结果?

原因在于:

TPS/QPS 的本质是“单位时间内成功完成的请求数量”。

对于固定线程数:

TPS ≈ 线程数 ÷ 单次请求循环耗时

JMeter 采用的是多线程并发模型。在这个模型中,有两个核心的铁律:

1、线程之间物理隔离、互不干扰: 每个并发线程(Thread)都是一个独立的执行流,线程 A 变慢或阻塞,并不会直接引发线程 B 的卡顿。
2、线程内部严格串行、首尾相接: 每一个独立线程的完整执行生命周期是一个闭环的流水线,通常包括以下步骤:
→ 发送请求
→ 等待响应
→ 接收响应
→ 执行断言
→ 执行后处理
→ 记录结果
→ 进入下一轮请求

因此如果后处理逻辑变慢,例如:

  • MD5 计算
  • JSON 深度解析
  • 复杂 Groovy 脚本
  • 大量正则断言

都会导致线程在本地出现:

Thread Stalling(线程挂起)

线程虽然没有阻塞在服务端,但正在本地执行大量计算,无法继续发起下一次请求。

例如:原本1秒完成一次请求循环,增加复杂逻辑后1.5 秒才能完成一次请求循环。那么,单位时间内,该线程能够发送的请求数量自然下降。因此,即使服务端性能没有变化,JMeter 制造压力的能力也可能下降。最终表现为:

  • TPS 降低
  • 并发压力不足
  • 服务端未真正压满

这也是为什么压测工具自身瓶颈会导致测试结果失真。


四、为什么需要平衡“正确性校验”和“性能消耗”

4.1 为什么不能完全不做断言

如果完全不做断言,可能会出现:

  • HTTP 200 但业务失败
  • 返回错误页面
  • 返回异常数据
  • 返回空数据

但压测报告却显示:

成功率 100%

因此:

必要的正确性校验是性能测试中不可缺少的一部分。

最常见的校验包括:

  • HTTP 状态码校验
  • 业务码校验
  • 响应关键字段校验

这些校验用于保证:

压测过程中系统不仅“有响应”,而且“业务真正成功”。


4.2 不同断言的性能消耗差异

性能测试中,不同断言的资源开销差异非常大。


轻量级断言(推荐)

例如:

  • Response Assertion(响应断言)

其本质通常是:

  • 字符串包含匹配
  • 字节流匹配
  • 简单文本比较

例如:

code=200 success=true

这类断言:

  • CPU 消耗低
  • 内存占用小
  • 执行速度快

因此:

大多数业务码校验通常都属于轻量级断言。

也是正式压测中最推荐使用的断言方式。


重量级断言(慎用)

例如:

  • JSON Assertion
  • JSON JMESPath Assertion
  • XPath Assertion

这类断言通常需要:

  • 先将完整响应体反序列化
  • 构建 JSON 对象树或 XML DOM 树
  • 再进行层级解析

如果响应体非常大,例如:

  • 大数据量查询
  • 大型 JSON 返回
  • 深层嵌套对象

那么会显著增加:

  • CPU 消耗
  • Heap 内存占用
  • Full GC 风险

因此:

重量级断言通常不建议在高并发主压测阶段大量使用。


高成本后处理逻辑(谨慎使用)

例如:

  • MD5 哈希计算
  • BeanShell
  • Groovy 脚本
  • JavaScript 后处理
  • 大量正则表达式

这些操作本质上属于压测机本地计算。

在大文件、高并发、长时间压测场景下,很容易导致:

  • CPU 打满
  • Full GC
  • 压测机自身卡顿

因此通常建议:

  • 小规模验证
  • 抽样校验
  • 功能阶段验证

而不是在主压测阶段全量开启。


五、为什么真实项目中会精简 JMeter 功能

正式压测的核心目标,是让压测工具尽可能稳定地产生压力,而不是把大量资源消耗在本地调试、UI 渲染或复杂数据处理上。因此,真实项目中的正式压测通常会对 JMeter 功能进行精简,只保留必要能力。


5.1 调试功能通常不会在正式压测中开启

很多人在调试阶段会开启:

  • View Results Tree
  • Debug Sampler
  • 实时图形监听器

用于查看请求、响应或实时统计结果。

但这些功能通常会保存大量响应数据,同时伴随 Swing UI 渲染与实时统计计算。在高并发、大响应体或长时间压测场景下,容易导致:

  • JMeter Heap 内存快速增长
  • Full GC 频繁
  • 压测机 CPU 占用升高
  • 压测机卡顿甚至 OOM

因此:

这类功能通常只用于脚本调试,而不会在正式压测时开启。


5.2 高成本脚本与断言通常会被控制使用

JMeter 支持:

  • Groovy
  • BeanShell
  • JSON Assertion
  • XPath Assertion

等扩展能力。

这些功能虽然灵活,但本质上都属于压测机本地计算。在高并发、长时间压测场景下,会明显增加:

  • CPU 消耗
  • JVM Heap 压力
  • GC 开销

因此真实项目中通常会:

  • 保留 HTTP 状态码校验
  • 保留核心业务码断言
  • 减少复杂脚本与深度解析
  • 避免全量高成本后处理逻辑

例如:

  • 大文件 MD5 全量校验
  • 深层 JSON 解析
  • 高频 Groovy 脚本执行

通常只会在特定阶段按需使用。


5.3 正式压测通常采用 Non-GUI 模式

JMeter 官方明确建议:

GUI 模式仅用于脚本开发与调试,不用于正式压测。

因为 GUI 模式下:

  • 图表刷新
  • 实时统计
  • 聚合展示
  • Swing UI 渲染

都会持续消耗压测机资源。

因此正式压测通常采用:

jmeter-n-ttest.jmx-lresult.jtl

即:

  • 使用 Non-GUI 模式运行
  • 压测阶段仅记录原始结果
  • 压测结束后再离线生成聚合报告

从而尽量降低压测工具自身对测试结果的影响。


六、总结

性能测试不仅仅是在“压服务端”。

实际上:

压测工具本身也是性能测试体系中的重要组成部分。

如果压测工具自身先达到瓶颈,那么最终得到的测试结果可能已经失去参考意义。

因此,一个合理的性能测试方案,需要同时关注:

  • 服务端性能
  • 压测机资源
  • 正确性校验
  • 压测工具开销

真实项目中的核心目标,并不是“完全不消耗资源”,而是:

在保证结果可信的前提下,尽量减少压测工具自身对测试结果的影响。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询