JMeter 性能测试教程

JMeter 性能测试教程

▍ 从零开始学习 Apache JMeter:安装、编写测试脚本、参数化、断言、关联、压力测试与报告生成。

▍ 本仓库附带一个可直接运行的示例测试计划:`examples/demo-api.jmx`

1. 什么是 JMeter

Apache JMeter 是 Apache 基金会出品的一款开源的、基于 Java 的压力测试工具。它原本用于 Web 应用性能测试,后来扩展为支持多种协议:

|----------------------|---------------|
| 协议 | 用途 |
| 协议 | 用途 |
| HTTP / HTTPS | Web 接口、网页性能测试 |
| FTP | 文件上传下载 |
| JDBC | 数据库性能测试 |
| JMS | 消息队列 |
| TCP / UDP | 自定义协议 |
| WebSocket / gRPC(插件) | 现代应用 |

JMeter 能做什么:

  • 模拟成百上千的并发用户
  • 对接口做负载 / 压力 / 稳定性测试
  • 用断言校验响应是否正确
  • 参数化与关联,模拟真实业务场景
  • 生成聚合报告与 HTML 图表报告

JMeter 不是浏览器的替代品:它不执行 JS / CSS 渲染(除非使用 Selenium / WebDriver 插件)。做纯接口性能测试它是最佳选择之一。

2. 安装与启动

2.1 环境要求

  • JDK 8+(JMeter 5.x 建议 JDK 8~11,JMeter 5.5 之后需 JDK 8+,部分新版本需 11+)
  • 不需要安装 Tomcat 等容器,JMeter 是独立程序

2.2 下载

官方下载页:<https://jmeter.apache.org/download_jmeter.cgi\>

  • 选择 apache-jmeter-x.x.zip(Windows)或 .tgz(Linux/macOS)
  • 解压即可,无需安装

2.3 启动

进入解压后的 bin 目录

cd apache-jmeter-5.6.3/bin

Windows

jmeter.bat

Linux / macOS(需要执行权限)

./jmeter

▍ 提示:jmeter.bat 打开的是 GUI(图形界面)。性能测试时务必使用命令行模式(见 第 13 节),GUI 只用于编写和调试脚本。

2.4 界面速览

图 1 JMeter 主界面:菜单栏、工具栏、左侧测试计划树与右侧配置面板(整棵树已展开)

启动后默认打开测试计划编辑器。常用区域:

  • 左侧树:测试计划结构(Test Plan 根节点、线程组、采样器等)
  • 右侧面板:选中节点的配置区域
  • 菜单栏:File / Edit / Run / Options 等

3. 核心概念

JMeter 的一切都是建立在下面的树形结构上:

Test Plan(测试计划)

└── Thread Group(线程组)

├── Config Element(配置元件):如 HTTP 请求默认值、CSV 数据

├── Sampler(采样器):如 HTTP 请求、JDBC 请求

├── Logic Controller(逻辑控制器):循环、条件、随机等

├── Timer(定时器):控制请求间隔

├── Assertion(断言):校验结果

└── Listener(监听器):查看/保存结果

|----------------------|---------------------------------------------------------|
| 概念 | 说明 |
| 概念 | 说明 |
| Test Plan | 整个测试的根节点,可配置全局变量、线程组执行策略 |
| Thread Group | 一组虚拟用户。线程数 = 并发用户数,Ramp-Up 表示多久启动完所有线程 |
| Sampler | 真正发起请求的组件,如 HTTP Request |
| Config Element | 提供默认值 / 数据源,如 HTTP Request Defaults、CSV Data Set Config |
| Logic Controller | 控制采样器的执行顺序和次数,如 Loop Controller、If Controller |
| Timer | 控制请求之间的间隔,模拟用户思考时间 |
| Assertion | 校验响应是否符合预期,失败则标记该请求为错误 |
| Listener | 收集并展示结果,如 View Results Tree、Aggregate Report |

▍ 执行顺序:配置元件 → 前置处理器 → 定时器 → 采样器 → 断言 → 后置处理器 → 监听器(同一作用域内按此顺序执行)。

4. 第一个测试计划

下面创建一个访问 https://httpbin.org/get 的最小测试计划。

4.1 新建并保存

  1. 打开 JMeter,测试计划会自动创建,命名为 Test Plan
  2. 点击 File → Save As,保存为 first-test.jmx

4.2 添加线程组

图 2 线程组面板:设置线程数、Ramp-Up 时间与循环次数

  1. 右键 Test Plan → Add → Threads (Users) → Thread Group
  2. 配置:
  • 线程数(Number of Threads):10
  • Ramp-Up 时间(秒):5
  • 循环次数(Loop Count):1

