JMeter从入门到精通

JMeter 从入门到精通

适合人群:测试工程师、后端开发工程师、DevOps 工程师
本文从 JMeter 基础概念讲起,覆盖脚本编写、参数化、关联、分布式压测、CI/CD 集成等进阶内容,带你从零掌握性能测试利器。

目录


1. JMeter 简介

1.1 什么是 JMeter

Apache JMeter 是 Apache 组织开发的开源性能测试工具,最初用于 Web 应用程序的负载测试,如今已扩展支持多种协议和应用程序类型。

1.2 JMeter 的核心特点

  • 开源免费:基于 Java 开发,100% 免费开源
  • 跨平台:支持 Windows、Linux、macOS
  • 多协议支持:HTTP/HTTPS、FTP、JDBC、JMS、SMTP、TCP 等
  • GUI + 非 GUI 双模式:GUI 用于脚本编写调试,非 GUI 用于大规模压测
  • 可视化测试报告:自带丰富的图表和报表
  • 可扩展:支持自定义插件、BeanShell/Groovy 脚本
  • 分布式测试:支持多机协同压测,轻松模拟海量并发

1.3 JMeter 能做什么

测试类型说明
负载测试模拟预期用户量,验证系统在正常/峰值负载下的表现
压力测试不断增加负载,找出系统的崩溃点
稳定性测试长时间运行,验证系统是否存在内存泄漏等问题
性能基准测试建立性能基线,用于版本对比
接口功能测试验证接口的正确性

1.4 JMeter vs 其他性能测试工具

特性JMeterLoadRunnerGatlingLocust
开源❌(商业)
GUI❌(DSL)❌(Python)
分布式
协议支持丰富非常丰富HTTP 为主HTTP 为主
学习曲线中等较陡较陡
报告良好优秀优秀一般

2. 环境搭建

2.1 前置条件

JMeter 基于 Java 开发,需要先安装 JDK。

# 检查 JDK 是否已安装
java -version

# JMeter 5.6+ 推荐 JDK 11 或 JDK 17
# 如果未安装,Mac 可使用 Homebrew
brew install openjdk@17

# 配置 JAVA_HOME(Mac)
echo 'export JAVA_HOME=$(/usr/libexec/java_home -v 17)' >> ~/.zshrc
echo 'export PATH=$JAVA_HOME/bin:$PATH' >> ~/.zshrc
source ~/.zshrc

# 验证
javac -version

2.2 下载安装 JMeter

# 方式一:官网下载
# 访问 https://jmeter.apache.org/download_jmeter.cgi 下载最新版本

# 方式二:命令行下载(以 5.6.3 为例)
cd /usr/local
wget https://archive.apache.org/dist/jmeter/binaries/apache-jmeter-5.6.3.tgz
tar -xzf apache-jmeter-5.6.3.tgz

# 配置环境变量
echo 'export JMETER_HOME=/usr/local/apache-jmeter-5.6.3' >> ~/.zshrc
echo 'export PATH=$JMETER_HOME/bin:$PATH' >> ~/.zshrc
source ~/.zshrc

# 验证安装
jmeter --version

2.3 Docker 安装(可选)

# 使用官方镜像快速启动 GUI 模式
docker run -d \
  --name jmeter \
  -p 5900:5900 \
  -v /Users/wanghao/Desktop/study/jmeter-scripts:/jmeter \
  justb4/jmeter:latest

2.4 启动 JMeter

# GUI 模式(用于脚本编写和调试)
jmeter

# 非 GUI 模式(用于执行压测,推荐)
jmeter -n -t test_plan.jmx -l result.jtl -e -o report/

# Mac 上也可以直接双击 JMETER_HOME/bin/jmeter.sh

⚠️ 重要提示:GUI 模式消耗资源大,仅用于编写和调试脚本。正式执行压测务必使用非 GUI 模式,否则 GUI 本身会成为瓶颈,导致测试结果不准确。


3. JMeter 核心概念与组件体系

3.1 JMeter 的组件分类

JMeter 的测试脚本由若干"元件(Element)"组成,按功能分为以下几大类:

┌─────────────────────────────────────────────────────────┐
│                    测试计划 (Test Plan)                    │
│   线程组 (Thread Group)                                    │
│   ├── 采样器 (Sampler)         → 发送请求                  │
│   ├── 逻辑控制器 (Logic Controller) → 控制执行流程          │
│   ├── 配置元件 (Config Element) → 提供默认值、变量          │
│   ├── 前置处理器 (Pre-Processor) → 请求前处理               │
│   ├── 后置处理器 (Post-Processor) → 请求后处理(提取数据)    │
│   ├── 断言 (Assertion)        → 验证响应结果               │
│   ├── 定时器 (Timer)          → 控制请求间隔               │
│   └── 监听器 (Listener)       → 收集展示结果               │
└─────────────────────────────────────────────────────────┘

3.2 元件的执行顺序

JMeter 中同一作用域下元件的执行顺序是固定的(无论在树中的排列顺序如何):

1. 配置元件 (Config Element)
2. 前置处理器 (Pre-Processor)
3. 定时器 (Timer)
4. 采样器 (Sampler)
5. 后置处理器 (Post-Processor)
6. 断言 (Assertion)
7. 监听器 (Listener)

💡 记忆技巧:配置 → 前置 → 定时 → 采样 → 后置 → 断言 → 监听。理解执行顺序对编写复杂脚本至关重要。

3.3 作用域规则

元件的作用域由它在测试树中的层级位置决定:

  • 采样器:不与任何其他元件通信,它本身就是请求
  • 逻辑控制器:影响其子节点的执行
  • 其他元件(配置、断言、定时器、前后置处理器、监听器):作用于其同级或父级的采样器
Thread Group
├── HTTP Request A          ← Sampler
├── Response Assertion      ← 作用于 A(同级)
├── View Results Tree       ← 作用于 A 和 B(同级所有采样器)
├── HTTP Request B          ← Sampler
│   ├── JSON Extractor      ← 仅作用于 B(B 的子节点,父级是 B)
│   └── Constant Timer      ← 仅作用于 B
└── HTTP Request C          ← Sampler

3.4 变量与属性

概念作用范围线程间共享设置方式
变量 (Variables)当前线程❌ 不共享${varName}
属性 (Properties)全局✅ 共享${__P(propName)}${__property(propName)}
# 命令行传参使用属性
jmeter -n -t test.jmx -JthreadNum=100 -JrampUp=10

在脚本中引用:

线程数: ${__P(threadNum)}
Ramp-Up: ${__P(rampUp)}

4. 第一个测试计划

4.1 创建测试计划

  1. 启动 JMeter GUI
  2. 默认有一个 Test Plan 节点,重命名为 MyFirstTest
  3. 右键 Test Plan → Add → Threads (Users) → Thread Group

4.2 配置线程组

线程组是 JMeter 模拟虚拟用户的核心:

参数说明示例
Number of Threads虚拟用户数100
Ramp-Up Period (seconds)启动所有线程所需时间10(每秒启动10个用户)
Loop Count每个线程循环次数10(0表示无限)
Same user on each iteration每次迭代是否复用同一用户默认勾选

