8. 测试-性能测试-JMeter实战

文章目录

  • 前言
    • 第一部分:基础教程
      • [🛠️ 第一步:安装适配 Java 8 的 JMeter](#🛠️ 第一步:安装适配 Java 8 的 JMeter)
        • [1. 选择正确的版本](#1. 选择正确的版本)
        • [2. 下载与解压](#2. 下载与解压)
        • [3. 启动验证](#3. 启动验证)
        • [4. 界面汉化(可选)](#4. 界面汉化(可选))
      • [🚀 第二步:创建你的第一个测试计划Demo](#🚀 第二步:创建你的第一个测试计划Demo)
        • [1. 添加线程组 (模拟用户)](#1. 添加线程组 (模拟用户))
        • [2. 添加 HTTP 请求 (模拟操作)](#2. 添加 HTTP 请求 (模拟操作))
        • [3. 添加监听器 (查看结果)](#3. 添加监听器 (查看结果))
      • [📊 第三步:运行与分析](#📊 第三步:运行与分析)
        • [1. 运行测试](#1. 运行测试)
        • [2. 结果分析](#2. 结果分析)
    • 第二部分:进阶教程
      • [🚀 第一步:构建进阶测试计划](#🚀 第一步:构建进阶测试计划)
        • [1. 准备数据文件](#1. 准备数据文件)
        • [2. 配置登录接口](#2. 配置登录接口)
        • [3. 提取 Token](#3. 提取 Token)
        • [4. 跨线程组传参](#4. 跨线程组传参)
        • [5. 配置业务接口](#5. 配置业务接口)
        • [6. 调用全局 Token](#6. 调用全局 Token)
      • [📊 第二步:运行与结果分析](#📊 第二步:运行与结果分析)
    • 第三部分:进阶分析
      • 第一步:构建测试计划(接口配置http请求)
        • [1. 进阶核心点:跨线程组传参(token 共享)](#1. 进阶核心点:跨线程组传参(token 共享))
        • [2. 阶梯加压策略(Stepping Thread Group)](#2. 阶梯加压策略(Stepping Thread Group))
        • [3. 参数化配置(CSV Data Set Config)](#3. 参数化配置(CSV Data Set Config))
        • [4. 关键配置细节审查](#4. 关键配置细节审查)
        • [5. 整体流程逻辑梳理](#5. 整体流程逻辑梳理)
        • [6. 下一步建议](#6. 下一步建议)
      • 第二步:结果分析(监听器详解)
        • [1. 查看结果树](#1. 查看结果树)
        • [2. jp@gc - Active Threads Over Time](#2. jp@gc - Active Threads Over Time)
        • [3. jp@gc - Response Times Over Time](#3. jp@gc - Response Times Over Time)
        • [4. jp@gc - Transactions per Second](#4. jp@gc - Transactions per Second)
        • [5. 聚合报告](#5. 聚合报告)
    • 第四部分:脚本优化
        • [1. 替换 BeanShell(性能优化)](#1. 替换 BeanShell(性能优化))
        • [2. 完善跨线程组取值](#2. 完善跨线程组取值)
        • [3. 添加事务控制器](#3. 添加事务控制器)
    • 第五部分:执行与验证步骤
    • 第六部分:技巧
      • [1. 挡板机制](#1. 挡板机制)
        • [①🧪 使用 WireMock(最推荐,轻量级)](#①🧪 使用 WireMock(最推荐,轻量级))
        • [②⚙️ JMeter 内部实现 Mock(无需额外工具)](#②⚙️ JMeter 内部实现 Mock(无需额外工具))
          • [方案一:使用 JSR223 Sampler 模拟 HTTP 服务(最灵活)](#方案一:使用 JSR223 Sampler 模拟 HTTP 服务(最灵活))
          • [方案二:使用 JMeter 原生 HTTP Mirror Server(最简单)](#方案二:使用 JMeter 原生 HTTP Mirror Server(最简单))
          • 方案对比总结
        • [③🛡️ 使用 Nginx 反向代理(运维级方案)](#③🛡️ 使用 Nginx 反向代理(运维级方案))
        • [④📊 方案对比与总结](#④📊 方案对比与总结)

前言

JMeter

JMeter是基于Java语言开发的开源轻量级测试工具。


第一部分:基础教程

JMeter 版本 最低 Java 要求 你的环境 (Java 8)
JMeter 6.x (最新版) Java 17 ❌ 不支持 (无法启动)
JMeter 5.7 - 5.9 Java 11+ ❌ 不支持 (无法启动)
JMeter 5.6.3 Java 8+ ✅ 完美支持

由于 Apache JMeter 的最新版本(5.6+)已经不再支持 Java 8,如果需要使用当前的 Java 环境,必须安装旧版本的 JMeter 。以下介绍Java 8 专属的安装与使用教程。


🛠️ 第一步:安装适配 Java 8 的 JMeter

1. 选择正确的版本
  • 核心规则:JMeter 5.5 及之前的版本支持 Java 8。
  • 推荐版本JMeter 5.5(这是支持 Java 8 的最后一个稳定版本,功能足够强大)。
  • 避坑指南千万不要 下载 JMeter 5.6、5.6.3 或更高版本,否则启动时会报错提示 Java version not supported
2. 下载与解压
  1. 下载地址 :访问 Apache JMeter 官方下载归档
  2. 获取文件 :找到并下载 apache-jmeter-5.5.zip
  3. 解压 :将压缩包解压到一个路径中不包含中文和空格 的目录(例如 D:\JMeter\apache-jmeter-5.5)。
3. 启动验证
  1. 进入解压目录的 bin 文件夹。
  2. 双击 jmeter.bat
  3. 如果成功弹出图形界面,说明 Java 8 环境与 JMeter 5.5 完美匹配。
4. 界面汉化(可选)

默认是英文界面,建议修改配置永久汉化:

  • 打开 bin/jmeter.properties 文件。
  • 搜索 language=en
  • 修改为 language=zh_CN(去掉前面的 # 号)。
  • 保存并重启 JMeter。

🚀 第二步:创建你的第一个测试计划Demo

我们将模拟 10 个用户10 秒内 启动,并发访问百度首页。

1. 添加线程组 (模拟用户)

线程组是测试的发动机。

  • 在左侧"测试计划"上右键 -> 添加 -> 线程 (用户) -> 线程组
  • 配置参数
    • 线程数10(代表 10 个虚拟用户)。
    • Ramp-Up 时间 (秒)10(代表在 10 秒内逐步启动这 10 个用户,避免瞬间冲击)。
    • 循环次数1(每个用户执行 1 次任务)。
2. 添加 HTTP 请求 (模拟操作)

这是用户具体要执行的动作。

  • 右键点击"线程组" -> 添加 -> 取样器 -> HTTP请求
  • 配置参数
    • 协议https
    • 服务器名称或IPwww.baidu.com
    • 端口号:(留空)
    • 方法GET
    • 路径/
3. 添加监听器 (查看结果)

没有监听器,测试数据将无法显示。

  • 查看结果树 :用于调试,看请求是否成功。
    • 右键"线程组" -> 添加 -> 监听器 -> 查看结果树
  • 聚合报告 :用于性能分析,看响应时间和吞吐量。
    • 右键"线程组" -> 添加 -> 监听器 -> 聚合报告

📊 第三步:运行与分析

1. 运行测试

点击工具栏上方的绿色"播放"按钮 (▶️)。

2. 结果分析

测试完成后,点击"聚合报告",重点关注以下指标:

表格

指标名称 含义 你的关注点
Average 平均响应时间 数值越小越好(单位:毫秒)。
90% Line 90% 请求的响应时间 比如是 50ms,说明 90% 的用户都在 50ms 内得到了响应。
Error % 错误率 必须为 0%。如果有红色报错,说明请求失败。
Throughput 吞吐量 (TPS/QPS) 服务器每秒处理的请求数,数值越大性能越好。

第二部分:进阶教程

目标的测试计划结构非常清晰,是一个典型的登录获取 Token -> 传递 Token -> 携带 Token 请求业务接口的链路测试场景。

🚀 第一步:构建进阶测试计划

构建一个"登录获取 Token -> 跨线程组传递 -> 业务接口调用"的实战场景。

1. 准备数据文件

CSV Data Set Config

  • 操作 :在本地创建一个 codes.csv 文件,内容如下:

    文件内容是模拟认证鉴权Oauth2.0流程的授权码列表。

    测试目标是已通过用户名密码认证后进入,获取授权码->获取token->接口交互的流程

    csv 复制代码
    h3UVsSKprUTK
    WY2FIJBQBlng
    nMkIuCspPQvC
  • JMeter配置

    • 右键点击"测试计划" -> "添加" -> "配置元件" -> "CSV 数据文件设置"。
    • 文件名 :选择你的 codes.csv 路径。
    • 变量名称 :填写 code(与文件列对应)。
    • 作用:让每个线程(用户)登录时使用不同的授权码。
2. 配置登录接口

HTTP Request + HTTP Header Manager

HTTP 请求 + HTTP信息头管理器

操作:右键"线程组" -> "添加" -> "取样器" -> "HTTP 请求"。命名为"登录接口"。

  • 配置

    • 协议:HTTPS。

    • 服务器名称或 IPauth-api-perf.xxx.com.cn

    • 路径/auth/oauth2/begin-by-postal

    • 方法:POST。

    • Body Data(在"发送文件随请求"上方):

      json 复制代码
      {
          "code": "${code}"
      }

      注意:这里使用了 ${} 来调用 CSV 中的变量。

信息头管理器,请求头列表中不要多余空行,请求头的键值项也必须准确无空格。

3. 提取 Token

JSON Extractor

JSON提取器

登录成功后,服务器会返回 JSON 数据,我们需要从中提取 token

  • 操作:右键点击"登录接口" -> "添加" -> "后置处理器" -> "JSON 提取器"。
  • 配置
    • 名称:提取请求结果token。
    • 作用域:主样本(Main sample only)。
    • Names of created variablestoken(定义变量名)。
    • JSON Path expressions$..value(假设返回的 JSON 中 token 字段叫 value,封装在两层的结构体中,请根据实际返回修改)。
    • Match No.1
4. 跨线程组传参

BeanShell PostProcessor

BeanShell 后置处理器

这是进阶的核心。JMeter 默认变量是线程隔离的,为了让后续的"业务线程组"能用到这里的 token,必须将其转为全局属性。

  • 操作:右键点击"登录接口" -> "添加" -> "后置处理器" -> "BeanShell 后置处理器"。

  • 配置:有以下两种方式可以实现

    • 使用 JMeter函数,如,内置的 __setProperty 函数。在脚本区域输入以下代码:

      java 复制代码
      ${__setProperty(global_token,${token},)};

      不要加 // 注释,因为 BeanShell 可能会尝试解析注释后的符号

      代码解释:将提取到的局部变量 token 转换为全局属性 global_token。第三个参数 true 表示保存到文件,重启 JMeter 后依然有效(可选)

    • 使用 BeanShell 原生 API。在脚本区域输入以下代码:

      java 复制代码
      // 获取局部变量 token
      String tokenVal = vars.get("token");
      
      // 判空保护
      if (tokenVal != null) {
          // 设置为全局属性
          props.put("token", tokenVal);
          log.info("成功将 token 存入全局属性: " + tokenVal);
      } else {
          log.error("未找到 token 变量,请检查前置提取器");
      }

      代码解释:1. 从 JMeter 变量池(vars) 中获取 token 。
      2. 将 token 设置到 JMeter 属性池(props) 中,使其全局有效。

      1. 如果需要保存到文件(持久化),使用 props.put 的第三个参数无法直接实现文件保存。 __setProperty 的 true 参数是保存到 jmeter.properties 文件。在 BeanShell 中,通常 props 是内存级的。如果需要持久化,建议直接用 ${__setProperty(...)} 函数。但如果只是为了跨线程组使用,props.put 就够了。
5. 配置业务接口

第二个 HTTP Request

  • 操作:右键"线程组" -> "添加" -> "取样器" -> "HTTP 请求"。命名为"查看用户信息"。
  • 配置
    • 路径/auth/detail/perms
    • 方法:GET。
6. 调用全局 Token

HTTP Header Manager

在业务请求中,我们需要把刚才存到全局属性的 token 取出来,放到请求头里。

  • 操作:右键点击"查看用户信息"请求 -> "添加" -> "配置元件" -> "HTTP 信息头管理器"。
  • 配置
    • 名称Authorization
    • bearer ${__P(global_token)}
    • 解析:${__P(global_token)} 是 JMeter 函数,用于读取全局属性。

📊 第二步:运行与结果分析

添加监听器

右键点击"测试计划"或具体的"线程组" -> "添加" -> "监听器",添加以下组件:

  • 查看结果树:用于调试,看请求是否成功(绿色钩)。
  • 聚合报告:查看平均响应时间、吞吐量(TPS)。
  • jp@gc - Active Threads Over Time:查看并发用户数随时间的变化(需安装插件)。
  • jp@gc - Response Times Over Time:查看响应时间趋势随时间的变化(需安装插件)。
  • jp@gc - Transactions per Second:查看每秒事务数/吞吐量,即 QPS随时间的变化(需安装插件)。
运行测试

点击工具栏上的绿色三角右箭头"启动"按钮。

观察日志

点击工具栏右侧的三角警告牌"日志"按钮。

log 复制代码
2026-04-10 16:35:08,398 INFO o.a.j.e.StandardJMeterEngine: Running the test!
2026-04-10 16:35:08,399 INFO o.a.j.s.SampleEvent: List of sample_variables: []
2026-04-10 16:35:08,399 INFO o.a.j.g.u.JMeterMenuBar: setRunning(true, *local*)
2026-04-10 16:35:08,399 INFO o.a.j.e.StandardJMeterEngine: Starting setUp thread groups
2026-04-10 16:35:08,399 INFO o.a.j.e.StandardJMeterEngine: Starting setUp ThreadGroup: 1 : setUp Thread Group 
2026-04-10 16:35:08,399 INFO o.a.j.e.StandardJMeterEngine: Starting 1 threads for group setUp Thread Group.
2026-04-10 16:35:08,399 INFO o.a.j.e.StandardJMeterEngine: Thread will continue on error
2026-04-10 16:35:08,399 INFO o.a.j.t.ThreadGroup: Starting thread group... number=1 threads=1 ramp-up=1 delayedStart=false
2026-04-10 16:35:08,402 INFO o.a.j.t.ThreadGroup: Started thread group number 1
2026-04-10 16:35:08,402 INFO o.a.j.e.StandardJMeterEngine: Waiting for all setup thread groups to exit
2026-04-10 16:35:08,402 INFO o.a.j.t.JMeterThread: Thread started: setUp Thread Group 1-1
2026-04-10 16:35:08,467 INFO o.a.j.p.j.s.J.JSR223 Sampler: >>> Mock HTTP Server started on port: 48001
2026-04-10 16:35:08,467 INFO o.a.j.t.JMeterThread: Thread is done: setUp Thread Group 1-1
2026-04-10 16:35:08,467 INFO o.a.j.t.JMeterThread: Thread finished: setUp Thread Group 1-1
2026-04-10 16:35:08,468 INFO o.a.j.e.StandardJMeterEngine: All Setup Threads have ended
2026-04-10 16:35:08,733 INFO o.a.j.e.StandardJMeterEngine: Starting ThreadGroup: 1 : 第一轮线程组
2026-04-10 16:35:08,733 INFO o.a.j.e.StandardJMeterEngine: Starting 32 threads for group 第一轮线程组.
2026-04-10 16:35:08,734 INFO o.a.j.e.StandardJMeterEngine: Thread will continue on error
2026-04-10 16:35:08,734 INFO k.a.j.t.AbstractSimpleThreadGroup: Starting thread group number 1 threads 32
2026-04-10 16:35:08,744 INFO o.a.j.t.JMeterThread: Thread started: 第一轮线程组 1-1
2026-04-10 16:35:09,007 INFO k.a.j.t.AbstractSimpleThreadGroup: Started thread group number 1
2026-04-10 16:35:09,007 INFO o.a.j.e.StandardJMeterEngine: Waiting for thread group: 第一轮线程组 to finish before starting next group
2026-04-10 16:35:09,488 INFO o.a.j.t.JMeterThread: Thread started: 第一轮线程组 1-2
2026-04-10 16:35:10,234 INFO o.a.j.t.JMeterThread: Thread started: 第一轮线程组 1-3
2026-04-10 16:35:10,985 INFO o.a.j.t.JMeterThread: Thread started: 第一轮线程组 1-4
2026-04-10 16:35:41,735 INFO o.a.j.t.JMeterThread: Thread started: 第一轮线程组 1-5
2026-04-10 16:35:42,484 INFO o.a.j.t.JMeterThread: Thread started: 第一轮线程组 1-6
2026-04-10 16:35:43,235 INFO o.a.j.t.JMeterThread: Thread started: 第一轮线程组 1-7
2026-04-10 16:35:43,985 INFO o.a.j.t.JMeterThread: Thread started: 第一轮线程组 1-8
2026-04-10 16:36:14,735 INFO o.a.j.t.JMeterThread: Thread started: 第一轮线程组 1-9
2026-04-10 16:36:15,489 INFO o.a.j.t.JMeterThread: Thread started: 第一轮线程组 1-10
2026-04-10 16:36:16,242 INFO o.a.j.t.JMeterThread: Thread started: 第一轮线程组 1-11
2026-04-10 16:36:16,984 INFO o.a.j.t.JMeterThread: Thread started: 第一轮线程组 1-12
2026-04-10 16:36:47,734 INFO o.a.j.t.JMeterThread: Thread started: 第一轮线程组 1-13
2026-04-10 16:36:48,484 INFO o.a.j.t.JMeterThread: Thread started: 第一轮线程组 1-14
2026-04-10 16:36:49,234 INFO o.a.j.t.JMeterThread: Thread started: 第一轮线程组 1-15
2026-04-10 16:36:49,984 INFO o.a.j.t.JMeterThread: Thread started: 第一轮线程组 1-16
2026-04-10 16:37:20,734 INFO o.a.j.t.JMeterThread: Thread started: 第一轮线程组 1-17
2026-04-10 16:37:21,485 INFO o.a.j.t.JMeterThread: Thread started: 第一轮线程组 1-18
2026-04-10 16:37:22,235 INFO o.a.j.t.JMeterThread: Thread started: 第一轮线程组 1-19
2026-04-10 16:37:22,984 INFO o.a.j.t.JMeterThread: Thread started: 第一轮线程组 1-20
2026-04-10 16:37:53,744 INFO o.a.j.t.JMeterThread: Thread started: 第一轮线程组 1-21
2026-04-10 16:37:54,485 INFO o.a.j.t.JMeterThread: Thread started: 第一轮线程组 1-22
2026-04-10 16:37:55,235 INFO o.a.j.t.JMeterThread: Thread started: 第一轮线程组 1-23
2026-04-10 16:37:55,986 INFO o.a.j.t.JMeterThread: Thread started: 第一轮线程组 1-24
2026-04-10 16:38:26,735 INFO o.a.j.t.JMeterThread: Thread started: 第一轮线程组 1-25
2026-04-10 16:38:27,484 INFO o.a.j.t.JMeterThread: Thread started: 第一轮线程组 1-26
2026-04-10 16:38:28,235 INFO o.a.j.t.JMeterThread: Thread started: 第一轮线程组 1-27
2026-04-10 16:38:28,985 INFO o.a.j.t.JMeterThread: Thread started: 第一轮线程组 1-28
2026-04-10 16:38:59,734 INFO o.a.j.t.JMeterThread: Thread started: 第一轮线程组 1-29
2026-04-10 16:39:00,485 INFO o.a.j.t.JMeterThread: Thread started: 第一轮线程组 1-30
2026-04-10 16:39:01,235 INFO o.a.j.t.JMeterThread: Thread started: 第一轮线程组 1-31
2026-04-10 16:39:01,984 INFO o.a.j.t.JMeterThread: Thread started: 第一轮线程组 1-32
2026-04-10 16:40:03,822 INFO o.a.j.t.JMeterThread: Stopping because end time detected by thread: 第一轮线程组 1-3
2026-04-10 16:40:03,822 INFO o.a.j.t.JMeterThread: Thread finished: 第一轮线程组 1-3
2026-04-10 16:40:04,823 INFO o.a.j.t.JMeterThread: Stopping because end time detected by thread: 第一轮线程组 1-2
2026-04-10 16:40:04,823 INFO o.a.j.t.JMeterThread: Thread finished: 第一轮线程组 1-2
2026-04-10 16:40:06,024 INFO o.a.j.t.JMeterThread: Stopping because end time detected by thread: 第一轮线程组 1-4
2026-04-10 16:40:06,024 INFO o.a.j.t.JMeterThread: Thread finished: 第一轮线程组 1-4
2026-04-10 16:40:06,410 INFO o.a.j.t.JMeterThread: Stopping because end time detected by thread: 第一轮线程组 1-1
2026-04-10 16:40:06,411 INFO o.a.j.t.JMeterThread: Thread finished: 第一轮线程组 1-1
2026-04-10 16:40:07,930 INFO o.a.j.t.JMeterThread: Stopping because end time detected by thread: 第一轮线程组 1-8
2026-04-10 16:40:07,930 INFO o.a.j.t.JMeterThread: Thread finished: 第一轮线程组 1-8
2026-04-10 16:40:09,165 INFO o.a.j.t.JMeterThread: Stopping because end time detected by thread: 第一轮线程组 1-9
2026-04-10 16:40:09,166 INFO o.a.j.t.JMeterThread: Thread finished: 第一轮线程组 1-9
2026-04-10 16:40:11,099 INFO o.a.j.t.JMeterThread: Stopping because end time detected by thread: 第一轮线程组 1-10
2026-04-10 16:40:11,099 INFO o.a.j.t.JMeterThread: Thread finished: 第一轮线程组 1-10
2026-04-10 16:40:11,941 INFO o.a.j.t.JMeterThread: Stopping because end time detected by thread: 第一轮线程组 1-6
2026-04-10 16:40:11,943 INFO o.a.j.t.JMeterThread: Thread finished: 第一轮线程组 1-6
2026-04-10 16:40:12,034 INFO o.a.j.t.JMeterThread: Stopping because end time detected by thread: 第一轮线程组 1-7
2026-04-10 16:40:12,035 INFO o.a.j.t.JMeterThread: Thread finished: 第一轮线程组 1-7
2026-04-10 16:40:12,091 INFO o.a.j.t.JMeterThread: Stopping because end time detected by thread: 第一轮线程组 1-16
2026-04-10 16:40:12,092 INFO o.a.j.t.JMeterThread: Thread finished: 第一轮线程组 1-16
2026-04-10 16:40:12,493 INFO o.a.j.t.JMeterThread: Stopping because end time detected by thread: 第一轮线程组 1-11
2026-04-10 16:40:12,493 INFO o.a.j.t.JMeterThread: Thread finished: 第一轮线程组 1-11
2026-04-10 16:40:12,901 INFO o.a.j.t.JMeterThread: Stopping because end time detected by thread: 第一轮线程组 1-15
2026-04-10 16:40:12,901 INFO o.a.j.t.JMeterThread: Thread finished: 第一轮线程组 1-15
2026-04-10 16:40:12,954 INFO o.a.j.t.JMeterThread: Stopping because end time detected by thread: 第一轮线程组 1-5
2026-04-10 16:40:12,954 INFO o.a.j.t.JMeterThread: Thread finished: 第一轮线程组 1-5
2026-04-10 16:40:13,472 INFO o.a.j.t.JMeterThread: Stopping because end time detected by thread: 第一轮线程组 1-14
2026-04-10 16:40:13,473 INFO o.a.j.t.JMeterThread: Thread finished: 第一轮线程组 1-14
2026-04-10 16:40:13,785 INFO o.a.j.t.JMeterThread: Stopping because end time detected by thread: 第一轮线程组 1-13
2026-04-10 16:40:13,786 INFO o.a.j.t.JMeterThread: Thread finished: 第一轮线程组 1-13
2026-04-10 16:40:14,149 INFO o.a.j.t.JMeterThread: Stopping because end time detected by thread: 第一轮线程组 1-12
2026-04-10 16:40:14,149 INFO o.a.j.t.JMeterThread: Thread finished: 第一轮线程组 1-12
2026-04-10 16:40:16,109 INFO o.a.j.t.JMeterThread: Stopping because end time detected by thread: 第一轮线程组 1-20
2026-04-10 16:40:16,109 INFO o.a.j.t.JMeterThread: Thread finished: 第一轮线程组 1-20
2026-04-10 16:40:16,737 INFO o.a.j.t.JMeterThread: Stopping because end time detected by thread: 第一轮线程组 1-17
2026-04-10 16:40:16,737 INFO o.a.j.t.JMeterThread: Thread finished: 第一轮线程组 1-17
2026-04-10 16:40:16,762 INFO o.a.j.t.JMeterThread: Stopping because end time detected by thread: 第一轮线程组 1-19
2026-04-10 16:40:16,762 INFO o.a.j.t.JMeterThread: Thread finished: 第一轮线程组 1-19
2026-04-10 16:40:17,284 INFO o.a.j.t.JMeterThread: Stopping because end time detected by thread: 第一轮线程组 1-18
2026-04-10 16:40:17,284 INFO o.a.j.t.JMeterThread: Thread finished: 第一轮线程组 1-18
2026-04-10 16:40:17,736 INFO o.a.j.t.JMeterThread: Stopping because end time detected by thread: 第一轮线程组 1-23
2026-04-10 16:40:17,737 INFO o.a.j.t.JMeterThread: Thread finished: 第一轮线程组 1-23
2026-04-10 16:40:17,874 INFO o.a.j.t.JMeterThread: Stopping because end time detected by thread: 第一轮线程组 1-21
2026-04-10 16:40:17,874 INFO o.a.j.t.JMeterThread: Thread finished: 第一轮线程组 1-21
2026-04-10 16:40:17,969 INFO o.a.j.t.JMeterThread: Stopping because end time detected by thread: 第一轮线程组 1-24
2026-04-10 16:40:17,969 INFO o.a.j.t.JMeterThread: Thread finished: 第一轮线程组 1-24
2026-04-10 16:40:18,590 INFO o.a.j.t.JMeterThread: Stopping because end time detected by thread: 第一轮线程组 1-22
2026-04-10 16:40:18,590 INFO o.a.j.t.JMeterThread: Thread finished: 第一轮线程组 1-22
2026-04-10 16:40:21,262 INFO o.a.j.t.JMeterThread: Stopping because end time detected by thread: 第一轮线程组 1-27
2026-04-10 16:40:21,263 INFO o.a.j.t.JMeterThread: Thread finished: 第一轮线程组 1-27
2026-04-10 16:40:21,421 INFO o.a.j.t.JMeterThread: Stopping because end time detected by thread: 第一轮线程组 1-28
2026-04-10 16:40:21,421 INFO o.a.j.t.JMeterThread: Thread finished: 第一轮线程组 1-28
2026-04-10 16:40:24,467 INFO o.a.j.t.JMeterThread: Stopping because end time detected by thread: 第一轮线程组 1-31
2026-04-10 16:40:24,467 INFO o.a.j.t.JMeterThread: Thread finished: 第一轮线程组 1-31
2026-04-10 16:40:25,859 INFO o.a.j.t.JMeterThread: Stopping because end time detected by thread: 第一轮线程组 1-26
2026-04-10 16:40:25,859 INFO o.a.j.t.JMeterThread: Thread finished: 第一轮线程组 1-26
2026-04-10 16:40:28,895 INFO o.a.j.t.JMeterThread: Stopping because end time detected by thread: 第一轮线程组 1-30
2026-04-10 16:40:28,895 INFO o.a.j.t.JMeterThread: Thread finished: 第一轮线程组 1-30
2026-04-10 16:40:30,998 INFO o.a.j.t.JMeterThread: Stopping because end time detected by thread: 第一轮线程组 1-29
2026-04-10 16:40:30,998 INFO o.a.j.t.JMeterThread: Thread finished: 第一轮线程组 1-29
2026-04-10 16:40:31,070 INFO o.a.j.t.JMeterThread: Stopping because end time detected by thread: 第一轮线程组 1-32
2026-04-10 16:40:31,070 INFO o.a.j.t.JMeterThread: Thread finished: 第一轮线程组 1-32
2026-04-10 16:40:31,492 INFO o.a.j.t.JMeterThread: Stopping because end time detected by thread: 第一轮线程组 1-25
2026-04-10 16:40:31,493 INFO o.a.j.t.JMeterThread: Thread finished: 第一轮线程组 1-25
2026-04-10 16:40:31,494 INFO o.a.j.e.StandardJMeterEngine: All thread groups have been started
2026-04-10 16:40:31,494 INFO o.a.j.e.StandardJMeterEngine: Starting tearDown thread groups
2026-04-10 16:40:31,494 INFO o.a.j.e.StandardJMeterEngine: Starting tearDown ThreadGroup: 1 : tearDown Thread Group
2026-04-10 16:40:31,494 INFO o.a.j.e.StandardJMeterEngine: Starting 1 threads for group tearDown Thread Group.
2026-04-10 16:40:31,494 INFO o.a.j.e.StandardJMeterEngine: Thread will continue on error
2026-04-10 16:40:31,494 INFO o.a.j.t.ThreadGroup: Starting thread group... number=1 threads=1 ramp-up=1 delayedStart=false
2026-04-10 16:40:31,498 INFO o.a.j.t.ThreadGroup: Started thread group number 1
2026-04-10 16:40:31,498 INFO o.a.j.t.JMeterThread: Thread started: tearDown Thread Group 1-1
2026-04-10 16:40:31,538 INFO o.a.j.p.j.s.J.JSR223 Sampler: >>> Mock HTTP Server stopped.
2026-04-10 16:40:31,538 INFO o.a.j.t.JMeterThread: Thread is done: tearDown Thread Group 1-1
2026-04-10 16:40:31,538 INFO o.a.j.t.JMeterThread: Thread finished: tearDown Thread Group 1-1
2026-04-10 16:40:31,538 INFO o.a.j.e.StandardJMeterEngine: Notifying test listeners of end of test
2026-04-10 16:40:31,538 INFO o.a.j.g.u.JMeterMenuBar: setRunning(false, *local*)
结果解读
  • 查看结果树:如果"查看用户信息"显示绿色,且响应数据中有用户信息,说明 token 传递成功。如果显示红色或 401 错误,说明 token 提取或传递失败。
  • 聚合报告
    • # Samples:样本数,即请求总数。
    • Average:平均响应时间,越短越好。
    • Throughput:吞吐量,即每秒处理请求数,越高越好。
    • Error %:错误率,应为 0%。
💡 关键知识点总结
  • 作用域:配置元件(如 HTTP 信息头管理器)放在哪个层级,就对哪个层级及其子节点生效。
  • 关联:使用 JSON 提取器从上一个请求的响应中提取数据。
  • 跨线程组传参 :使用 __setProperty 将变量转为属性,使用 __P 读取属性。
  • 参数化:使用 CSV 数据文件设置,实现多用户登录。

第三部分:进阶分析

上面讲述了基础操作,针对具体配置进行进阶分析 。这涉及到了参数化、关联(Correlation)、作用域以及跨线程组传参等核心高级技巧。

第一步:构建测试计划(接口配置http请求)

1. 进阶核心点:跨线程组传参(token 共享)

本测试计划中最关键的进阶点。通常 JMeter 的变量是线程私有的(即线程 A 登录拿到的 token,线程 B 无法直接使用)。但在本计划中,需要实现"一轮线程组登录后,后续请求复用 token" 或者 "不同线程组间共享 token"。

测试计划脚本分析:

  • JSON Extractor 从登录接口提取了 token,变量名设为 token
  • BeanShell PostProcessor 你使用了脚本 ${__setProperty(token,${token},true)}
  • HTTP Header Manager 在后续请求中,你使用了 bearer ${__P(token)} 来调用这个值。

深度解析:

使用了 __setProperty 函数将局部变量转换为全局属性(JMeter Property)

  • 优点:实现了跨线程组的数据共享。如果"第一轮线程组"负责登录,"第二轮线程组"负责业务,这种方式可以让第二轮拿到第一轮登录的 Token。
  • 风险setProperty 是全局的。如果你有 100 个并发用户同时登录,最后存入全局属性的 token互相覆盖,导致所有用户都使用最后那个用户的 token。
  • 优化建议
    • 方案 A (单用户调试/串行测试):脚本写法完全没问题,可以正常工作。
    • 方案 B (高并发场景) :如果要做高并发,不建议用全局属性传 token。应该将登录请求和业务请求放在同一个线程组 内,利用 JMeter 的默认变量作用域(同一个线程内变量天然共享),去掉 BeanShell 脚本,直接在 Header Manager 中使用 ${token} 即可。
2. 阶梯加压策略(Stepping Thread Group)

使用了 jp@gc - Stepping Thread Group 插件。

  • 配置:初始 4 线程,每 30 秒增加 4 个线程,直到 32 个,保持 60 秒。
  • 用途 :这是非常专业的负载测试(Load Testing)配置,用于观察系统在用户量逐步增加时的性能拐点(TPS 何时开始下降,响应时间何时飙升)。

建议

你的配置中 This group will start 32 threadsFinally, stop 4 threads every 3 seconds 配合得很好,模拟了平滑的上线和下线,避免了瞬间断崖式压力。

3. 参数化配置(CSV Data Set Config)
  • 配置 :读取 codes.csv,变量名为 codeRecycle on EOF 设为 True

  • 场景 :用于登录接口,传入不同的 code

注意点

  • Sharing Mode :你选择了 All threads。这意味着所有线程(用户)会依次读取 CSV 文件中的行(线程 1 读第 1 行,线程 2 读第 2 行...)。
  • Recycle on EOF = True :如果 CSV 里的 code 用完了,会从头开始读。
  • 潜在问题 :如果这是登录接口,重复使用同一个 code 可能会失败(因为 code 通常是一次性的)。请确保你的 codes.csv 数据量足够大,或者生成 code 的机制支持重复使用。
4. 关键配置细节审查
  1. 关于 BeanShell 的性能隐患

目前使用了 BeanShell PostProcessor 来设置属性。

  • 问题:BeanShell 在 JMeter 中性能较差,且在高并发下会消耗大量 CPU。

  • 替代方案 :如果你只是为了传参,推荐使用 __setProperty 函数直接写在登录请求的"参数"或"Body"里(虽然不美观),或者使用 JSR223 PostProcessor 并选择 Groovy 语言。

  • Groovy 写法

    groovy 复制代码
    // 将变量 token 的值存入全局属性
    props.put("token", vars.get("token"))
  1. 关于 Header Manager 的引用(图 10)

目前使用了 bearer ${__P(token)}

  • ${__P(token)} 是读取 JMeter 属性的标准函数。
  • 如果你的 BeanShell 脚本执行成功,这里就能正确拿到值。
  • 调试技巧 :如果发现请求 401 未授权,请检查 BeanShell 是否执行成功,或者查看日志(jmeter.log)。
5. 整体流程逻辑梳理

根据截图,你的脚本逻辑如下:

  1. 准备阶段 :从 CSV 读取 code
  2. 登录接口 (IAM认证登录) :POST 请求,发送 JSON {"code": "${code}"}。现使用挡板机制,另外启动服务器拦截请求,返回固定结果token。详见后面第六部分:技巧。
  3. 提取 :使用 JSON Extractor 提取响应中的 $.value 赋值给变量 token
  4. 转换 :使用 BeanShell 将 token 变量转为全局属性 token
  5. 业务请求 1 (查看当前用户信息) :GET 请求,Header 中携带 bearer ${__P(token)}
  6. 业务请求 2 & 3:获取菜单、系统信息(复用上述 token)。
  7. 压力控制:使用 Stepping Thread Group 控制并发节奏。
6. 下一步建议

下面是 "查看结果树"和"聚合报告"。在性能测试中,这两步是分析结果的关键。重点分析:

  1. 聚合报告的指标解读(TPS、90% Line、错误率)。
  2. 查看结果树中的断言(是否有验证响应内容)。
  3. 监听器的性能影响(在大规模压测时是否应该禁用"查看结果树")。

第二步:结果分析(监听器详解)

你添加了非常专业的监听器,但在截图中它们都是空的(Waiting for samples...)。这是因为你还没有运行或者没有产生数据。

以下是针对你截图中监听器的专业解读:

1. 查看结果树
  • 作用:调试脚本的神器。用来查看请求是否发送成功,响应数据是否符合预期。
  • 进阶用法
    • 断言 :在目前的脚本中还没有添加断言。建议右键点击 HTTP 请求 -> 添加 -> 断言 -> 响应断言。比如检查响应文本中是否包含 "code":200
    • 注意 :在进行大规模压测(如 100+ 并发)时,必须禁用"查看结果树",因为它极其消耗内存,会导致 JMeter 崩溃。
2. jp@gc - Active Threads Over Time
  • 作用:展示随时间变化的活跃线程数。
  • 解读:结合你之前的"阶梯线程组"配置,这个图表应该画出一个阶梯状上升的折线图。如果线条是平的,说明线程没有按预期启动;如果线条突然掉到底,说明有线程报错退出了。
3. jp@gc - Response Times Over Time
  • 作用:展示随时间变化的响应时间趋势。
  • 解读 :这是判断系统稳定性的关键。
    • 理想状态:线条平稳,没有剧烈波动。
    • 异常状态:随着并发增加(对应上面的阶梯图),如果响应时间呈指数级上升(陡峭变高),说明系统达到了瓶颈,处理不过来了。
4. jp@gc - Transactions per Second
  • 作用:每秒事务数/吞吐量,即 QPS。
  • 解读
    • 拐点 :随着并发增加,QPS 会上升。当 QPS 不再随并发增加而上升,甚至开始下降时,那个点就是系统的最大吞吐量
5. 聚合报告
  • 作用:最终的汇总成绩单。
  • 关键指标
    • Samples:总请求数。
    • Average:平均响应时间。
    • 90% Line:90% 的请求都小于这个时间(比平均值更有参考价值)。
    • Error %:错误率。压测中这个必须为 0%,否则测试结果无效。
    • Throughput:吞吐量,这是衡量服务器性能最核心的指标。

第四部分:脚本优化

你的脚本逻辑是通的,但为了适应 Java 8 环境和更高并发,有以下 3 点优化建议:

1. 替换 BeanShell(性能优化)

在之前的截图中,你使用了 BeanShell PostProcessor 来设置全局属性。

  • 问题:BeanShell 在 JMeter 中性能较差,且在高并发下会成为瓶颈。

  • 优化 :既然你用的是 JMeter 5.6.2,建议改用 JSR223 PostProcessor + Groovy 语言。

  • 代码修改

    groovy 复制代码
    // 替换掉 BeanShell 中的代码
    props.put("token", vars.get("token"));

    解释:props 代表 JMeter 属性(全局),vars 代表变量(局部)。这行代码效果和你的 BeanShell 一样,但速度快得多。

2. 完善跨线程组取值

你在 Header Manager 中使用了 ${__P(token)}

  • 确认__P 函数用于读取属性。这完全正确。
  • 备选 :也可以使用 ${__property(token)},效果一样。
  • 注意 :确保登录请求在业务请求之前执行完毕。你的"阶梯线程组"配置中,如果所有请求都在同一个组里,要确保登录只执行一次或者在循环外。看你的结构,似乎是把登录和业务放在了同一个线程组里?
    • 如果是这样,要注意 CSV Data Set 的"共享模式"。如果是 All threads,多个线程可能会抢到同一行账号数据。
3. 添加事务控制器

你的截图里,一个业务操作(如"获取用户信息")可能包含多个请求。

  • 建议 :右键线程组 -> 添加 -> 逻辑控制器 -> 事务控制器
  • 操作:把相关的 HTTP 请求拖进去,勾选"Generate parent sample"。
  • 效果:聚合报告里会把这一组请求合并成一条记录,显示整个业务流程的总耗时,而不是单个接口的耗时。

第五部分:执行与验证步骤

现在你可以运行测试了,请按以下步骤操作:

  1. 清理环境 :点击工具栏上的扫帚图标(清理),清空上一次的结果。
  2. 运行 :点击绿色的启动按钮
  3. 观察
  • 先看 jp@gc - Active Threads,确认线程数是否按你预期的阶梯(10, 20, 30...)在增加。
  • 再看 jp@gc - Response Times,看响应时间是否有剧烈抖动。
  1. 停止:测试运行完后,点击停止。
  2. 分析 :查看 聚合报告 中的 Throughput 和 Error %。

总结:

你的脚本结构已经具备了中级水平(参数化+关联+跨线程/全局变量)。接下来的重点就是运行它,并学会看懂那几个图表。如果运行中遇到报错(比如 401 Unauthorized),通常是 Token 没取对或者没传过去,那时候再回来检查 JSON Extractor 和 Header Manager 即可。


第六部分:技巧

1. 挡板机制

在性能测试中,调用第三方外部接口是个"大坑"。这些接口不受你控制,可能不稳定、有频率限制,甚至还要额外付费。

为了屏蔽这些"外部不确定因素",通常需要引入挡板机制(Service Virtualization / Mocking)。这就好比拍电影时,不直接去外太空,而是在绿幕前拍摄,后期再合成背景。

以下是几种主流且实用的挡板方案,按推荐程度排序:


①🧪 使用 WireMock(最推荐,轻量级)

WireMock 是目前最流行的 HTTP Mock 服务工具,它独立于 JMeter 运行,专门用来模拟 HTTP 接口。

原理

你在本地或测试环境启动一个 WireMock 服务。JMeter 不直接调用第三方接口,而是调用 WireMock。WireMock 收到请求后,根据预设规则返回一个"假"的固定响应。

操作步骤
  1. 下载 :下载 wiremock-standalone.jar

  2. 启动 :在命令行运行 java -jar wiremock-standalone.jar

  3. 编写桩(Stub):告诉 WireMock 当收到特定请求时返回什么。

  • 例如,创建一个 JSON 文件 mock-response.json
json 复制代码
{
  "request": {
    "method": "GET",
    "url": "/api/v1/external-system"
  },
  "response": {
    "status": 200,
    "body": "{\"code\": 200, \"data\": \"Mocked Success\"}",
    "headers": {
      "Content-Type": "application/json"
    },
    "fixedDelayMilliseconds": 50  // 模拟网络延迟,更真实
  }
}
  1. 加载配置 :将上述文件放入 WireMock 的 mappings 目录。

  2. JMeter 修改

  • 使用"HTTP 请求默认值"或"HTTP 信息头管理器"。
  • 将原本指向第三方域名的请求,修改为指向 localhost:8080(WireMock 默认端口)。

②⚙️ JMeter 内部实现 Mock(无需额外工具)

如果你不想部署额外服务,可以直接在 JMeter 内部"截胡"请求。

JSR223 Sampler :模拟 HTTP 服务是一个非常灵活且强大的方案。它允许你编写 Groovy 脚本来启动一个轻量级的嵌入式服务器(通常使用 NanoHTTPD 或 Java 原生 Socket),从而完全控制返回的响应内容、状态码和延迟,非常适合做挡板(Mock)测试。

HTTP Mirror Server:在 JMeter 的官方组件中,更加如何"开箱即用"特性的组件,它可以原封不动地返回请求内容,适合快速验证请求格式。

下面介绍,高阶的 JSR223 自定义挡板 (满足你"返回固定结果"的需求)和 原生的 HTTP Mirror Server(用于快速调试)。

方案一:使用 JSR223 Sampler 模拟 HTTP 服务(最灵活)

这种方式不需要依赖外部工具,直接在 JMeter 内部运行一个微型服务。我们需要使用 Groovy 脚本结合 NanoHTTPD(JMeter 自带或需引入 jar 包)或者简单的 Socket 来实现。

注意 :JMeter 的 Sampler 是"客户端"角色,要把它变成"服务端",通常是在 setUp Thread Group 中启动一个后台线程来监听端口。

  1. 实现步骤

启动脚本(放在 setUp Thread Group 的 JSR223 Sampler 中):

groovy 复制代码
import com.sun.net.httpserver.HttpServer;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpExchange;
import java.io.OutputStream;
import java.net.InetSocketAddress;

// 1. 配置端口和返回内容
int port = 48001;
String fixedResponse = "{\"header\": {\"code\": 10000000}, \"body\": {\"value\": \"eyJhbGc\"}}";

// 2. 创建服务器实例
// 注意:这里使用 InetSocketAddress 绑定到 0.0.0.0 以允许外部访问,或者 "localhost" 仅限本地
HttpServer server = HttpServer.create(new InetSocketAddress(port), 0);

// 3. 定义处理逻辑 (上下文路径为 /)
server.createContext("/", new HttpHandler() {
    @Override
    public void handle(HttpExchange exchange) throws IOException {
        // 获取请求方法 (GET, POST 等)
        String method = exchange.getRequestMethod();
        // log.info("Mock Server received: " + method + " " + exchange.getRequestURI());

        // 设置响应头
        exchange.getResponseHeaders().add("Content-Type", "application/json");

        // 将响应字符串转换为字节流
        byte[] responseBytes = fixedResponse.getBytes("UTF-8");

        // 发送响应头 (状态码 200, 内容长度)
        exchange.sendResponseHeaders(200, responseBytes.length);

        // 发送响应体
        OutputStream os = exchange.getResponseBody();
        os.write(responseBytes);
        os.close();
    }
});

// 4. 启动服务器
// 使用单线程 executor,或者根据需要设置线程池
server.setExecutor(null); 
server.start();

log.info(">>> Mock HTTP Server started on port: " + port);

// 5. 将 server 对象存入 props,以便在测试结束后关闭它
props.put("mockServer", server);
  1. 验证方案

启动上述脚本后,你的"挡板"就已经在 48001 端口运行了。

  1. 添加 HTTP 请求:在测试计划中添加一个普通的 HTTP 请求采样器。
  2. 配置目标
  • 服务器名称或IP : localhost
  • 端口号 : 48001
  • 协议 : http
  1. 运行测试:运行测试计划,查看"查看结果树"。
  2. 预期结果 :你应该能看到请求成功,且响应数据正是你在脚本中写死的 {"code": 10000000,...}

方案二:使用 JMeter 原生 HTTP Mirror Server(最简单)

如果你不需要复杂的逻辑判断,只是想看看发出的请求长什么样,或者需要一个简单的回显服务,JMeter 自带的 HTTP Mirror Server 是最快的选择。它会将接收到的请求原封不动地作为响应返回。

  1. 启动步骤

  2. 在 JMeter 界面中,右键点击 "工作台" (WorkBench)"测试计划"

  3. 选择 "非测试元件" (Non-Test Elements) -> "HTTP 镜像服务器" (HTTP Mirror Server)

  4. 配置组件

  • 端口 : 默认 8081(如果端口被占用,请修改)。
  • Max number of Threads: 线程数,可根据需要设置。
  1. 启动服务 :点击组件面板上的 "启动" (Start) 按钮。

  2. 验证与使用

启动后,该服务器就在本地 8081 端口监听。

  • 修改采样器 :将你现有的 HTTP 请求采样器的"服务器名称或 IP"改为 localhost,端口改为 8081
  • 运行并查看
    • 运行测试。
    • 添加 "查看结果树" 监听器。
    • 点击具体的请求,查看 "响应数据" 标签页。
  • 结果分析:你会发现响应数据里包含了你刚才发出的请求头、请求体等所有信息。这非常适合用来调试"我的请求头是否设置正确"、"Cookie 是否带上"等问题。
方案对比总结

对比

特性 JSR223 Sampler (自定义脚本) HTTP Mirror Server (原生组件)
灵活性 ⭐⭐⭐⭐⭐ (极高,可写代码控制逻辑) ⭐ (低,仅回显请求)
返回内容 可返回固定结果、动态数据或根据请求参数返回不同结果 只能原样返回接收到的请求内容
实现难度 中等 (需要编写 Groovy/Java 代码) 简单 (拖拽组件,点击启动)
适用场景 模拟第三方接口挡板、模拟超时/错误码、复杂业务逻辑解耦 调试请求格式、验证 HTTP 头/签名是否正确

📌 总结建议

如果目标是"返回固定结果 ",方案一(JSR223) 是最佳选择。虽然代码稍微多一点,但它能让你完全掌控挡板的行为,比如模拟接口超时(在脚本里 Thread.sleep())或模拟 500 错误。。


③🛡️ 使用 Nginx 反向代理(运维级方案)

如果你的测试环境有 Nginx,这是最优雅的方案,完全对 JMeter 透明。

原理

在测试环境的 Nginx 配置中,拦截对第三方系统的请求,直接返回本地文件。

配置示例
nginx 复制代码
location /api/external/ {
    # 屏蔽真实调用,直接返回本地文件
    default_type application/json;
    return 200 '{"code":200, "msg":"Nginx Mock Success"}';

    # 或者返回一个静态文件
    # alias /path/to/mock/data.json;
}
优点
  • JMeter 脚本完全不用改,还是调原来的域名。
  • 所有调用该环境的流量都被 Mock,干净彻底。
④📊 方案对比与总结

方案对比

方案 实施难度 灵活性 适用场景
WireMock ⭐⭐ ⭐⭐⭐⭐⭐ 最推荐。适合需要模拟复杂逻辑、不同返回码、网络延迟的场景。
Nginx Mock ⭐⭐⭐ ⭐⭐⭐ 适合运维配合,不想改动 JMeter 脚本,且是固定返回值的场景。
JMeter 内部 Mock ⭐⭐ ⭐⭐ 适合临时调试,或者不想依赖外部工具的小型测试。

📌 总结:实战步骤

建议采用 WireMock 方案:

  1. 启动 WireMock,模拟那个"外部系统"的接口,设定延迟 100ms,返回成功 JSON。
  2. 修改 JMeter
  • 在"HTTP 请求默认值"里,或者针对那个特定的 HTTP 请求,把"服务器名称或 IP"改成 WireMock 的地址。
  • 或者,更高级一点,使用 JMeter 的"CSV 数据文件设置"来控制域名:
    • CSV 内容:ENV,DOMAIN -> TEST,real-api.com / MOCK,localhost:8080
    • HTTP 请求里填 ${DOMAIN}
  1. 这样,你只需切换 CSV 里的参数,就能在"真实调用"和"挡板模式"之间随意切换。
相关推荐
cheems95272 小时前
[SpringMVC] SpringWebMVC常见注解介绍
java·springmvc·注解
me8322 小时前
【Java】Spring MVC接口执行流程详解:从前端请求到参数封装全解析(前端到底是怎么和后端交互的?)
java·spring·mvc
niucloud-admin2 小时前
插件开发——upgrade 插件版本升级
java
vortex52 小时前
Gradle 从入门到实战
java·gradle
代码丰2 小时前
Zero Code Studio:LangChain4j 工具调用 + LangGraph4j 工作流双模式的 AI 网站生成系统
java·人工智能
云烟成雨TD2 小时前
Spring AI 1.x 系列【28】基于内存和 MySQL 的多轮对话实现案例
java·人工智能·spring
Lyyaoo.2 小时前
【JAVA基础面经】String、StringBuffer、StringBuilder
java·开发语言
TeamDev2 小时前
JxBrowser 8.18.2 版本发布啦!
java·前端·跨平台·桌面应用·web ui·jxbrowser·浏览器控件
晴天sir2 小时前
Redis 在业务中的几种典型用法
java·数据库·redis