一、JMeter 基础概念

核心结论

JMeter 是 Apache 开源的性能测试工具,核心由线程组、采样器、监听器、配置元件、前置/后置处理器、断言六类组件构成。理解各组件的执行顺序和作用域是写出稳定测试脚本的基础。


1. 核心组件与执行顺序

JMeter 测试计划的执行顺序固定,理解这个顺序有助于正确放置各类组件。

组件类型 作用 典型示例
线程组 模拟并发用户,控制线程数/循环次数/Ramp-Up Thread Group
配置元件 提供请求所需的配置数据 CSV Data Set Config、HTTP Cookie Manager
前置处理器 请求发送前执行,准备动态数据 JSR223 PreProcessor、User Parameters
采样器 实际发送请求 HTTP Request、JDBC Request
后置处理器 响应返回后执行,提取数据 JSON Extractor、正则表达式提取器
断言 验证响应是否符合预期 Response Assertion、JSON Assertion
监听器 收集并展示测试结果 聚合报告、查看结果树

2. 前置处理器 vs 后置处理器

前置和后置处理器是最容易混淆的两类组件,核心区别在于执行时机。

特性 前置处理器 后置处理器
执行时机 采样器请求发送之前 采样器收到响应之后
典型用途 动态生成参数、修改请求头、准备测试数据 提取响应数据、保存变量、调试
常见组件 JSR223 PreProcessor、CSV Data Set Config JSON 提取器、正则表达式提取器、Debug PostProcessor
失败影响 失败会阻止采样器执行 失败不影响采样器本身结果

作用域规则: 附加到采样器则仅对该采样器生效;附加到线程组或逻辑控制器则对其下所有采样器生效。

典型流程示例:

CSV Data Set Config(前置)→ 读取用户名密码
JSR223 PreProcessor(前置)→ 生成加密参数
HTTP 请求(采样器)→ 发送登录请求
JSON 提取器(后置)→ 提取 Token 存入变量

二、JMeter 运行模式与进程

核心结论

JMeter 支持 GUI 模式和命令行(CLI)模式,正式压测必须使用命令行模式,GUI 模式仅用于脚本调试。GUI 模式下 JMeter 自身消耗大量资源,会干扰测试结果。


1. 运行模式对比

模式 适用场景 进程数 资源消耗
GUI 模式 脚本录制、调试、结果查看 1 个主进程
CLI 模式 正式压测 1 个主进程
分布式模式 超大并发(单机不够用) 1 Master + N Slaves 按节点数线性扩展

2. 命令行运行

# 基本运行
jmeter -n -t test.jmx -l result.jtl

# 指定日志文件
jmeter -n -t test.jmx -l result.jtl -j jmeter.log

# 运行后生成 HTML 报告
jmeter -n -t test.jmx -l result.jtl -e -o ./report
参数 说明
-n 非 GUI 模式
-t 测试计划文件(.jmx)
-l 结果文件(.jtl)
-e 测试结束后生成报告
-o 报告输出目录

3. JVM 参数调优

编辑 bin/jmeterbin/jmeter.bat,修改 JVM 参数:

JVM_ARGS="-Xms1g -Xmx4g -XX:MaxMetaspaceSize=512m -XX:+UseG1GC"
参数 说明
-Xms1g 初始堆内存 1GB
-Xmx4g 最大堆内存 4GB
-XX:MaxMetaspaceSize=512m 元空间上限
-XX:+UseG1GC 使用 G1 垃圾收集器

4. 查看 JMeter 进程

# Linux/macOS
ps aux | grep jmeter

# 查看 Java 进程
jps -l | grep jmeter

# 查看线程级 CPU 占用
top -H -p <JMeter_PID>

三、线程组参数详解

核心结论

线程组的三个核心参数(线程数、Ramp-Up、循环次数)共同决定了测试的并发模式和持续时长。错误的 Ramp-Up 配置会导致压测一开始就打满并发,无法观察系统在不同负载下的表现曲线。


1. 核心参数说明

配置路径: 右键测试计划 → Add → Threads → Thread Group

参数 说明
Number of Threads(线程数) 并发用户数,每个线程模拟一个用户
Ramp-Up Period(启动时间/s) 所有线程在多少秒内全部启动完毕
Loop Count(循环次数) 每个线程执行测试计划的次数,勾选 Infinite 表示持续运行
Duration(持续时长) 勾选 Scheduler 后可设置测试持续秒数,比循环次数更精确
Startup delay 延迟多少秒后开始启动线程

2. Ramp-Up 设置策略

Ramp-Up 控制线程启动速率,公式为:

每秒启动线程数 = 线程数 / Ramp-Up 时间(秒)
场景 推荐设置
阶梯加压,观察拐点 Ramp-Up = 线程数(每秒启动 1 个线程)
模拟瞬间涌入 Ramp-Up = 0 或极小值(所有线程同时启动)
稳定负载测试 Ramp-Up = 线程数 × 3(缓慢启动,系统有预热时间)

例如:100 个线程,Ramp-Up 设为 100 秒,则每秒启动 1 个线程,第 100 秒才达到 100 并发。


3. 常用线程组类型

线程组类型 特点 适用场景
Thread Group(默认) 固定线程数,线性启动 稳定负载测试
Stepping Thread Group(插件) 分阶段逐步增加线程数 寻找系统性能拐点
Ultimate Thread Group(插件) 可精细控制每个阶段的线程数和持续时间 复杂负载模型
Concurrency Thread Group(插件) 维持目标并发数恒定 恒定并发压测