4.3 添加 HTTP 请求

  1. 右键线程组 → Add → Sampler → HTTP Request
  2. 配置:
  • 协议:https
  • 服务器名称:httpbin.org
  • 方法:GET
  • 路径:/get

4.4 添加监听器

  1. 右键线程组 → Add → Listener → View Results Tree
  2. 右键线程组 → Add → Listener → Summary Report

4.5 运行

点击工具栏绿色 启动 按钮。运行后:

  • View Results Tree 中可查看每个请求的响应体、响应头
  • Summary Report 显示平均响应时间、吞吐量、错误率

一个合格测试计划的最低配置 :线程组 + 采样器 + 断言 + 监听器。请记住:只有加断言,测试结果才有意义

5. HTTP 请求详解

5.1 核心字段

图 3 HTTP 请求面板:协议、服务器、方法与路径等核心字段

|------------------|-----------------------------|-----------------------------|
| 字段 | 说明 | 示例 |
| 字段 | 说明 | 示例 |
| 协议 | http / https | https |
| 服务器名称或 IP | 主机名,可包含端口 | httpbin.org 或 10.0.0.1:8080 |
| 方法 | GET / POST / PUT / DELETE 等 | POST |
| 路径 | 接口路径 | /api/login |
| Content encoding | 编码,建议 UTF-8 | UTF-8 |
| 参数 | GET 查询参数 或 表单参数 | name=john |
| Body Data | 请求体(POST 常用) | JSON 字符串 |

5.2 GET 请求

路径:/get

参数(Query String):

name = john

age = 18

等价于请求 https://httpbin.org/get?name=john\&age=18

5.3 POST JSON 请求

方法:POST

路径:/post

Content encoding:UTF-8

Body Data:

{"username": "admin", "password": "123456"}

5.4 设置请求头(HTTP Header Manager)

图 4 HTTP Header Manager:为请求添加请求头

右键采样器 → Add → Config Element → HTTP Header Manager,添加:

Content-Type: application/json

Authorization: Bearer <token>

5.5 复用公共配置(HTTP Request Defaults)

如果多个请求都访问同一个服务器,右键线程组 → Add → Config Element → HTTP Request Defaults,统一配置协议和服务器名。子级采样器无需重复填写。

▍ 把不变的配置放在 HTTP Request Defaults,变的部分写在采样器里,脚本更易维护。

6. 断言(Assertions)

断言用于校验响应是否符合预期,是判断请求"成功/失败"的依据。

6.1 响应断言(Response Assertion)

图 5 响应断言面板:校验响应是否包含指定文本

右键采样器 → Add → Assertions → Response Assertion

配置示例:

要测试的模式: 响应文本(Response Text)

匹配规则: 包含(Contains)

要测试的模式: "success": true

常用匹配规则:

|----------------|---------------------|
| 规则 | 说明 |
| 规则 | 说明 |
| 包含(Contains) | 响应中是否包含指定文本(子串匹配) |
| 匹配(Matches) | 正则匹配整个响应 |
| 等于(Equals) | 完全相等 |
| 字符串(Substring) | 与 Contains 类似,无正则语法 |

6.2 断言失败时的表现

  • 请求在结果树中显示为红色
  • 汇总报告中的错误率(Error %)上升
  • 可通过 Response Assertion 的"自定义失败消息"查看失败原因

6.3 JSON 断言

右键采样器 → Add → Assertions → JSON Assertion

JSON Path:$.success

Expected Value:true

要求返回的 JSON 中 success 字段为 true。

6.4 持续时间断言(Duration Assertion)

用于校验响应时间不超过阈值:

Duration:1000 (毫秒,超过 1 秒视为失败)

7. 参数化

参数化让不同虚拟用户使用不同数据,更贴近真实场景。

7.1 用户自定义变量(User Defined Variables)

右键线程组 → Add → Config Element → User Defined Variables

host = https://httpbin.org

username = test01

在采样器中使用 {host}、{username} 引用。

7.2 CSV 数据驱动(CSV Data Set Config)

图 6 CSV Data Set Config:数据驱动参数化

步骤 1:准备 users.csv:

username,password

test01,123456

test02,abcdef

test03,xyz789

步骤 2 :右键线程组 → Add → Config Element → CSV Data Set Config,配置:

|--------|------------------------------|
| 字段 | |
| 字段 | 值 |
| 文件名 | users.csv(绝对路径或相对 bin 目录) |
| 文件编码 | UTF-8 |
| 变量名称 | username,password |
| 分隔符 | , |
| 是否忽略首行 | True(跳过表头) |
| 循环模式 | 可设置 Recycle 为 True(数据用完重新开始) |