💡 Ramp-Up 理解:如果线程数=100,Ramp-Up=10秒,那么每秒会启动 10 个线程,10秒后全部启动完毕。这模拟了用户逐渐上线的真实场景。

4.3 添加 HTTP 请求

右键 Thread Group → Add → Sampler → HTTP Request:

字段
Name访问首页
Protocolhttp
Server Namewww.example.com
Path/

4.4 添加结果监听器

右键 Thread Group → Add → Listener → View Results Tree

4.5 运行测试

点击工具栏绿色 ▶ 按钮运行,在 View Results Tree 中查看结果。

4.6 测试计划 XML 结构

保存后的 .jmx 文件本质是 XML,核心结构如下:

<?xml version="1.0" encoding="UTF-8"?>
<jmeterTestPlan version="1.2" properties="5.0">
  <hashTree>
    <TestPlan guiclass="TestPlanGui" testname="MyFirstTest">
      <stringProp name="TestPlan.comments">第一个测试计划</stringProp>
      <boolProp name="TestPlan.functional_mode">false</boolProp>
    </TestPlan>
    <hashTree>
      <ThreadGroup guiclass="ThreadGroupGui" testname="用户组">
        <intProp name="ThreadGroup.num_threads">100</intProp>
        <stringProp name="ThreadGroup.ramp_time">10</stringProp>
        <intProp name="LoopController.loops">10</intProp>
      </ThreadGroup>
      <hashTree>
        <HTTPSamplerProxy guiclass="HttpTestSampleGui" testname="访问首页">
          <stringProp name="HTTPSampler.domain">www.example.com</stringProp>
          <stringProp name="HTTPSampler.path">/</stringProp>
        </HTTPSamplerProxy>
        <hashTree/>
      </hashTree>
    </hashTree>
  </hashTree>
</jmeterTestPlan>

5. 常用采样器详解

5.1 HTTP Request 采样器

最常用的采样器,用于发送 HTTP/HTTPS 请求。

核心配置项:

Protocol: http / https
Server Name or IP: api.example.com
Port Number: 8080
Method: GET / POST / PUT / DELETE / PATCH
Path: /api/v1/users
Content Encoding: UTF-8

POST 请求传参方式:

  1. Parameters 方式(form-data / x-www-form-urlencoded)
name=admin&age=25
  1. Body Data 方式(JSON)
{
    "username": "admin",
    "password": "123456",
    "email": "admin@example.com"
}

上传文件:

在 HTTP Request 中勾选 Use multipart/form-data,在 Files Upload 标签页添加:

File Path: /path/to/test.png
Parameter Name: file
MIME Type: image/png

Advanced 配置(重要):

参数说明
Redirect Automatically自动跟随重定向(不记录重定向请求)
Follow Redirects跟随重定向(会记录重定向请求,推荐)
Use KeepAlive使用持久连接
Source Address指定请求的源 IP(多网卡场景)
Timeout (ms)连接/读取超时时间

5.2 JDBC Request 采样器

用于直接对数据库执行 SQL,常用于测试数据库性能或准备测试数据。

前置准备——配置 JDBC Connection Configuration:

参数
Variable Name Bound to Poolmysql_conn
Database URLjdbc:mysql://localhost:3306/testdb
JDBC Driver classcom.mysql.cj.jdbc.Driver
Usernameroot
Password123456

⚠️ 需要将 MySQL JDBC 驱动 jar 包放入 $JMETER_HOME/lib/ 目录。

JDBC Request 配置:

Variable Name: mysql_conn
Query Type: Select Statement
Query:
  SELECT id, username, email FROM users WHERE id = ${user_id}

参数化查询(Parameter Values):

Query Type: Prepared Select Statement
Query: SELECT * FROM users WHERE age > ? AND status = ?
Parameter values: 18, active
Parameter types: INTEGER, VARCHAR

5.3 Debug Sampler

不发送实际请求,用于输出变量和属性的值,调试时非常有用。

JMeter Properties: False
JMeter Variables: True
System Properties: False

💡 配合 View Results Tree 的 Response Data 可以查看所有变量的当前值,是调试关联变量时的利器。

5.4 其他常用采样器一览

采样器用途
FTP Request测试 FTP 服务器
SMTP Sampler发送邮件测试
TCP Sampler测试 TCP 协议
JMS Publisher/Subscriber测试消息队列
BeanShell/Groovy Script执行自定义脚本
OS Process Sampler执行操作系统命令
JSR223 Sampler支持多种脚本语言的高级采样器

6. 配置元件

6.1 HTTP Request Defaults

为同作用域下所有 HTTP 采样器设置默认值,避免重复配置。

Protocol: https
Server Name: api.example.com
Port: 443
Content Encoding: UTF-8

当多个请求都访问同一服务器时,统一配置请求默认值,后续采样器只需填写 Path 即可。

6.2 HTTP Header Manager

管理请求头信息:

NameValue
Content-Typeapplication/json
AuthorizationBearer ${token}
Accept-Languagezh-CN
User-AgentMozilla/5.0

6.3 HTTP Cookie Manager

自动管理 Cookie,模拟浏览器行为:

  • Clear cookies each iteration:每次迭代清除 Cookie(模拟新用户登录)
  • Store HTTP cookies:存储并自动携带 Cookie

💡 测试需要登录的接口时,Cookie Manager 可以自动保存登录后的 Session Cookie,后续请求自动携带,无需手动处理。

6.4 User Defined Variables

定义全局变量:

NameValue
HOSTapi.example.com
PORT443
PROTOCOLhttps
USERNAMEtestuser

引用方式:${HOST}

6.5 CSV Data Set Config

从 CSV 文件读取参数化数据,是最常用的参数化方式(详见 第12章 参数化)。


7. 前置处理器与后置处理器

7.1 前置处理器(Pre-Processor)

在采样器发送请求之前执行。

7.1.1 User Parameters

为每个线程/每次迭代设置不同参数:

NameThread 1Thread 2Thread 3
usernameuser1user2user3
passwordpass1pass2pass3
7.1.2 JSR223 PreProcessor(推荐)

使用 Groovy 在请求前执行逻辑:

// 生成随机时间戳作为请求参数
def timestamp = System.currentTimeMillis()
vars.put("timestamp", timestamp.toString())

// 生成 MD5 签名
def sign = "key=" + vars.get("apiKey") + "&timestamp=" + timestamp
def md5 = java.security.MessageDigest.getInstance("MD5")
def bytes = md5.digest(sign.getBytes("UTF-8"))
def hexString = new BigInteger(1, bytes).toString(16).padLeft(32, '0')
vars.put("sign", hexString)

log.info("Generated sign: " + hexString)

💡 为什么用 Groovy 而不是 BeanShell? Groovy 性能更好(编译缓存),语法兼容 Java,且是 JMeter 官方推荐脚本语言。请始终在 JSR223 元件中勾选 Cache compiled script

7.2 后置处理器(Post-Processor)

在采样器收到响应之后执行,主要用于提取数据。