插件安装:JMeter 插件管理器 → 搜索 “Custom Thread Groups” → 安装。


4. 典型配置示例

场景:测试系统在 200 并发下的稳定性,持续 10 分钟

Number of Threads: 200
Ramp-Up Period: 60          ← 60秒内逐步达到200并发
勾选 Scheduler
Duration: 600               ← 持续600秒(10分钟)
Startup delay: 0

场景:寻找系统最大承载量(阶梯加压)

使用 Stepping Thread Group 插件:

Start Threads Count: 10     ← 初始10个线程
Add Threads Every: 30 sec   ← 每30秒
Thread Count to Add: 10     ← 增加10个线程
Max Threads Count: 200      ← 最多加到200

四、HTTP 配置元件

核心结论

HTTP Cookie Manager 和 HTTP Header Manager 是 HTTP 测试中最常用的两个配置元件。前者自动管理会话 Cookie,后者统一设置请求头(如 Content-Type、Authorization)。两者放在线程组级别时对所有请求生效,放在单个请求下时仅对该请求生效。


1. HTTP Cookie Manager

作用: 自动收集响应中的 Set-Cookie 并在后续请求中携带,模拟浏览器的 Cookie 行为(保持登录态)。

配置路径: 右键线程组 → Add → Config Element → HTTP Cookie Manager

配置项 说明
Clear cookies each iteration 每次循环开始时清除 Cookie(模拟新用户登录)
Cookie Policy 推荐选 standard,兼容大多数场景

使用建议:

  • 每个线程组添加一个 Cookie Manager
  • 需要模拟多用户独立 Cookie 时,勾选 “Clear cookies each iteration” 并配合 CSV Data Set Config 参数化账号

2. HTTP Header Manager

作用: 为请求统一添加 HTTP 请求头,如 Content-Type、Authorization、自定义业务头。

配置路径: 右键线程组(或单个请求)→ Add → Config Element → HTTP Header Manager

常用请求头配置示例:

Header 名称 说明
Content-Type application/json JSON 接口必须设置
Authorization Bearer ${__P(globalToken,)} 携带全局 Token
Accept application/json 期望返回 JSON
User-Agent Mozilla/5.0 ... 模拟浏览器 UA

注意: 同一请求上如果有多个 Header Manager(线程组级 + 请求级),两者的 Header 会合并;若存在同名 Header,请求级的会覆盖线程组级的。


3. HTTP Request Defaults

作用: 统一设置所有 HTTP 请求的默认值(服务器地址、端口、协议),避免每个请求都重复填写。

配置路径: 右键线程组 → Add → Config Element → HTTP Request Defaults

配置项 说明
Server Name or IP 被测服务器地址,如 api.example.com
Port Number 端口,如 8080
Protocol httphttps
Content encoding 编码格式,通常填 UTF-8

配置后,各 HTTP 请求只需填写路径(如 /api/users),服务器地址从默认值继承,修改环境时只需改一处。


五、定时器(Timer)

核心结论

定时器用于控制 JMeter 发送请求前的等待时间,模拟真实用户的思考时间(Think Time)。不加定时器时 JMeter 会尽可能快地连续发请求,与真实用户行为差异较大。Synchronizing Timer(同步定时器)是实现集合点(瞬时并发)的专用工具。


1. 常用定时器类型

配置路径: 右键采样器(或线程组)→ Add → Timer → 选择定时器类型

定时器类型 等待规则 适用场景
Constant Timer 固定等待 N 毫秒 模拟固定思考时间
Uniform Random Timer 在 [最小值, 最大值] 范围内均匀随机 模拟不规则用户行为
Gaussian Random Timer 以均值为中心、高斯分布随机 更贴近真实用户分布
Synchronizing Timer 等待 N 个线程全部到达后同时释放 瞬时并发(集合点)测试
Poisson Random Timer 泊松分布随机等待 模拟事件到达的随机性

2. Constant Timer

最简单的定时器,在每次采样器执行前等待固定时间。

配置:

Thread Delay (in milliseconds): 1000    ← 每次请求前等待1秒

作用域: 放在采样器下只对该采样器生效;放在线程组下对所有采样器生效。


3. Gaussian Random Timer

在固定偏移量基础上叠加高斯分布随机延迟,更贴近真实用户行为。

配置:

Deviation (in milliseconds): 500        ← 标准差500ms
Constant Delay Offset (in milliseconds): 1000   ← 固定基准1000ms

实际等待时间 = 基准值 + 高斯随机值,约 99.7% 的等待时间落在 [基准 - 3×标准差, 基准 + 3×标准差] 范围内。


4. Synchronizing Timer(集合点)

Synchronizing Timer 会让线程等待,直到指定数量的线程全部到达该点后,再同时释放,实现真正的瞬时并发。

配置:

Number of Simulated Users to Group by: 50    ← 等待50个线程全部到达
Timeout in milliseconds: 5000               ← 超时时间,超时则强制释放

典型用途: 测试抢购、秒杀等需要大量用户同一时刻发起请求的场景。

使用建议:

  • 集合点线程数 ≤ 实际线程组线程数,否则永远无法凑齐而超时
  • 建议同时设置合理的 Timeout,防止测试卡死
Test Plan
└─ Thread Group(线程数: 100)
   ├─ HTTP Request 1(登录)
   └─ Synchronizing Timer(等待50线程)← 50个线程同时到达后同时发下一请求
      └─ HTTP Request 2(抢购)

六、参数化与数据驱动

核心结论