步骤 3:在 HTTP 请求参数 / Body 中使用:

Body Data:{"username": "{username}", "password": "{password}"}

步骤 4线程数设为 CSV 数据行数,每个线程读取一行,实现多用户登录压测。

7.3 内置函数(Functions)

右键参数输入框 → Generate,或直接手写函数:

|--------------------------|------------|---------------------------|
| 函数 | 作用 | 示例 |
| 函数 | 作用 | 示例 |
| {__threadNum} | 当前线程号 | {__threadNum} |
| {__time(YYYY-MM-dd)} | 当前时间 | {__time(2024-01-01)} |
| {__Random(1,100)} | 随机数 | {__Random(1,100)} |
| {__UUID} | 随机 UUID | {__UUID} |
| {__CSVRead(file,index)} | 读取 CSV 指定列 | {__CSVRead(users.csv,0)} |

8. 关联

实际场景中,登录后返回的 token 往往要传给后续请求使用,这称为关联

8.1 正则表达式提取器(Regular Expression Extractor)

图 7 正则表达式提取器:提取 token 实现关联

场景:登录接口返回 {"token": "abc123"},订单接口需要在 Header 中使用它。

  1. 在登录采样器上右键 → Add → Post Processors → Regular Expression Extractor
  2. 配置:

Apply to:Main sample

字段:响应文本

引用名称(Reference Name):token

正则表达式:\{"token": "(.+?)"\}

模板:1

匹配数字:1

默认值:NOT_FOUND

  1. 在订单请求的 Header 中引用:Authorization: Bearer ${token}

▍ 正则中 (.+?) 是非贪婪匹配,用于提取括号内的内容。模板 1 表示取第一个分组。

8.2 JSON 提取器(JSON Extractor)

图 8 JSON Extractor:用 JSON Path 提取字段

需要 JSON Path 更直观:

引用名称:token

JSON Path:$.token

步骤 :右键登录采样器 → Add → Post Processors → JSON Extractor

9. 逻辑控制器

控制采样器的执行逻辑。

|------------------------|----------------|
| 控制器 | 作用 |
| 控制器 | 作用 |
| Loop Controller | 循环执行子节点 N 次 |
| If Controller | 满足条件才执行 |
| While Controller | 满足条件时一直循环 |
| Random Controller | 随机执行其中一个子节点 |
| Transaction Controller | 把一组请求合并为一个事务统计 |
| Interleave Controller | 轮流执行子节点 |
| ForEach Controller | 遍历变量数组 |

示例:事务控制器

图 9 事务控制器:把一组请求合并为一个事务统计

把"下单流程"(登录 → 创建订单 → 查询订单)包进 Transaction Controller,报表中会把它当做一个整体事务统计耗时。

勾选 Generate parent sample 后,事务控制器本身也会生成一条汇总记录,便于统计完整业务链路的耗时。

10. 定时器

定时器控制请求发送的节奏,避免所有线程在瞬间发出请求。

|---------------------------|---------------------|
| 定时器 | 说明 |
| 定时器 | 说明 |
| Constant Timer | 固定等待 N 毫秒 |
| Gaussian Random Timer | 服从高斯分布的随机延迟,更贴近真实用户 |
| Uniform Random Timer | 均匀分布的随机延迟 |
| Constant Throughput Timer | 控制吞吐量(每分钟请求数) |
| Synchronizing Timer | 集合点,让所有线程同时发起请求 |

示例:模拟思考时间

线程组下添加 Gaussian Random Timer:

延迟范围:500 ms (基础 0.5 秒)

标准差: 100 ms

示例:集合点(并发瞬间压测)

图 10 集合点定时器:等待 N 个线程同时发起请求

右键线程组 → Add → Timer → Synchronizing Timer

Number of Simulated Users to Group:100

Timeout in milliseconds:5000

即:等 100 个线程都到达后,一起发出请求,用于测试服务端瞬间峰值并发能力。

11. 压力测试与并发

11.1 线程组参数含义

|-----------------------------|-------------------------------------|
| 参数 | 说明 |
| 参数 | 说明 |
| Number of Threads | 虚拟用户总数(并发数) |
| Ramp-Up Period | 启动所有线程花费的时间(秒)。0 表示立即全部启动 |
| Loop Count | 每个线程循环执行的次数。勾选 Forever 表示一直跑,直到手动停止 |
| Same user on each iteration | 勾选后,每个迭代使用同一用户数据(配合 CSV 保持登录态时有用) |

11.2 Ramp-Up 的计算建议