7.2.1 Regular Expression Extractor(正则提取)
字段
Reference Nametoken
Regular Expression“token”:“(.+?)”
Template 1 1 1
Match No.1
Default ValueNOT_FOUND

执行后可通过 ${token} 引用提取的值。

正则表达式说明:

  • ():捕获组,$1$ 表示取第一个捕获组
  • .+?:非贪婪匹配,匹配任意字符至少一次
  • Match No.:0=随机,1=第一个匹配,-1=所有匹配(生成数组 token_1, token_2…)
7.2.2 JSON Extractor

针对 JSON 响应,使用 JSONPath 提取:

{
    "code": 200,
    "data": {
        "userId": 10086,
        "token": "abc123xyz",
        "roles": ["admin", "user"]
    }
}
字段
Name of created variableuserId
JSON Path$.data.userId
Match No.1
Default ValueNOT_FOUND

常用 JSONPath 语法:

表达式说明示例
$.key根对象的属性$.code → 200
$.a.b嵌套属性$.data.token → abc123xyz
$.list[0]数组第一个元素$.data.roles[0] → admin
$.list[-1]数组最后一个元素$.data.roles[-1] → user
$.list[0:2]数组切片前两个元素
$.list[?(@.age>18)]条件过滤过滤 age>18 的对象
$..key递归搜索所有层级找到所有 token
7.2.3 Boundary Extractor(边界提取器)

比正则更简单高效的提取方式,适合提取固定边界的内容:

字段
Reference NamesessionId
Left BoundarysessionId="
Right Boundary"
Match No.1
Default ValueNOT_FOUND
7.2.4 JSR223 PostProcessor

最灵活的后置处理方式:

import groovy.json.JsonSlurper

// 解析响应
def jsonSlurper = new JsonSlurper()
def response = jsonSlurper.parseText(prev.getResponseDataAsString())

// 提取并设置变量
if (response.code == 200) {
    vars.put("userId", response.data.userId.toString())
    vars.put("token", response.data.token)
    log.info("Login success, userId: ${response.data.userId}")
} else {
    log.error("Login failed: ${response.message}")
    prev.setSuccessful(false)  // 标记请求失败
}

💡 prev 是内置变量,代表当前 SampleResult 对象,可以获取响应数据、响应码、响应时间等信息。


8. 断言

断言用于验证响应是否符合预期,如果断言失败,该采样器会被标记为失败。

8.1 Response Assertion(响应断言)

字段配置
Apply toMain sample only
Field to TestResponse Code / Response Body
Pattern Matching RulesContains / Equals / Matches
Patterns to Test200

常见用例:

验证目标Field to TestRulePattern
HTTP状态码Response CodeEquals200
响应包含关键词Text ResponseContainssuccess
响应为指定JSONText ResponseEquals{“code”:200}
正则匹配Text ResponseMatches.“status”:“ok”.

8.2 JSON Assertion

专门针对 JSON 响应的断言:

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

8.3 Duration Assertion

验证响应时间是否在预期范围内:

Duration in milliseconds: 2000  (超过2秒则断言失败)

8.4 Size Assertion

验证响应体大小:

Size in bytes: 1024
Compare: > (大于1024字节)

8.5 JSR223 Assertion

最灵活的断言,可编写复杂逻辑:

import groovy.json.JsonSlurper

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

// 复杂断言逻辑
if (response.code != 200) {
    AssertionResult.setFailure(true)
    AssertionResult.setFailureMessage("期望code=200,实际code=${response.code}")
} else if (response.data.list.size() == 0) {
    AssertionResult.setFailure(true)
    AssertionResult.setFailureMessage("返回列表为空")
} else if (response.data.list[0].price < 0) {
    AssertionResult.setFailure(true)
    AssertionResult.setFailureMessage("商品价格不能为负数")
}

8.6 断言结果查看

添加 Assertion Results 监听器,可以查看哪些请求断言失败及失败原因。


9. 定时器

定时器控制请求之间的间隔,模拟真实用户的思考时间。

⚠️ 定时器作用于其作用域内的所有采样器,且在采样器之前执行。如果只想对某个采样器生效,需将其作为该采样器的子节点。

9.1 Constant Timer(固定定时器)

Thread Delay (ms): 3000  (每个请求间隔3秒)

9.2 Uniform Random Timer(均匀随机定时器)

Random Delay Maximum (ms): 2000
Constant Delay Offset (ms): 1000

实际延迟 = 1000 + Random(0, 2000) = 1000~3000ms

💡 这是最常用的定时器,模拟真实用户操作间的随机停顿。

9.3 Gaussian Random Timer(高斯随机定时器)

Deviation (ms): 1000
Constant Delay Offset (ms): 2000

延迟符合高斯分布,更接近真实用户行为。

9.4 Constant Throughput Timer(常数吞吐量定时器)

控制吞吐量而非延迟:

Target throughput: 60.0 (每分钟60次)
Calculate throughput based on: all active threads in current thread group

💡 用于精确控制 TPS(每秒事务数)。注意这是"尽力达到"目标吞吐量,实际取决于服务器响应速度。

9.5 Precise Throughput Timer(精确吞吐量定时器)

JMeter 5.x 引入,比 Constant Throughput Timer 更精确:

Target throughput: 60.0 (每分钟60次)
Throughput period: 1 (分钟)

10. 逻辑控制器

逻辑控制器控制采样器的执行流程

10.1 Simple Controller

最简单的控制器,仅用于分组组织,不改变执行逻辑。

10.2 Loop Controller(循环控制器)

Loop Count: 5  (循环执行子节点5次)
Thread Group
├── Login (HTTP)
├── Loop Controller (5次)
│   ├── Search (HTTP)
│   └── View Detail (HTTP)
└── Logout (HTTP)

在线程组中勾选 Same user on each iteration,如果取消勾选则每次迭代会重置线程上下文(模拟不同用户)。

10.3 If Controller(条件控制器)

根据条件决定是否执行子节点:

Condition (default javascript): "${loginStatus}" == "success"

⚠️ 默认使用 JavaScript 解释条件,推荐取消勾选 Interpret Condition as Variable Expression,然后使用 Groovy 语法:

Condition: ${__groovy(vars.get('loginStatus') == 'success',)}

10.4 While Controller(循环控制器)

满足条件时持续循环:

Condition: ${__groovy(vars.get('hasMore') != 'false' && vars.get('retryCount').toInteger() < 3,)}

常用于轮询等待某个条件满足。

10.5 ForEach Controller(遍历控制器)

遍历一组变量执行子节点:

Input variable prefix: user
Start index: 0
End index: 5
Output variable name: currentUser

配合正则提取器的 -1(全部匹配)使用,遍历 user_1, user_2user_5

10.6 Switch Controller(切换控制器)

根据索引执行特定子节点:

Switch Value: 2  (执行第3个子节点,从0开始)

如果值为变量,可实现动态路由。

10.7 Interleave Controller(交替控制器)

交替执行子节点:

Thread 1 → 子节点1
Thread 2 → 子节点2
Thread 3 → 子节点3
Thread 4 → 子节点1
...

10.8 Once Only Controller(仅一次控制器)

子节点在每个线程中只执行一次(第一次迭代时):