参数化是性能测试的基础能力,JMeter 提供 CSV Data Set Config(文件驱动)和 JSON/正则表达式提取器(响应驱动)两种主要方式。前者用于准备输入数据,后者用于提取接口返回值供后续请求使用。


1. CSV Data Set Config

CSV Data Set Config 是最常用的参数化方式,适合并发场景下每个线程使用不同数据。

配置路径: 右键线程组 → Add → Config Element → CSV Data Set Config

配置项 说明
Filename CSV 文件路径(建议使用相对路径)
Variable Names 变量名,逗号分隔,对应 CSV 列
Delimiter 分隔符,默认逗号
Sharing Mode All threads(所有线程共享)/ Current thread group
Recycle on EOF 文件读完后是否循环
Stop thread on EOF 文件读完后是否停止线程

CSV 文件示例(testdata.csv):

username,password
user001,pass001
user002,pass002
user003,pass003

在 HTTP 请求中引用变量:

用户名字段:${username}
密码字段:${password}

2. JSON 提取器

JSON 提取器用于从 JSON 格式的响应中提取数据,是接口关联的核心工具。

配置路径: 右键 HTTP 请求 → Add → Post Processors → JSON Extractor

配置项 说明
Names of created variables 提取后存储的变量名
JSON Path expressions JSONPath 表达式
Match No. 0=随机,1=第一个,-1=全部
Default Value 未匹配时的默认值

假设响应体为:

{
  "code": 200,
  "data": {
    "token": "eyJhbGciOiJIUzI1NiJ9...",
    "userId": 1001
  }
}

提取 token 的 JSONPath 表达式:

$.data.token

提取后在后续请求中引用:

Authorization: Bearer ${token}

3. 正则表达式提取器

正则表达式提取器适用于非 JSON 格式的响应,或需要提取响应头中的数据。

配置路径: 右键 HTTP 请求 → Add → Post Processors → Regular Expression Extractor

配置项 说明
Field to check 检查范围:Body / Response Headers / URL
Reference Name 变量名
Regular Expression 正则表达式,用 () 包裹要提取的部分
Template $1$ 表示第一个捕获组
Match No. 1=第一个匹配

从响应头提取 Location 重定向地址:

Field to check: Response Headers
Regular Expression: Location: (.*?)\r\n
Template: $1$

七、内置函数(Functions)

核心结论

JMeter 内置函数以 ${__functionName(params)} 语法调用,无需编写脚本即可实现动态参数生成。常用函数覆盖随机数、时间戳、UUID、计数器、属性读写等高频场景,是参数化的轻量替代方案。


1. 函数助手

打开方式: 菜单栏 → Options → Function Helper Dialog

函数助手提供可视化配置界面:选择函数名 → 填写参数 → 点击 Generate → 复制生成的函数表达式。


2. 常用函数一览

函数 说明 示例
${__Random(1,100,)} 生成指定范围内的随机整数 ${__Random(1,1000,)} 生成 1~1000 的随机数
${__time(,)} 返回当前时间戳(毫秒) ${__time(,)}
${__time(yyyy-MM-dd,)} 格式化当前日期 ${__time(yyyy-MM-dd HH:mm:ss,)}
${__UUID()} 生成 UUID 用于唯一标识符字段
${__counter(TRUE,)} 线程内自增计数器(每次调用+1) ${__counter(FALSE,)} 全局计数器
${__threadNum} 当前线程编号(从 1 开始) 区分不同线程的请求
${__P(key,default)} 读取 JMeter Properties ${__P(globalToken,)}
${__setProperty(key,value,)} 写入 JMeter Properties ${__setProperty(env,prod,)}
${__eval(expression)} 对表达式求值(支持嵌套变量) ${__eval(${varName})}
${__groovy(expression,)} 执行 Groovy 表达式 ${__groovy(new Date().format('yyyyMMdd'),)}

3. 实战示例

生成唯一用户名(时间戳 + 随机数):

user_${__time(,)}_${__Random(1000,9999,)}

生成带日期的订单号:

ORDER_${__time(yyyyMMdd,)}_${__counter(FALSE,)}

读取命令行传入的参数(-Jenv=prod):

${__P(env,dev)}     ← 默认值为 dev,命令行传 -Jenv=prod 时使用 prod

命令行运行时传入参数:

jmeter -n -t test.jmx -l result.jtl -Jenv=prod -Jbaseurl=https://api.example.com

动态生成 JSON 请求体:

在 HTTP 请求的 Body Data 中直接使用函数:

{
  "requestId": "${__UUID()}",
  "timestamp": "${__time(,)}",
  "userId": "${__Random(1000,9999,)}",
  "username": "user_${__threadNum}"
}

八、逻辑控制器

核心结论

逻辑控制器控制采样器的执行逻辑,包括条件分支、循环、遍历变量、随机选择等。合理使用逻辑控制器可以模拟真实用户行为(如按概率随机访问不同页面),减少重复请求配置,使测试场景更贴近生产流量。


1. 常用逻辑控制器

配置路径: 右键线程组 → Add → Logic Controller → 选择类型

控制器 功能 典型用途
If Controller 条件为真时执行子节点 登录成功才执行后续操作
Loop Controller 循环执行子节点 N 次 重复某个操作指定次数
ForEach Controller 遍历一组变量(_1, _2…) 遍历 JSON 提取器提取的多个值
Once Only Controller 每个线程只执行一次 登录操作只执行一次
Interleave Controller 每次循环轮流执行一个子节点 模拟用户依次浏览不同页面
Random Controller 每次随机执行一个子节点 模拟随机行为
Random Order Controller 随机顺序执行所有子节点 随机化操作顺序