Ramp-Up ≈ 并发数 / 每秒目标启动数

  • 想要 1 秒启动 5 个线程,100 并发 → Ramp-Up = 20
  • 压测通常建议 Ramp-Up 给 5~10 秒,避免启动风暴把网络/代理打满

11.3 常用测试类型

|--------|---------|---------|----------------------|
| 类型 | 线程数 | 循环 | 说明 |
| 类型 | 线程数 | 循环 | 说明 |
| 冒烟测试 | 1~10 | 1~3 | 验证脚本正确、环境连通 |
| 负载测试 | 50~500 | N | 观察系统在预期负载下的表现 |
| 压力测试 | 逐步增加 | Forever | 找到系统拐点 / 最大承载 |
| 稳定性测试 | 适中 | Forever | 长时间运行(如 8 小时)看资源是否泄漏 |

11.4 压测通用流程

  1. 先小并发冒烟,确认脚本无误
  2. 用少量线程 + 循环验证断言正确性
  3. 逐步增加线程数,观察吞吐量(TPS)与响应时间曲线
  4. 找到拐点:TPS 不再增长、错误率上升的时间点即系统瓶颈

重要 :压测请使用命令行模式(第 13 节),GUI 本身会消耗资源且影响结果,切勿用 GUI 做高压测试。

12. 查看与生成报告

12.1 常用监听器

图 11 View Results Tree:查看单个请求的请求/响应细节

图 12 Summary Report:平均/中位数/90% 分位/吞吐量/错误率

|-------------------------|-----------------------------|
| 监听器 | 用途 |
| 监听器 | 用途 |
| View Results Tree | 查看单个请求的请求/响应细节,调试用 |
| Summary Report | 汇总:平均、中位数、90% 分位、吞吐量、错误率 |
| Aggregate Report | 类似 Summary,含 Min/Max/StdDev |
| Assertion Results | 只看断言失败信息 |
| Graph Results | 曲线图 |
| Response Time Over Time | 响应时间随时间变化(需插件) |

12.2 生成 HTML 报告

先在命令行跑一次并保存结果,然后生成报告:

运行并保存结果到 result.jtl

jmeter -n -t test.jmx -l result.jtl -e -o report-dir

参数说明:

|--------|-----------------------|
| 参数 | 含义 |
| 参数 | 含义 |
| -n | 非 GUI 模式 |
| -t | 指定测试计划 .jmx 文件 |
| -l | 结果日志文件 .jtl |
| -e | 测试结束后生成 HTML 报告 |
| -o | HTML 报告输出目录(必须为空或不存在) |

生成后打开 report-dir/index.html 即可看到图表化报告(吞吐量、响应时间分位数、活跃线程数等)。

13. 命令行(非 GUI)运行

压测和 CI 集成时使用命令行模式:

基础运行

jmeter -n -t demo-api.jmx -l result.jtl

运行 + 生成 HTML 报告

jmeter -n -t demo-api.jmx -l result.jtl -e -o html-report

通过参数覆盖脚本中的自定义变量(如服务器地址)

jmeter -n -t demo-api.jmx -Jhost=httpbin.org -Jthreads=50 -l result.jtl

脚本中引用自定义属性:{__P(host,httpbin.org)}、{__P(threads,10)}。

▍ 远程分布式压测:jmeter -n -R <slave_ip> 可调用远程机执行,具体配置见官方文档。

14. 常用函数与变量

14.1 变量引用方式

|----------------------|--------------------|
| 写法 | 说明 |
| 写法 | 说明 |
| {var} | 引用变量/参数 | | {__P(name,default)} | 命令行参数,可给默认值 |
| {__property(name)} | 引用 properties 文件属性 | | {__V(expr)} | 动态求值(嵌套变量时用) |

14.2 高频函数速查

|--------------------------------|----------|--------------------------------|
| 函数 | 返回值 | 示例 |
| 函数 | 返回值 | 示例 |
| {__Random(1,9999)} | 随机整数 | {__Random(1,9999)} |
| {__time(/1000)} | 当前时间戳(秒) | {__time(/1000)} |
| {__time(YYYY-MM-dd HH:mm:ss)} | 格式化时间 | {__time(2026-08-14 12:00:00)} |
| {__UUID} | UUID 字符串 | {__UUID} |
| {__threadNum} | 当前线程号 | {__threadNum} |
| {__counter(FALSE,id)} | 计数器 | {__counter(FALSE,id)} |
| {__evalVar(var)} | 变量求值 | {__evalVar(host)} |
| {__intSum(a,b)} | 整数求和 | {__intSum(100,${n})} |