Thread Group
├── Once Only Controller
│   └── Login (HTTP)    ← 每个线程只登录一次
├── Loop Controller (10次)
│   └── Query Data (HTTP)  ← 查询10次
└── Logout (HTTP)

10.9 Transaction Controller(事务控制器)

将多个采样器组合成一个事务:

Transaction Controller (Generate parent sample: 勾选)
├── Login (HTTP)       ← 200ms
├── Search (HTTP)      ← 300ms
└── View Detail (HTTP) ← 150ms

事务总耗时 = 200 + 300 + 150 = 650ms,在报告中作为单一事务统计。

💡 事务控制器是衡量"用户完整业务流程"性能的关键。一个完整下单流程(登录→浏览→加购→下单→支付)应作为一个事务统计。

10.10 Critical Section Controller(临界区控制器)

控制并发访问,确保同一时刻只有一个线程执行子节点:

Lock Name: orderLock

用于模拟真实场景中的串行化操作(如库存扣减)。


11. 监听器与结果分析

11.1 View Results Tree(查看结果树)

最常用的调试监听器,展示每个请求的详细信息:

  • Sampler results:请求/响应头信息
  • Request:发送的请求内容
  • Response data:响应体(支持 JSON/HTML/正则/JSONPath/XPath 等多种查看器)
  • Assertion result:断言结果

⚠️ 压测时务必禁用或删除 View Results Tree,它会消耗大量内存。仅调试时使用。

11.2 Summary Report(汇总报告)

Label# SamplesAverageMinMaxStd. Dev.Error %ThroughputReceived KB/secSent KB/secAvg. Bytes

各指标含义:

  • Average:平均响应时间(ms)
  • Std. Dev.:标准差,越小越稳定
  • Error %:错误率
  • Throughput:吞吐量(请求/秒)
  • Avg. Bytes:平均响应大小

11.3 Aggregate Report(聚合报告)

类似 Summary Report,但按百分位统计:

Label# SamplesAverageMedian90% Line95% Line99% LineMinMaxError %Throughput
  • Median(50% Line):中位数,50% 的请求响应时间低于此值
  • 90% Line:90% 的请求响应时间低于此值
  • 95% Line:95% 的请求响应时间低于此值
  • 99% Line:99% 的请求响应时间低于此值

💡 关注 P95/P99 而非平均值:平均值会被少量极慢请求拉高或被大量快请求拉低,P95/P99 更能反映大多数用户的真实体验。

11.4 HTML Dashboard Report(HTML 报告)

生成最专业的可视化报告:

jmeter -n -t test_plan.jmx -l result.jtl -e -o ./report/

报告包含:

  • Statistics:统计表格
  • APDEX:应用性能指数
  • Response Times Over Time:响应时间趋势图
  • Throughput Over Time:吞吐量趋势图
  • Active Threads Over Time:活跃线程趋势图
  • Response Time Percentiles:响应时间百分位分布
  • Errors:错误统计
APDEX 详解

APDEX(Application Performance Index)是衡量用户满意度的标准指标:

  • T(满意阈值):默认 500ms,响应时间 ≤ T 为"满意"
  • F(容忍阈值):默认 1500ms,T < 响应时间 ≤ F 为"容忍"
  • 响应时间 > F:为"不满意"
APDEX = (满意数 + 容忍数/2) / 总请求数

分数范围 0~1,越接近 1 用户满意度越高。

11.5 Backend Listener(后端监听器)

将结果实时发送到 InfluxDB / Grafana,实现实时监控:

Backend Listener implementation: org.apache.jmeter.visualizers.backend.influxdb.InfluxdbBackendListenerClient
influxdbMetricsSender: org.apache.jmeter.visualizers.backend.influxdb.HttpMetricsSender
influxdbUrl: http://localhost:8086/write?db=jmeter
measurement: jmeter
summaryOnly: false

配合 Grafana 的 JMeter Dashboard 模板,可实时展示压测数据。


12. 参数化

12.1 为什么需要参数化

如果所有虚拟用户发送相同的请求,缓存等机制会使测试结果失真。参数化让每个用户使用不同的数据,模拟真实场景。

12.2 CSV Data Set Config(最常用)

准备数据文件 users.csv

username,password,email
user1,pass123456,user1@example.com
user2,pass123456,user2@example.com
user3,pass123456,user3@example.com
user4,pass123456,user4@example.com
user5,pass123456,user5@example.com

配置 CSV Data Set Config:

参数说明
Filename/path/to/users.csvCSV 文件路径
File EncodingUTF-8文件编码
Variable Namesusername,password,email变量名(留空则用首行)
Ignore first lineTrue忽略首行(表头)
Delimiter,分隔符
Allow quoted data?False是否允许引号
Recycle on EOF?True读到文件末尾后是否从头开始
Stop thread on EOF?False读到末尾是否停止线程
Sharing modeAll threads共享模式

Sharing mode 说明:

模式说明适用场景
All threads所有线程共享,每个线程拿不同数据每个用户用不同账号
Current thread group当前线程组内共享多线程组各自独立
Current thread每个线程独立读取(每个线程都从第一行开始)每个线程遍历所有数据
Identifier按线程名分配特殊场景

在 HTTP Request 中引用:

POST /api/login
Body:
{
    "username": "${username}",
    "password": "${password}",
    "email": "${email}"
}

12.3 User Parameters

手动指定参数值,适合数据量少的场景:

Name: username
User 1: admin
User 2: testuser
User 3: guest

12.4 函数助手参数化

JMeter 内置丰富的函数,可动态生成数据:

12.4.1 随机数据函数
${__Random(1,100,)}          → 1~100的随机整数
${__RandomString(8,abc123,)}  → 从abc123中随机取8个字符
${__RandomFromMultipleVars(var1|var2|var3,)} → 从多个变量中随机选一个
12.4.2 字符串函数
${__time(yyyy-MM-dd,)}       → 当前日期,如 2025-06-19
${__time(yyyyMMddHHmmss,)}   → 时间戳,如 20250619143000
${__time(,)}                  → 毫秒时间戳
${__UUID()}                   → 生成UUID
${__threadNum}                → 当前线程编号
12.4.3 字符串处理函数
${__split(a,b,c,separator)}  → 分割字符串到变量
${__V(var_${counter})}        → 嵌套变量引用
${__upperCase(abc,)}          → 转大写
${__lowerCase(ABC,)}          → 转小写
12.4.4 计数器函数
${__counter(TRUE,)}           → 线程独立计数器
${__counter(FALSE,)}          → 全局计数器

12.5 使用 __Random 生成唯一数据

// JSR223 中生成唯一手机号
def phone = "138" + String.format("%08d", ${__Random(0,99999999,)})
vars.put("phone", phone)

// 生成随机邮箱
def email = "test_" + System.currentTimeMillis() + "@example.com"
vars.put("email", email)

13. 关联与动态数据处理

13.1 什么是关联

关联是指将前一个请求的响应数据提取出来,作为后一个请求的输入参数。典型场景:

  • 登录获取 token → 后续请求携带 token
  • 查询列表获取商品ID → 查看商品详情
  • 获取验证码 → 提交验证码