2. If Controller

基于条件表达式决定是否执行子节点,条件为 true 时执行。

配置路径: 右键线程组 → Add → Logic Controller → If Controller

配置项 说明
Condition 条件表达式,结果为 truefalse
Interpret Condition as Variable Expression 勾选后条件可以直接使用 ${varName} 格式
Evaluate for all children 勾选后对每个子节点都重新评估条件

条件表达式示例(使用 __jexl3 函数):

${__jexl3("${responseCode}" == "200",)}
${__jexl3(${errorCount} > 0,)}
${__jexl3("${userRole}" == "admin",)}

3. ForEach Controller

遍历 JSON 提取器或 CSV 提取的多个值,适合处理列表类响应数据。

配置:

Input variable prefix: userId    ← 变量前缀(对应 userId_1, userId_2...)
Start index for loop: 1
End index for loop: ${userId_matchNr}   ← matchNr 是提取器自动生成的总数变量
Output variable name: currentUserId    ← 每次迭代的当前值

使用场景:JSON 提取器设置 Match No. = -1 提取所有匹配值,ForEach Controller 遍历每个值依次发请求:

Test Plan
└─ Thread Group
   ├─ HTTP Request(获取用户列表)
   │  └─ JSON Extractor(变量名: userId, Match No: -1)← 提取所有 userId
   └─ ForEach Controller(prefix: userId, output: currentUserId)
      └─ HTTP Request(获取用户详情 /users/${currentUserId})

4. Once Only Controller

Once Only Controller 内的采样器在整个测试中每个线程只执行一次(无论循环多少次),适合放登录操作。

Test Plan
└─ Thread Group(Loop Count: 10)
   ├─ Once Only Controller
   │  └─ HTTP Request(登录,只执行1次)
   │     └─ JSON Extractor(提取 Token)
   └─ HTTP Request(业务请求,执行10次)← 使用登录获取的 Token

5. Random Controller

Random Controller 每次循环随机选择一个子节点执行,用于模拟用户随机浏览行为。

Test Plan
└─ Thread Group
   └─ Random Controller
      ├─ HTTP Request(首页)
      ├─ HTTP Request(商品列表)
      ├─ HTTP Request(搜索)
      └─ HTTP Request(商品详情)

每次循环随机执行其中一个请求,模拟真实用户的随机访问模式。


九、断言(Assertion)

核心结论

断言用于验证服务器响应是否符合预期,是区分"请求成功"和"业务正确"的关键手段。HTTP 状态码 200 只代表请求通了,不代表业务逻辑正确,必须通过断言校验响应内容。


1. 常用断言类型

断言类型 适用场景
响应断言(Response Assertion) 校验响应体/响应码/响应头包含或匹配指定文本
JSON 断言(JSON Assertion) 校验 JSON 响应中某个字段的值
持续时间断言(Duration Assertion) 校验响应时间不超过指定毫秒数
大小断言(Size Assertion) 校验响应体字节数在指定范围内
JSR223 断言 用 Groovy 脚本编写自定义校验逻辑

2. 响应断言配置

配置路径: 右键 HTTP 请求 → Add → Assertions → Response Assertion

配置项 说明
Field to Test 校验范围:Response Body / Response Code / Response Headers
Pattern Matching Rules Contains(包含)/ Equals(完全匹配)/ Matches(正则)/ Not(取反)
Patterns to Test 期望出现的字符串或正则

校验响应码为 200:

Field to Test: Response Code
Pattern Matching Rules: Equals
Patterns to Test: 200

校验响应体包含 "success":true

Field to Test: Response Body
Pattern Matching Rules: Contains
Patterns to Test: "success":true

3. JSON 断言配置

配置路径: 右键 HTTP 请求 → Add → Assertions → JSON Assertion

JSON 断言直接基于 JSONPath 校验字段值,比响应断言更精准。

配置项 说明
Assert JSON Path exists JSONPath 表达式,如 $.data.token
Additionally assert value 勾选后可校验具体值
Expected value 期望的字段值
Invert assertion 取反,校验该字段不存在或值不匹配

校验响应中 code 字段等于 200:

Assert JSON Path exists: $.code
Additionally assert value: 勾选
Expected value: 200

4. JSR223 断言(自定义逻辑)

配置路径: 右键 HTTP 请求 → Add → Assertions → JSR223 Assertion

当需要复杂校验逻辑时(如比较两个字段、校验数组长度),使用 JSR223 断言:

import groovy.json.JsonSlurper

def response = new JsonSlurper().parseText(prev.getResponseDataAsString())

// 校验 code 为 200 且 data 不为空
if (response.code != 200 || response.data == null) {
    AssertionResult.setFailure(true)
    AssertionResult.setFailureMessage("断言失败:code=${response.code}, data=${response.data}")
}

5. 断言失败的处理

断言失败时,该请求在聚合报告中计入 Error%,在查看结果树中标红显示失败原因。

注意事项:

  • 断言应放在需要校验的采样器下,作为子节点
  • 放在线程组下则对该线程组所有请求生效
  • 正式压测时建议只保留关键断言,过多断言会影响性能

十、脚本录制与 HTTPS 配置

核心结论

JMeter 内置 HTTP(S) Test Script Recorder 可以录制浏览器操作生成测试脚本。录制 HTTPS 请求需要将 JMeter 生成的根证书安装到浏览器,否则浏览器会拒绝 JMeter 的代理连接。


1. 录制器配置步骤

第一步:启动录制器

右键测试计划 → Add → Non-Test Elements → HTTP(S) Test Script Recorder