14.3 JMeter 内置属性

  • ${JMeterThread.last_sample_ok}:上一个采样器是否成功(true/false)
  • ${prev}:前一个采样器的 SampleResult 对象
  • ${vars}:变量容器(可在 JSR223 中使用)

15. 最佳实践

  1. 不要用 GUI 压测:GUI 用于编写和调试,正式压测一律命令行(-n)。
  2. 必须加断言:没有断言,错误率永远是 0,结果无意义。
  3. 使用 HTTP Request Defaults:统一协议/服务器,减少重复配置、便于迁移环境。
  4. 参数化数据:用 CSV 模拟多用户,避免全部线程用同一账号导致服务端缓存干扰结果。
  5. 合理设置 Ramp-Up:不要 0 秒瞬间启动所有线程,除非专门测集合点。
  6. 分离测试环境:压测会影响被测系统性能,不要在生产直接压(除非有明确授权)。
  7. 先小规模验证:10 并发跑通后再放大并发。
  8. 监控服务端资源:结合 CPU / 内存 / 数据库连接等指标定位瓶颈,别只看 JMeter 数字。
  9. 保持脚本简单:优先用内置组件,JSR223 脚本(Groovy)慎用且要注意性能。
  10. 结果可复现:固定线程数、循环、定时器,避免用 GUI 手动点来点去。
  11. 不要超时中断:设置合理的连接/响应超时(如 10s),否则 JMeter 会长时间挂起。

16. 常见问题

Q1:启动时提示找不到 Java?

安装 JDK 8+ 并配置 JAVA_HOME 环境变量,重启终端后再启动。

Q2:提示 `OutOfMemoryError`?

修改 bin/jmeter.bat 中的堆内存参数:

set HEAP=-Xms512m -Xmx2g

Q3:为什么响应时间看起来很大?

  • 是否包含了思考时间(定时器)
  • 是否在 GUI 里运行(GUI 影响性能)
  • 采样器是否设置了超时,被网络阻塞挂起

Q4:CSV 中文乱码?

文件保存为 UTF-8(可用 notepad++/VS Code 转换),并在 CSV Data Set Config 中设置文件编码为 UTF-8。

Q5:如何保持登录态?

图 13 HTTP Cookie Manager:自动保存并携带 Cookie 保持登录态

  • 用 HTTP Cookie Manager(右键线程组 → Add → Config Element → HTTP Cookie Manager)
  • 跨线程组共享登录态时,可把 token 写入文件或用 setProperty / ${__property} 传递

Q6:如何只看失败请求?

View Results Tree 顶部筛选框输入 Error,或直接看 Assertion Results 监听器。

Q7:JMeter 与 Selenium 的区别?

JMeter 走 HTTP 协议层,不渲染页面;Selenium 是真浏览器。性能测试用 JMeter,功能/UI 测试用 Selenium。

附:示例测试计划

仓库中附带 `examples/demo-api.jmx`:

  • 线程组:10 个线程,5 秒 Ramp-Up,循环 3 次
  • HTTP 请求:GET https://httpbin.org/get?name=${__threadNum}
  • 断言:响应包含 "args",且响应时间小于 5000ms
  • 监听器:View Results Tree + Summary Report

运行方式:

jmeter -n -t examples/demo-api.jmx -l result.jtl -e -o report

▍ 注意:httpbin.org 为公网免费测试服务,网络波动可能影响结果,仅用于学习。

参考资源

相关推荐
行者-全栈开发20 小时前
JMeter + InfluxDB + Grafana:搭建生产级压测实时监控与告警体系
jmeter·grafana·时序数据库·influxdb·实时监控·backendlistener·告警性能测试
wuhuhuan2 天前
【JMeter 学习打卡 Day 7】从会用到会用对,完成一次完整压测
学习·jmeter
行者-全栈开发4 天前
JMeter 自定义 Sampler 开发:从零写 TCP 私有协议压测插件
jmeter·tcp协议·源码解析·插件开发·gradle构建·自定义sampler·abstractsampler
wuhuhuan4 天前
【JMeter 学习打卡 Day 5】控制流程和节奏的利器
学习·jmeter
wuhuhuan5 天前
【JMeter 学习打卡 Day 4】多用户登录 + 动态 token 关联
学习·jmeter
wuhuhuan5 天前
jmeter高级用法-ForEach控制器和BeanShell断言
jmeter
weixin_440730509 天前
手工接口测试使用总结
jmeter·接口测试
Amir_zy9 天前
JMeter吞吐量与并发数:要求TPS达到200,并发至少是多少?
jmeter
孫治AllenSun14 天前
【Jmeter】项目压测流程案例
jmeter