13.2 登录 Token 关联实战

Step 1:登录请求

POST /api/login
Body: {"username":"${username}","password":"${password}"}

响应:
{
    "code": 200,
    "data": {
        "token": "eyJhbGciOiJIUzI1NiJ9...",
        "userId": 10086
    }
}

Step 2:添加 JSON Extractor 提取 token

Variable names: token,userId
JSON Path expressions: $.data.token,$.data.userId
Match No.: 1,1
Default values: NOT_FOUND,NOT_FOUND

Step 3:后续请求携带 token

在 HTTP Header Manager 中:

Authorization: Bearer ${token}

或在 HTTP Request 的 Header 中直接引用 ${token}

13.3 跨线程组传递变量

默认情况下变量只在当前线程组内有效。跨线程组传递需要使用 __setProperty__P

线程组 A(登录)——设置属性:

// JSR223 PostProcessor
def token = vars.get("token")
props.put("globalToken", token)
log.info("Set global token: " + token)

线程组 B(业务操作)——获取属性:

# 在 HTTP Header Manager 中引用
Authorization: Bearer ${__P(globalToken,)}

# 或在 JSR223 中获取
def token = props.get("globalToken")
vars.put("token", token)

⚠️ 使用属性传递数据时需注意线程同步问题,确保设置属性的线程组先执行(通过 Test Plan 中的 “Run Thread Groups consecutively” 选项控制)。

13.4 列表数据遍历

场景:查询商品列表,然后逐个查看详情

Step 1:查询列表并提取所有商品 ID

Regular Expression Extractor:
Reference Name: goodsId
Regular Expression: "id":(\d+)
Template: $1$
Match No.: -1  (全部匹配)

此时生成变量:goodsId_1, goodsId_2, … goodsId_N,以及 goodsId_matchNr(总数)

Step 2:ForEach Controller 遍历

Input variable prefix: goodsId
Start index: 1
End index: ${goodsId_matchNr}
Output variable name: currentGoodsId

Step 3:查看详情请求

GET /api/goods/${currentGoodsId}

13.5 动态参数签名(JSR223 实战)

很多接口需要签名验证,使用 JSR223 PreProcessor 生成:

import java.security.MessageDigest

// 获取参数
def apiKey = vars.get("apiKey")
def secret = vars.get("apiSecret")
def timestamp = System.currentTimeMillis().toString()

// 按字典序排序参数
def params = ["apiKey": apiKey, "timestamp": timestamp]
def sortedKeys = params.keySet().sort()
def sb = new StringBuilder()
for (key in sortedKeys) {
    sb.append(key).append("=").append(params[key]).append("&")
}
sb.append("secret=").append(secret)

// MD5 签名
def md5 = MessageDigest.getInstance("MD5")
def bytes = md5.digest(sb.toString().getBytes("UTF-8"))
def hex = new BigInteger(1, bytes).toString(16).padLeft(32, '0')

// 设置变量
vars.put("timestamp", timestamp)
vars.put("sign", hex.toUpperCase())

在 HTTP Request 中引用:${timestamp}${sign}


14. 性能测试实战完整流程

14.1 测试目标定义

以"电商系统商品查询接口"为例:

指标目标
目标 TPS≥ 500
P95 响应时间≤ 200ms
P99 响应时间≤ 500ms
错误率≤ 0.1%
持续时间30 分钟

14.2 测试场景设计

场景一:基准测试

线程数: 1
Ramp-Up: 1秒
循环: 100次
目的: 获取单用户基准性能

场景二:负载测试

线程数: 100
Ramp-Up: 60秒
循环: 永久
持续时间: 30分钟
目的: 验证正常负载下系统表现

场景三:压力测试(阶梯加压)

使用 Stepping Thread Group 或 Ultimate Thread Group 插件:

阶段1: 0-50用户,持续2分钟
阶段2: 50-100用户,持续2分钟
阶段3: 100-200用户,持续2分钟
阶段4: 200-500用户,持续2分钟
阶段5: 持续500用户10分钟
阶段6: 逐步降到0

场景四:稳定性测试

线程数: 200
Ramp-Up: 60秒
循环: 永久
持续时间: 24小时
目的: 检查内存泄漏、连接泄漏

14.3 完整测试脚本结构

商品查询性能测试
├── User Defined Variables (全局变量)
│   ├── HOST=api.example.com
│   ├── PORT=443
│   └── PROTOCOL=https
├── HTTP Cookie Manager
├── HTTP Request Defaults (默认配置)
├── HTTP Header Manager (默认请求头)
├── setUp Thread Group (数据准备)
│   ├── 登录获取Token
│   └── JSON Extractor (提取token)
├── Thread Group (主测试)
│   ├── CSV Data Set Config (参数化数据)
│   ├── HTTP Header Manager (携带token)
│   ├── Once Only Controller
│   │   └── 用户登录 (每个用户登录一次)
│   ├── Transaction Controller (查询商品事务)
│   │   ├── Uniform Random Timer (思考时间)
│   │   ├── 搜索商品 (HTTP Request)
│   │   │   └── Response Assertion (断言)
│   │   └── 查看商品详情 (HTTP Request)
│   │       └── Duration Assertion (响应时间断言)
│   └── Summary Report
└── tearDown Thread Group (数据清理)
    └── 清理测试数据

14.4 执行测试

非 GUI 模式执行(推荐):

# 基本执行
jmeter -n -t product_test.jmx -l result_001.jtl -e -o ./report_001/

# 带参数执行
jmeter -n -t product_test.jmx \
  -Jthreads=100 \
  -JrampUp=60 \
  -Jduration=1800 \
  -l result_001.jtl \
  -e -o ./report_001/

在脚本中使用 ${__P(threads)} 等引用命令行参数,实现脚本参数化。

14.5 结果分析

打开 report_001/index.html,重点关注:

  1. APDEX 分数:是否 > 0.85
  2. Statistics:Error% 是否达标
  3. Response Times Percentiles:P95/P99 是否达标
  4. Response Times Over Time:是否有上升趋势(可能内存泄漏)
  5. Throughput Over Time:TPS 是否稳定
  6. Errors:错误类型和分布

15. 命令行(非 GUI)模式

15.1 为什么用非 GUI 模式

对比项GUI 模式非 GUI 模式
资源消耗高(渲染界面)
结果准确性受 GUI 影响准确
适用场景脚本编写调试正式压测
远程执行不支持支持
CI/CD不适用适用

15.2 常用命令

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

# 生成 HTML 报告
jmeter -n -t test.jmx -l result.jtl -e -o ./report/

# 指定运行时长(秒)
jmeter -n -t test.jmx -l result.jtl -Jduration=600  # 10分钟
# 脚本中配合 ${__P(duration)} 使用

# 传参
jmeter -n -t test.jmx -Jthreads=50 -JrampUp=10 -Jloops=100 -l result.jtl

# 远程执行
jmeter -n -t test.jmx -r -l result.jtl

# 指定远程服务器
jmeter -n -t test.jmx -R 192.168.1.10,192.168.1.11 -l result.jtl