配置项 说明
Port 代理端口,默认 8888
Target Controller 录制结果存放的位置(通常选线程组)
URL Patterns to Include 只录制匹配的 URL,如 .*api.*
URL Patterns to Exclude 排除静态资源,如 .*\.(css|js|png|jpg)

第二步:配置浏览器代理

浏览器网络设置 → 手动代理 → HTTP 代理:localhost,端口:8888

第三步:安装 JMeter 根证书(HTTPS 必须)

启动录制器后,浏览器访问 http://localhost:8888,下载 JMeter 根证书(ApacheJMeterTemporaryRootCA.crt)。

Chrome 安装证书路径:

设置 → 隐私和安全 → 安全 → 管理证书 → 受信任的根证书颁发机构 → 导入

Firefox 安装证书路径:

设置 → 隐私与安全 → 证书 → 查看证书 → 证书颁发机构 → 导入

注意: JMeter 根证书有效期为 7 天,过期后需重新生成并重新安装。


2. 跨域重定向抓包

当应用存在跨域重定向(如 OAuth 登录跳转到第三方域名)时,默认配置可能丢失中间请求。

方案:禁用自动重定向,手动处理

在 HTTP 请求中取消勾选 Follow RedirectsRedirect Automatically,JMeter 将返回 3xx 响应,可在结果树中看到所有中间跳转。

从 3xx 响应头提取 Location 地址:

Field to check: Response Headers
Regular Expression: Location: (.*?)\r\n
Template: $1$
变量名:redirectURL

后续请求使用 ${redirectURL} 作为路径,并在 HTTP 头管理器中补充跨域头:

Origin: ${目标域名}
Referer: ${来源页面URL}

十一、JSR223 脚本使用

核心结论

JMeter 支持 BeanShell 和 JSR223 两种脚本处理器,官方推荐使用 JSR223 + Groovy。Groovy 编译为字节码执行,性能远优于 BeanShell 的解释执行,且支持现代 Java/Groovy 语法。


1. BeanShell vs JSR223

特性 BeanShell JSR223 + Groovy
执行方式 解释执行 编译为字节码
性能 较差,高并发下影响明显 优秀
语法支持 Java 1.5 基础语法,不支持 ?.、lambda 支持现代 Groovy/Java 语法
推荐程度 不推荐(遗留兼容) 推荐

2. 常用内置变量

在 JSR223 脚本中可直接使用以下内置对象:

变量 类型 说明
vars JMeterVariables 读写当前线程变量
props Properties 读写全局属性
prev SampleResult 上一个请求的结果
sampler HTTPSamplerProxy 当前采样器(仅 PostProcessor 中自动可用)
log Logger 写日志
ctx JMeterContext 当前线程上下文

3. 常用脚本示例

将上一个请求的完整响应体存入变量:

vars.put("full_response", prev.getResponseDataAsString())

从响应头中遍历查找特定 Header(getHeader(String) 方法不存在,必须遍历):

try {
    def referer = sampler.getHeaderManager()?.getHeaders()?.find {
        it.getName().equalsIgnoreCase("Referer")
    }?.getValue()

    if (referer) {
        def query = new URI(referer).query
        query?.split('&')?.each { pair ->
            def (key, value) = pair.split('=', 2).collect { URLDecoder.decode(it, "UTF-8") }
            if (key == "code") {
                vars.put("extracted_code", value)
                log.info("Extracted code: " + value)
            }
        }
    }
} catch (Exception e) {
    log.error("Error extracting code: ", e)
}

在 PreProcessor 中手动获取当前 Sampler(sampler 变量在 PreProcessor 中不自动注入):

import org.apache.jmeter.threads.JMeterContextService

def sampler = JMeterContextService.getContext().getCurrentSampler()
if (sampler != null) {
    // 操作 sampler
}

4. 将上一个请求响应作为下一个请求的 Body

方法一:JSR223 PostProcessor 存入变量

vars.put("full_response", prev.getResponseDataAsString())

在下一个 POST 请求的 Body Data 中引用:

${full_response}

方法二:正则表达式提取器提取整个响应

Field to check: Body
Regular Expression: (?s)(.*)
Template: $1$
变量名:response_data

方法三:直接引用前一个响应(最简单)

${__prev(ResponseData)}

十二、跨线程组变量共享

核心结论

JMeter 中 vars 变量只在当前线程内有效,跨线程组共享数据必须使用 props(JMeter Properties)。典型场景:第一个线程组登录获取 Token,后续线程组的所有请求都需要携带该 Token。


1. vars vs props 区别

特性 vars(Variables) props(Properties)
作用域 当前线程内 全局,所有线程组可见
生命周期 线程结束即销毁 测试计划运行期间持续存在
写入方式 vars.put("key", value) props.put("key", value)
读取方式 ${varName}vars.get("key") ${__P(key,)}props.get("key")
分布式测试 不跨节点 需手动同步到各 Slave 节点

2. 完整实现步骤

第一步:提取 Token(线程组 1)

在登录请求后添加 JSON 提取器,将 Token 存入局部变量:

变量名:token
JSONPath:$.data.token

第二步:写入全局属性(JSR223 PostProcessor,Groovy)

def token = vars.get('token')
props.put('globalToken', token)
log.info("Token saved to props: " + token)

第三步:其他线程组读取 Token

在 HTTP 请求的 Header Manager 中添加:

Authorization: Bearer ${__P(globalToken,)}

或在 JSR223 脚本中读取:

def token = props.get('globalToken')

3. 使用 __setProperty 函数(无需脚本)