# 使用代理
jmeter -n -t test.jmx -H 192.168.1.1 -P 8888 -l result.jtl

# 指定日志级别
jmeter -n -t test.jmx -l result.jtl -L DEBUG

# 指定 properties 文件
jmeter -n -t test.jmx -q user.properties -l result.jtl

15.3 命令行参数速查

参数说明
-n非 GUI 模式
-t测试计划文件 (.jmx)
-l结果文件 (.jtl)
-e测试结束后生成 HTML 报告
-oHTML 报告输出目录(必须为空或不存在)
-r远程执行所有配置的服务器
-R指定远程服务器列表
-J设置 JMeter 属性
-G设置远程服务器属性
-H代理服务器
-P代理端口
-q额外 properties 文件
-L日志级别
-dJMeter 主目录
-s以服务器模式启动(用于分布式)
-X测试结束后停止远程服务器

15.4 从已有 .jtl 生成报告

# 如果已有 .jtl 文件,可以单独生成 HTML 报告
jmeter -g result.jtl -o ./report/

15.5 后台执行长时测试

# 后台运行并输出日志
nohup jmeter -n -t test.jmx -l result.jtl -e -o ./report/ > jmeter.log 2>&1 &

# 查看进度
tail -f jmeter.log

# 查看进程
ps aux | grep jmeter

16. 分布式压测

16.1 为什么需要分布式

单台 JMeter 受限于 CPU 和内存,能模拟的并发量有限(通常 1000~2000 线程)。分布式压测通过多台机器协同,可模拟数万甚至数十万并发。

16.2 分布式架构

                    ┌──────────────────┐
                    │   Master (控制机)  │
                    │   发送测试计划      │
                    │   收集汇总结果      │
                    └────────┬─────────┘
                             │
          ┌──────────────────┼──────────────────┐
          │                  │                  │
┌─────────┴────────┐ ┌───────┴────────┐ ┌───────┴────────┐
│  Slave 1 (执行机) │ │  Slave 2 (执行机)│ │  Slave 3 (执行机)│
│  1000 线程       │ │  1000 线程      │ │  1000 线程      │
└──────────────────┘ └────────────────┘ └────────────────┘
         总计: 3000 并发用户

16.3 配置步骤

Step 1:所有机器安装相同版本 JMeter 和 JDK

# 确保版本一致
jmeter --version
java -version

Step 2:配置执行机(Slave)

修改 $JMETER_HOME/bin/jmeter.properties

# 设置服务器端口(默认 1099)
server.rmi.localport=1099

# 设置 RMI 相关
server.rmi.ssl.disable=true

⚠️ 生产环境建议开启 SSL,这里为演示简化关闭。

启动执行机:

# 在每台 Slave 上执行
jmeter-server
# 或
jmeter -s

Step 3:配置控制机(Master)

修改 $JMETER_HOME/bin/jmeter.properties

# 填写所有执行机的 IP
remote_hosts=192.168.1.10:1099,192.168.1.11:1099,192.168.1.12:1099
server.rmi.ssl.disable=true

Step 4:启动分布式测试

# 本地全部远程执行
jmeter -n -t test.jmx -r -l result.jtl -e -o ./report/

# 指定部分远程执行
jmeter -n -t test.jmx -R 192.168.1.10,192.168.1.11 -l result.jtl

16.4 线程数计算

关键概念:分布式测试中,Thread Group 中设置的线程数是每台执行机的线程数。

Thread Group 设置: 1000 线程
执行机数量: 3 台
实际总并发: 1000 × 3 = 3000 线程

16.5 分布式测试注意事项

  1. 防火墙:确保 1099 端口及 RMI 动态端口畅通
  2. 网络:Master 和 Slave 最好在同一内网
  3. 时钟同步:所有机器时钟需同步(NTP)
  4. 数据文件:CSV 数据文件需复制到每台 Slave 相同路径
  5. 端口冲突:一台机器可启动多个 jmeter-server,需设置不同端口
  6. 资源监控:压测时监控各 Slave 的 CPU/内存,确保 Slave 本身不是瓶颈

16.6 单机多实例压测

单台机器启动多个 JMeter Server 实例:

# 实例1
jmeter-server -Dserver.rmi.localport=1099

# 实例2
jmeter-server -Dserver.rmi.localport=1100

Master 配置:

remote_hosts=127.0.0.1:1099,127.0.0.1:1100

17. 性能指标分析与调优

17.1 核心性能指标

17.1.1 响应时间指标
指标说明计算方式
Average平均响应时间所有请求响应时间平均值
Median (P50)中位数50% 请求响应时间低于此值
90th Percentile (P90)90分位90% 请求响应时间低于此值
95th Percentile (P95)95分位95% 请求响应时间低于此值
99th Percentile (P99)99分位99% 请求响应时间低于此值
Min/Max最小/最大响应时间-

💡 性能评估优先级:P99 > P95 > P90 > Median > Average。平均值容易被极端值扭曲。

17.1.2 吞吐量指标
指标说明
TPS (Transactions Per Second)每秒事务数,业务级指标
QPS (Queries Per Second)每秒查询数,接口级指标
RPS (Requests Per Second)每秒请求数
Hits Per Second每秒点击数

TPS 与 QPS 区别:TPS 关注事务(一笔完整业务),QPS 关注单次请求。一次事务可能包含多次请求。

17.1.3 资源指标
指标警戒值危险值
CPU 使用率> 70%> 85%
内存使用率> 70%> 85%
磁盘 I/O--
网络带宽> 70%> 85%
数据库连接数> 80% 连接池连接池耗尽

17.2 Little’s Law(利特尔法则)

性能测试的核心理论之一,描述并发数、吞吐量和响应时间的关系:

L = λ × W

L = 并发用户数 (Concurrent Users)
λ = 吞吐量 (Throughput, 请求/秒)
W = 平均响应时间 (Response Time, 秒)

应用示例:

已知: 系统需要支持 500 TPS,平均响应时间 0.2秒
求: 需要多少并发用户?

L = 500 × 0.2 = 100 并发用户

💡 利特尔法则帮助我们在设计测试场景时,根据目标 TPS 和可接受的响应时间,反推需要的并发线程数。

17.3 性能瓶颈分析方法

17.3.1 分层分析法
客户端 (JMeter)
    ↓ TPS低 / 响应慢
网络层 → ping延迟、带宽、丢包
    ↓
应用层 → CPU、内存、GC、线程状态
    ↓
中间件 → 连接池、缓存命中率、队列堆积
    ↓
数据库 → 慢SQL、锁等待、连接数
    ↓
系统层 → CPU、内存、磁盘IO、网络IO
17.3.2 常见瓶颈与排查
瓶颈现象可能原因排查工具
TPS 上不去,响应时间正常线程数不足、连接池太小应用日志、线程dump
TPS 上不去,响应时间飙升数据库慢查询、锁竞争数据库监控、慢查询日志
错误率随时间增长内存泄漏、连接泄漏JVM监控、GC日志
TPS 波动大GC停顿、资源争抢GC日志、系统监控
响应时间波动大网络抖动、缓存穿透网络监控、缓存监控
17.3.3 监控工具推荐