也可以直接在 BeanShell/JSR223 后置处理器中用函数写入:

${__setProperty(globalToken,${token},)}

读取时同样使用:

${__P(globalToken,)}

十三、事务控制器

核心结论

事务控制器(Transaction Controller)将多个请求合并为一个逻辑事务,在聚合报告中生成该事务的整体响应时间和 TPS 指标。适合评估一个完整用户操作(如"登录→查询→下单")的端到端性能。


1. 使用场景

场景 是否需要事务控制器
评估页面整体加载时间 需要,将页面所有请求包裹为一个事务
独立分析每个接口性能 不需要,保持请求独立
逻辑分组,便于报告阅读 需要
并行请求的精确耗时统计 不适合(事务时间是子请求累计时间,非并行最大值)

2. 配置说明

配置路径: 右键线程组 → Add → Logic Controller → Transaction Controller

配置项 说明
Generate parent sample 勾选后在报告中生成父事务样本,汇总子请求总耗时
Include duration of timer 是否将定时器时间计入事务耗时

注意: 事务控制器的总耗时是所有子请求的累计时间。若请求实际是并行发送的,事务时间会大于实际用户感知时间,需结合 Response Times Over Time 监听器分析。


3. 并行请求实现

JMeter 默认按顺序执行请求,模拟并行需要额外配置:

Test Plan
└─ Thread Group
   └─ Transaction Controller(Generate parent sample: true)
      └─ Parallel Controller(需安装插件)
         ├─ HTTP Request 1
         ├─ HTTP Request 2
         └─ HTTP Request 3

Parallel Controller 插件安装: JMeter 插件管理器 → 搜索 “Parallel Controller” → 安装。


十四、JDBC 数据库测试

核心结论

JMeter 通过 JDBC Request 采样器直接操作数据库,适合验证接口写入结果、准备测试数据、或对数据库本身进行性能测试。使用前必须先配置 JDBC Connection Configuration 并将对应数据库驱动放入 JMeter 的 lib 目录。


1. 配置步骤

第一步:添加驱动

将数据库 JDBC 驱动 jar 包(如 mysql-connector-java-8.x.jar)放入 JMeter 的 lib/ 目录,重启 JMeter。

第二步:配置连接池

右键线程组 → Add → Config Element → JDBC Connection Configuration

配置项 示例值
Variable Name dbPool(后续 JDBC Request 引用此名)
Database URL jdbc:mysql://localhost:3306/testdb
JDBC Driver class com.mysql.cj.jdbc.Driver
Username root
Password your_password

第三步:添加 JDBC Request

右键线程组 → Add → Sampler → JDBC Request

配置项 说明
Variable Name 填写连接池名称,如 dbPool
Query Type Select Statement / Update Statement / Callable Statement
SQL Query 要执行的 SQL 语句
Variable names 查询结果存储的变量名(逗号分隔)

查询示例:

SELECT user_id, username FROM users WHERE status = 1 LIMIT 10

将结果存入变量(Variable names 填 userId,userName),后续用 ${userId_1}${userName_1} 引用第一行数据。


十五、性能指标与吞吐量

核心结论

吞吐量(Throughput)是性能测试最核心的指标,反映系统单位时间内处理请求的能力。JMeter 聚合报告中的 Throughput 单位为 requests/second,计算公式为:总请求数 ÷ 总测试时间。


1. 核心性能指标

指标 说明 参考标准
Throughput(吞吐量) 每秒处理的请求数(TPS/RPS) 越高越好
Average(平均响应时间) 所有请求的平均耗时(ms) 越低越好
90% Line 90% 的请求在此时间内完成 重要参考值
99% Line 99% 的请求在此时间内完成 长尾延迟指标
Error%(错误率) 失败请求占总请求的比例 正常应 < 1%
Min / Max 最快/最慢响应时间 关注 Max 异常值

2. 吞吐量计算

吞吐量计算公式:

Throughput = Total Samples / Total Time (seconds)

例如:10 分钟内发送了 60000 个请求:

Throughput = 60000 / 600 = 100 requests/second

影响吞吐量的因素:

  • 服务器处理能力(CPU、内存、线程池)
  • 数据库查询效率
  • 网络带宽
  • JMeter 压测机本身性能

3. 聚合报告字段说明

聚合报告(Aggregate Report)是最常用的结果分析监听器。

字段 说明
Label 采样器名称或事务名称
# Samples 总请求数
Average 平均响应时间(ms)
Median 中位数响应时间(ms)
90% Line 90 百分位响应时间
95% Line 95 百分位响应时间
99% Line 99 百分位响应时间
Min 最小响应时间
Max 最大响应时间
Error% 错误率
Throughput 吞吐量(requests/sec)
Received KB/sec 每秒接收数据量

十六、服务器资源监控

核心结论

JMeter 本身只能监控接口响应指标,要同时监控服务器的 CPU、内存、磁盘 IO 等资源,需要安装 PerfMon 插件并在被测服务器上运行 ServerAgent。这是定位性能瓶颈(如内存泄漏、CPU 飙高)的必要手段。


1. PerfMon 插件配置

安装步骤:

  1. 下载 JMeter Plugins Manager,放入 lib/ext/ 目录
  2. 重启 JMeter → Options → Plugins Manager
  3. 搜索 “PerfMon” → 安装 → 重启