JVM 监控:

# jstat 查看 GC 情况
jstat -gcutil <pid> 1000  # 每秒打印一次

# jmap 查看内存
jmap -heap <pid>
jmap -histo <pid> | head -20

# jstack 查看线程
jstack <pid> | grep -A 5 "BLOCKED"

# Arthas(阿里巴巴诊断工具)
./as.sh <pid>
arthas> dashboard  # 实时面板
arthas> thread -n 3  # 最忙的3个线程
arthas> trace com.example.Service method  # 方法调用链

系统监控:

# CPU/内存综合
top -p <pid>
htop

# 磁盘 IO
iostat -x 1

# 网络
iftop
nethogs

# 综合监控(推荐)
# Prometheus + Grafana + node_exporter

数据库监控:

-- MySQL 查看当前连接
SHOW STATUS LIKE 'Threads%';

-- 查看慢查询
SHOW VARIABLES LIKE 'slow_query%';

-- 查看锁等待
SELECT * FROM information_schema.INNODB_LOCK_WAITS;

-- 查看正在执行的 SQL
SHOW FULL PROCESSLIST;

17.4 JMeter 自身调优

当 JMeter 自身成为瓶颈时(单机无法模拟足够并发):

# jmeter.bat / jmeter.sh 中修改 JVM 参数

# 堆内存调大(根据机器内存调整)
HEAP="-Xms2g -Xmx4g"

# 元空间
GC_ALGO="-XX:MaxMetaspaceSize=512m"

# 推荐 JVM 配置(16GB 机器)
HEAP="-Xms4g -Xmx8g -XX:MaxMetaspaceSize=512m"

其他优化项:

# jmeter.properties

# 减少 JMeter 自身日志
jmeter.save.saveservice.response_data=false
jmeter.save.saveservice.response_data.on_error=false
jmeter.save.saveservice.samplerData=false

# 使用 Backend Listener 替代本地文件记录
# 减少 Results File 写入

# 禁用 GUI 组件(非GUI模式自动禁用)

18. JMeter 插件

18.1 插件管理器安装

  1. 下载 JMeter Plugins Manager 的 jar 包
  2. 放入 $JMETER_HOME/lib/ext/ 目录
  3. 重启 JMeter
  4. 菜单 Options → Plugins Manager

18.2 必装插件推荐

18.2.1 Custom Thread Groups

提供高级线程组,实现阶梯加压:

Ultimate Thread Group:

Start Threads CountInitial DelayRamp Up TimeHold TimeShutdown Time
10006030010
100606024010
1001206018010

效果:0秒启动100用户,60秒后再加100,120秒后再加100,逐步加压到300用户。

Stepping Thread Group:

可视化阶梯加压,适合压力测试场景。

18.2.2 3 Basic Graphs

提供更多图表:

  • Response Times Over Time
  • Active Threads Over Time
  • Bytes Throughput Over Time
  • Hits Per Second
  • Response Codes Per Second
18.2.3 PerfMon Plugin

监控服务器资源指标(CPU、内存、磁盘、网络):

  1. 在被测服务器上启动 ServerAgent
./startAgent.sh --interval 1
  1. JMeter 中添加 jp@gc - PerfMon Metrics Collector
Host: 192.168.1.100
Port: 4444
Metric to collect: CPU, Memory, Network I/O
18.2.4 Dummy Sampler

模拟请求(不发送真实请求),用于调试脚本逻辑:

Response Code: 200
Response Data: {"code":200,"data":{"token":"test_token"}}

💡 调试关联脚本时,用 Dummy Sampler 模拟前一个请求的响应,快速验证提取器配置是否正确。

18.2.5 Throughput Shaping Timer

精确控制吞吐量的定时器:

Start Threads: 100
End Threads: 500
Shaping Period: 60  (60秒内从100加到500)

18.3 常用插件一览

插件功能
Custom Thread Groups阶梯加压线程组
3 Basic Graphs基础图表扩展
5 Additional Graphs高级图表
PerfMon服务器资源监控
Dummy Sampler模拟采样器
Throughput Shaping Timer吞吐量塑形定时器
JSON PluginsJSON 相关工具
WebSocket SamplersWebSocket 协议测试
Kafka SamplersKafka 测试
Dubbo SamplerDubbo 接口测试

19. CI/CD 集成(Jenkins)

19.1 JMeter + Jenkins 自动化性能测试

将性能测试纳入 CI/CD 流程,实现每次代码提交自动执行性能回归。

19.2 安装 Performance Plugin

  1. Jenkins → Manage Jenkins → Plugins → Available
  2. 搜索 Performance 并安装
  3. 重启 Jenkins

19.3 Jenkins Pipeline 配置

pipeline {
    agent any
    
    environment {
        JMETER_HOME = '/usr/local/apache-jmeter-5.6.3'
        TEST_PLAN = 'product_test.jmx'
    }
    
    stages {
        stage('Checkout') {
            steps {
                git 'https://github.com/your-repo/performance-tests.git'
            }
        }
        
        stage('Run JMeter') {
            steps {
                sh """
                    ${JMETER_HOME}/bin/jmeter -n -t ${TEST_PLAN} \
                        -l result.jtl \
                        -e -o ./report/ \
                        -Jthreads=100 \
                        -JrampUp=60 \
                        -Jduration=300
                """
            }
        }
        
        stage('Publish Report') {
            steps {
                // Performance Plugin 解析结果
                perfReport modeThroughput: true, 
                           modeResponseTime: true, 
                           errorFailedThreshold: 1, 
                           errorUnstableThreshold: 0.5, 
                           compareBuildPrevious: true, 
                           configType: 'PRT', 
                           sourceDataFiles: 'result.jtl'
                
                // 发布 HTML 报告
                publishHTML(target: [
                    allowMissing: false,
                    alwaysLinkToLastBuild: true,
                    keepAll: true,
                    reportDir: 'report',
                    reportFiles: 'index.html',
                    reportName: 'JMeter Report'
                ])
            }
        }
        
        stage('Performance Gate') {
            steps {
                script {
                    // 解析结果并判断是否通过性能门禁
                    def result = sh(returnStdout: true, 
                        script: """
                            awk -F',' 'NR>1 {
                                if (\$4 > 500) {print "FAIL"; exit}
                            }' result.jtl
                        """).trim()
                    
                    if (result == 'FAIL') {
                        error("性能测试未通过: P95 响应时间超过 500ms")
                    }
                }
            }
        }
    }
    
    post {
        always {
            archiveArtifacts artifacts: 'result.jtl,report/**', allowEmptyArchive: true
        }
    }
}

19.4 性能门禁策略

指标门禁规则级别
错误率> 1%失败
错误率> 0.5%不稳定
P95 响应时间> 500ms失败
TPS 下降> 10%(对比上次)不稳定
APDEX< 0.7失败

19.5 命令行解析结果

#!/bin/bash
# parse_jmeter_result.sh

RESULT_FILE=$1

# 提取关键指标
TOTAL=$(grep -c "," $RESULT_FILE)
# 注意:根据 jtl 格式调整字段索引

echo "Total Samples: $TOTAL"

# 使用 JMeter 自带工具分析(如果有)
# 或使用第三方工具如 jmeter-results-parser

20. 最佳实践与常见问题

20.1 JMeter 最佳实践

20.1.1 脚本编写
  1. GUI 编写,非 GUI 执行:永远在 GUI 中编写和调试脚本,在非 GUI 中执行压测

  2. 使用测试片段(Test Fragment):将可复用的请求逻辑放入 Test Fragment,通过 Module Controller 引用,避免重复

  3. 合理命名元件:给每个元件取有意义的名字,便于维护

✅ 错误的命名: HTTP Request
✅ 正确的命名: 用户登录-POST /api/login
  1. 使用变量和属性:将服务器地址、端口等配置定义为变量,便于环境切换

  2. 添加注释:使用 Test Plan 的 Comments 字段或 Dummy Sampler 添加说明

20.1.2 性能测试执行
  1. 先做基准测试:单用户运行获取基准数据,作为对比参考

  2. 逐步加压:不要直接用大并发,先小后大,观察拐点

  3. 足够的持续时间:短时间测试无法发现内存泄漏等问题,稳定性测试至少 1 小时以上

  4. 预热阶段:JIT 编译、缓存预热需要时间,前 1~2 分钟的数据可以忽略

  5. 多次测试取平均:单次测试结果有偶然性,建议同一场景测试 3 次取中位数

  6. 监控被测系统:压测时同时监控系统资源,否则无法定位瓶颈

20.1.3 资源优化
  1. 禁用不必要的监听器:压测时删除 View Results Tree、View Results in Table 等监听器

  2. 减少日志写入

jmeter.save.saveservice.response_data=false
jmeter.save.saveservice.samplerData=false
jmeter.save.saveservice.encoding=false
  1. 使用 Backend Listener:大规模测试时用 InfluxDB 收集数据,替代本地 .jtl 文件

  2. 合理设置 JVM:根据机器内存调整 JMeter 的 JVM 堆大小

  3. CSV 文件控制大小:大文件读取影响性能,按需准备数据量

20.2 常见问题排查

Q1: 压测时大量 Connection Refused

原因:服务器连接数达到上限,或 JMeter 端口耗尽

解决

# 1. 检查服务器最大连接数
ulimit -n  # 默认 256/1024,调大
ulimit -n 65535

# 2. JMeter 端 (客户端) 调整
# macOS/Linux 释放端口更快
sudo sysctl -w net.ipv4.tcp_tw_reuse=1
# 扩大本地端口范围
sudo sysctl -w net.ipv4.ip_local_port_range="1024 65535"
Q2: OutOfMemoryError: Java heap space

原因:JMeter 内存不足

解决

# 增大堆内存
HEAP="-Xms4g -Xmx8g"

# 或减少并发 / 使用分布式
Q3: HTTPS 证书错误

解决

# 在 HTTP Request 中配置
# 或在 jmeter.properties 中
https.use.cached.ssl.context=false
https.socket.https.cps=0
Q4: 响应乱码

解决

# 在 HTTP Request 中设置
Content Encoding: UTF-8

# 或在后置处理器中
prev.setDataEncoding("UTF-8")

# jmeter.properties
sampleresult.default.encoding=UTF-8
Q5: TPS 始终上不去

排查思路

  1. 检查线程数和 Ramp-Up 设置是否合理
  2. 检查是否有定时器限制了请求频率
  3. 检查 JMeter 机器 CPU/内存是否打满
  4. 检查被测系统是否有限流(如 Sentinel、Nginx limit_req)
  5. 检查数据库连接池是否已满
  6. 检查网络带宽是否成为瓶颈
Q6: 分布式测试 Slave 连接失败

解决

# 1. 检查防火墙
# 2. 检查 RMI 端口
# 3. 使用 -Djava.rmi.server.hostname 指定正确 IP
jmeter-server -Djava.rmi.server.hostname=192.168.1.10

# 4. 确保 SSL 配置一致

20.3 JMeter 快捷键

快捷键功能
Ctrl+S保存测试计划
Ctrl+E清除所有结果
Ctrl+R运行测试
Ctrl+.停止测试
Ctrl+,关闭测试
Ctrl+Shift+E清除所有结果(全部)
Ctrl+0函数助手对话框

20.4 学习路径建议

入门阶段
├── 理解性能测试基本概念
├── 安装 JMeter,跑通第一个测试
├── 熟悉 GUI 界面和核心组件
└── 学会查看结果树和聚合报告
    ↓
进阶阶段
├── 掌握参数化(CSV Data Set Config)
├── 掌握关联(JSON/正则提取器)
├── 掌握断言
├── 掌握逻辑控制器(事务、循环、条件)
└── 学会使用非 GUI 模式
    ↓
高级阶段
├── 分布式压测
├── JSR223 Groovy 脚本编程
├── 性能瓶颈分析与调优
├── 插件开发
└── CI/CD 集成

附录

A. JMeter 内置函数速查

函数说明示例
${__time(,)}毫秒时间戳1718769600000
${__time(yyyy-MM-dd,)}格式化日期2025-06-19
${__Random(1,100,)}随机整数42
${__RandomString(10,abcdefg,)}随机字符串cdfbageba
${__UUID()}UUIDa3b4c5d6-…
${__counter(TRUE,)}线程计数器1, 2, 3…
${__threadNum}线程编号1
${__P(propName)}读取属性-
${__property(propName)}读取属性-
${__setProperty(name,value)}设置属性-
${__V(var)}嵌套变量-
${__split(str,var,sep)}分割字符串-
${__groovy(expr,)}执行Groovy-
${__jexl3(expr,)}执行JEXL-
${__log(msg)}写日志-
${__logn(msg)}写日志(换行)-
${__BeanShell(script)}BeanShell脚本-
${__evalVar(var)}求值变量-
${__FileToString(path)}读取文件内容-
${__base64Encode(str)}Base64编码-
${__base64Decode(str)}Base64解码-
${__urlencode(str)}URL编码-
${__urldecode(str)}URL解码-
${__md5(str)}MD5哈希-
${__sha256(str)}SHA256哈希-

B. JSR223 内置变量速查

变量类型说明
varsJMeterVariables当前线程的变量,vars.get()/vars.put()
propsProperties全局属性,props.get()/props.put()
prevSampleResult上一个采样器结果
ctxJMeterContext当前线程上下文
logLogger日志对象,log.info()/log.error()
OUTPrintStreamSystem.out
AssertionResultAssertionResult断言结果(仅断言中可用)
ResponseCodeString响应码
ResponseMessageString响应消息
SampleLabelString采样器名称
SamplerDataString请求数据
ctx.getPreviousResult()SampleResult上一个采样器结果

C. 推荐学习资源


结语:性能测试不是"跑个工具看个数字"那么简单。优秀的性能测试工程师需要理解系统架构、掌握测试方法论、熟悉监控分析工具。JMeter 是强大的武器,但真正决定测试质量的是你的分析能力和对系统的理解深度。希望本文能帮助你从入门走向精通,在实践中不断积累经验。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值