被测服务器配置:

  1. 下载 ServerAgent(ServerAgent-x.x.zip
  2. 上传到被测服务器,解压后执行:
# Linux
./startAgent.sh

# Windows
startAgent.bat

默认监听端口 4444。

JMeter 端配置:

右键线程组 → Add → Listener → jp@gc - PerfMon Metrics Collector

配置项 说明
Host/IP 被测服务器 IP
Port ServerAgent 端口,默认 4444
Metric to collect CPU / Memory / Disk I/O / Network I/O

2. 内存泄漏测试方法

内存泄漏的特征:随着测试时间增加,内存持续上涨,不会回落。

测试步骤:

  1. 配置 PerfMon 监控服务器内存(Memory → Used)
  2. 设置较长的测试时间(如 30 分钟持续压测)
  3. 观察内存曲线:
    • 正常:内存在一定范围内波动,GC 后回落
    • 泄漏:内存持续单调递增,不回落

结合 JVM 监控:

# 每秒输出 GC 情况
jstat -gcutil <PID> 1000

# 导出堆转储分析
jmap -dump:format=b,file=heap.hprof <PID>

3. 压测中 CPU/内存高的常见原因

压测机(JMeter 本身)CPU 高:

原因 解决方案
使用 GUI 模式运行 改用命令行模式:jmeter -n -t test.jmx -l result.jtl
启用了大量监听器 正式压测时禁用"查看结果树"等图形监听器
使用 BeanShell 脚本 改用 JSR223 + Groovy
JVM 堆内存不足,频繁 GC 调整启动参数:-Xms1g -Xmx4g

被测服务器 CPU 高但并发量不大:

原因 排查方法
单请求触发复杂业务逻辑 用 Arthas/JProfiler 分析热点代码
数据库慢查询(全表扫描) 开启慢查询日志,用 EXPLAIN 分析执行计划
线程池/连接池耗尽,大量等待 监控线程池活跃数和等待队列
缓存命中率低,请求穿透到 DB 监控 Redis 命中率
JVM 频繁 Full GC 分析 GC 日志,检查内存泄漏

4. 高并发下错误率上升但 CPU/内存正常的原因

这种情况通常是资源竞争或配置限制,而非计算瓶颈。

可能原因 说明
线程池耗尽 Tomcat maxThreads 不足,请求排队超时
数据库连接池耗尽 HikariCP 最大连接数过小
锁竞争 synchronized 块或数据库行锁导致线程阻塞
下游服务限流 第三方 API 返回 429 Too Many Requests
TCP 连接数限制 系统 net.core.somaxconn 参数过低
网关限流 Nginx/Spring Cloud Gateway 配置了限流规则

排查步骤:

# 查看 Java 线程堆栈,找阻塞点
jstack <PID> > threads.txt

# 查看数据库锁等待(MySQL)
SHOW ENGINE INNODB STATUS;

# 查看系统连接数
ss -s

十七、缓存测试

核心结论

JMeter 可以通过 JSR223 Sampler 集成 Jedis 客户端直接操作 Redis,用于测试缓存雪崩、缓存穿透等场景。测试时需结合 Redis 监控(redis-cli monitor)和数据库监控,观察缓存失效时数据库的压力变化。


1. 缓存雪崩测试

缓存雪崩:大量缓存同时失效,请求全部打到数据库。

测试步骤:

  1. 预热 Redis,填充测试数据
  2. 使用 JMeter 批量删除缓存,模拟同时失效
  3. 立即发送大量并发请求
  4. 监控数据库 QPS 和响应时间变化

JSR223 Sampler(Groovy)模拟缓存雪崩场景:

import redis.clients.jedis.Jedis

def jedis = new Jedis("localhost", 6379)
def key = "test_key_" + vars.get("__threadNum")
def value = jedis.get(key)

if (value == null) {
    // 缓存未命中,模拟数据库查询
    sleep(100)
    jedis.set(key, "value_from_db", "NX", "EX", 60)
}

jedis.close()
return "Success"

2. 缓存穿透测试

缓存穿透:查询不存在的 key,每次都穿透到数据库。

测试步骤:

  1. 准备大量不存在于 Redis 中的 key 列表(CSV 文件)
  2. 使用 CSV Data Set Config 加载这些 key
  3. 并发请求,观察数据库查询量

JSR223 Sampler(Groovy)模拟缓存穿透:

import redis.clients.jedis.Jedis

def jedis = new Jedis("localhost", 6379)
// 生成随机不存在的 key
def randomKey = "non_existent_" + System.currentTimeMillis() + "_" + (Math.random() * 10000).toInteger()
def value = jedis.get(randomKey)

if (value == null) {
    // 穿透到数据库,不写入缓存
    sleep(100)
}

jedis.close()
return "Requested: " + randomKey

3. 关键监控指标

指标 监控工具
Redis 命中率 redis-cli info statskeyspace_hits/keyspace_misses
数据库 QPS MySQL SHOW STATUS LIKE 'Questions'
数据库连接数 SHOW STATUS LIKE 'Threads_connected'
服务器 CPU/内存 PerfMon + ServerAgent

十八、HTML 报告解读

核心结论

jmeter -e -o 生成的 HTML 报告比聚合报告信息更丰富,包含随时间变化的趋势图。聚合报告只能看平均值,HTML 报告能看出性能在测试过程中是否稳定、何时开始劣化,是压测结果分析的首选输出形式。


1. 生成 HTML 报告

# 测试结束后直接生成
jmeter -n -t test.jmx -l result.jtl -e -o ./report

# 已有 jtl 文件时单独生成报告
jmeter -g result.jtl -o ./report

注意: -o 指定的目录必须为空或不存在,否则报告生成失败。


2. 核心图表说明

Dashboard(仪表板)

报告首页,汇总全局指标:

指标 说明
APDEX 应用性能指数(0~1),越接近 1 越好;基于 Toleration(容忍阈值)和 Frustration(挫败阈值)计算
Requests Summary 请求成功率饼图
Statistics 与聚合报告类似,含各接口的完整统计数据

Response Times Over Time

横轴为时间,纵轴为平均响应时间。用于判断:

  • 响应时间是否在测试过程中持续上升(系统在压力下劣化)
  • 是否有明显的响应时间突刺(某时刻出现性能抖动)

Hits per Second

每秒实际到达服务器的请求数(吞吐量趋势)。与线程数对比:

  • 线程数在增加但 Hits per Second 不增加 → 系统已达瓶颈
  • Hits 曲线出现下降 → 服务器开始拒绝请求或响应变慢导致吞吐下降

Response Time Percentiles Over Time

展示 50th、75th、95th、99th 百分位响应时间的时间趋势。比 Average 更能反映长尾问题:

  • 99th 远高于 95th → 存在少量极慢请求,排查是否有锁等待或 GC 停顿

Active Threads Over Time

实际活跃线程数随时间的变化。结合 Ramp-Up 配置验证线程是否按预期启动和运行。


Response Time Distribution

响应时间的频率分布直方图。理想情况下应呈正态分布;若出现双峰(两个集中区间)说明存在两类性能表现差异大的请求,需分开分析。


3. APDEX 阈值配置

APDEX 基于两个阈值计算满意度分数:

区间 用户体验 计分权重
≤ Toleration(容忍值) 满意 1.0
Toleration ~ Frustration(挫败值) 可接受 0.5
> Frustration 不满意 0

默认阈值:Toleration = 500ms,Frustration = 1500ms。可在 jmeter.properties 中修改:

jmeter.reportgenerator.apdex_satisfied_threshold=500
jmeter.reportgenerator.apdex_tolerated_threshold=1500

十九、常见错误排查

核心结论

JMeter 脚本调试阶段遇到的错误 90% 集中在连接问题、证书问题、编码问题、路径问题和变量提取失败这五类。遇到错误时优先开启"查看结果树",查看请求的实际发送内容和响应内容,而不是猜测。


1. 常见错误速查表

错误信息 原因 解决方案
Connection refused 服务未启动或端口不对 确认服务状态和端口;检查防火墙规则
Non HTTP response code: java.net.SocketTimeoutException 连接超时 调大 HTTP Request 的 Connect Timeout / Response Timeout
Non HTTP response code: javax.net.ssl.SSLException SSL 握手失败 安装 JMeter 根证书;或在 HTTP Request 中勾选"Use KeepAlive"并禁用证书验证
Response code: 415 Content-Type 未设置 在 Header Manager 中添加 Content-Type: application/json
Response code: 401 Token 未传或已失效 检查 JSON 提取器是否成功提取到 Token;检查 Header Manager 引用变量名是否正确
Response code: 400 请求体格式错误 在查看结果树中查看实际发送的请求体内容
响应乱码(中文变问号) 编码不一致 HTTP Request 的 Content encoding 填 UTF-8;HTTP Request Defaults 同步设置
变量取值为 ${varName}(未替换) 变量未被提取到 检查提取器 JSONPath/正则是否正确;查看结果树中的响应体确认格式

2. Connection refused 排查步骤

# 确认服务是否监听目标端口
telnet <host> <port>
curl -v http://<host>:<port>/health

# 确认防火墙是否放行
# Linux
iptables -L | grep <port>
# macOS
sudo lsof -i :<port>

3. SSL 证书问题处理

方案一:安装 JMeter 根证书(推荐录制场景)

见"脚本录制与 HTTPS 配置"章节。

方案二:跳过证书验证(适合内网测试环境)

jmeter.properties 中添加:

https.use.cached.ssl.context=false

或在 HTTP Request Sampler 中使用自定义 SSL 上下文(JSR223 PreProcessor):

import org.apache.http.conn.ssl.NoopHostnameVerifier
import org.apache.http.ssl.SSLContextBuilder

def sslContext = new SSLContextBuilder().loadTrustMaterial(null, { chain, authType -> true }).build()
ctx.getCurrentSampler().setProperty("https.socketFactory", sslContext.socketFactory)

4. 变量提取失败排查

变量提取失败时,${varName} 会被替换为默认值(未设置默认值则为 ${varName} 原文)。

排查步骤:

  1. 在查看结果树中查看响应体,确认响应格式是否符合预期
  2. 检查 JSONPath 表达式是否正确(可用在线工具如 jsonpath.com 验证)
  3. 检查 Match No. 设置:提取多个值用 -1,提取第一个用 1
  4. 在提取器后添加 Debug PostProcessor,查看当前所有变量值:

配置路径: 右键 HTTP 请求 → Add → Post Processors → Debug PostProcessor

Debug PostProcessor 会在查看结果树中输出所有 vars 变量,方便确认提取结果。


5. CSV 文件路径问题

问题 原因 解决方案
java.io.FileNotFoundException 路径不对或文件不存在 使用相对路径(相对于 .jmx 文件所在目录)
所有线程读取同一行数据 Sharing Mode 设置为 Current thread 改为 All threads
数据读完后报错停止 Stop thread on EOF = True 改为 False,配合 Recycle on EOF = True
中文乱码 CSV 文件编码问题 将文件保存为 UTF-8 无 BOM 格式

CSV 文件路径推荐写法(相对路径):

${__P(user.dir,)}/testdata/users.csv

或直接写相对于脚本的路径:

testdata/users.csv

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