JMeter
Apache JMeter 是开源的性能测试工具,用于模拟多用户并发请求,测量系统在压力下的吞吐量、响应时间和错误率,帮助发现性能瓶颈。
核心概念
测试计划(Test Plan)
├── 线程组(Thread Group) ← 模拟并发用户
│ ├── HTTP 请求采样器 ← 发送一个请求
│ ├── CSV 数据文件配置 ← 参数化数据
│ ├── HTTP Header 管理器 ← 请求头(Authorization、Content-Type)
│ ├── 响应断言 ← 验证返回内容
│ └── 监听器(Listener) ← 收集结果
└── 配置元件
├── HTTP Cookie 管理器
└── HTTP 请求默认值 ← 复用相同的 host/port
线程组配置
| 参数 | 含义 |
|---|---|
| 线程数(Number of Threads) | 模拟的并发用户数 |
| Ramp-Up 时间 | 启动所有线程花多少秒(避免瞬间冲击) |
| 循环次数(Loop Count) | 每个线程执行几次,-1 = 永远 |
| 持续时间(Duration) | 以秒为单位持续压测(与循环次数二选一) |
典型配置示例(阶梯加压):
阶段 1:10 用户 × 60s(热身)
阶段 2:50 用户 × 60s(正常负载)
阶段 3:200 用户 × 60s(峰值压测)
阶段 4:500 用户 × 30s(破坏性测试)
JMeter 3.0+ 支持 Ultimate Thread Group(插件)实现阶梯加压。
HTTP 请求采样器
GET 请求:
Protocol: https
Server Name: api.example.com
Port: 443
HTTP Request: GET
Path: /api/users/${userId}
POST JSON 请求:
HTTP Request: POST
Path: /api/orders
Body Data:
{
"userId": ${userId},
"productId": ${productId},
"quantity": 1
}
Header 管理器:
Content-Type: application/json
Authorization: Bearer ${token}
CSV 数据文件(参数化)
将测试数据写入 CSV,避免重复用相同数据:
# users.csv
userId,username,password
1,alice,pass123
2,bob,pass456
3,carol,pass789JMeter 配置:
- 文件名:
users.csv - 变量名称:
userId,username,password - 分隔符:
, - 是否循环:
true(CSV 读完后从头开始)
请求中用 ${userId} 引用。
响应断言
验证接口返回正确:
响应断言
├── 响应代码 = 200
├── 响应消息:包含 "success"
└── JSON 断言:$.code == 0
JSON 断言(需 JSON Extractor 插件):
JSON Path: $.code
预期值: 0
登录获取 Token(关联提取)
先登录获取 token,后续请求自动带上:
1. HTTP 请求:POST /api/auth/login
Body: {"username":"alice","password":"pass123"}
2. JSON 提取器(Post Processor)
变量名: token
JSON Path: $.data.accessToken
3. HTTP Header 管理器(后续请求)
Authorization: Bearer ${token}
命令行运行(无 GUI)
GUI 模式仅用于调试,正式压测用命令行(GUI 本身消耗大量资源):
# 运行测试计划,生成 JTL 结果文件
jmeter -n -t test-plan.jmx -l result.jtl -e -o report/
# 参数说明
# -n 非 GUI 模式
# -t 测试计划文件
# -l 结果文件(JTL 格式)
# -e 生成 HTML 报告
# -o 报告输出目录(必须为空目录)
# 覆盖测试计划中的属性(动态调整线程数)
jmeter -n -t test-plan.jmx -l result.jtl \
-Jthreads=100 \
-Jrampup=30 \
-Jduration=120HTML 报告位于 report/index.html,包含响应时间曲线、吞吐量、百分位分布等图表。
Maven 集成
<plugin>
<groupId>com.lazerycode.jmeter</groupId>
<artifactId>jmeter-maven-plugin</artifactId>
<version>3.8.0</version>
<configuration>
<testFilesDirectory>${project.basedir}/src/test/jmeter</testFilesDirectory>
<resultsDirectory>${project.build.directory}/jmeter/results</resultsDirectory>
<generateReports>true</generateReports>
<!-- 覆盖线程数等参数 -->
<propertiesJMeter>
<threads>50</threads>
<rampup>30</rampup>
<duration>60</duration>
</propertiesJMeter>
<!-- 错误率超过 1% 则构建失败 -->
<errorRateThresholdInPercent>1</errorRateThresholdInPercent>
</configuration>
<executions>
<execution>
<id>jmeter-tests</id>
<phase>verify</phase>
<goals>
<goal>configure</goal>
<goal>jmeter</goal>
<goal>results</goal>
</goals>
</execution>
</executions>
</plugin>mvn verify -Pperformance # 在 performance Profile 下运行压测关键性能指标
| 指标 | 含义 | 参考标准 |
|---|---|---|
| 吞吐量(TPS/RPS) | 每秒完成的事务/请求数 | 越高越好 |
| 平均响应时间 | 所有请求的平均耗时 | 越低越好 |
| P90 / P95 / P99 | 90%/95%/99% 的请求在此时间内完成 | P99 < 1s 为常见目标 |
| 错误率 | 失败请求占总请求的比例 | < 1% 为可接受 |
| 最大响应时间 | 最慢的那次请求 | 避免长尾 |
压测结果分析思路
错误率高?
├── 接口报 500 → 查应用日志,是 OOM / 线程池耗尽 / 数据库连接不够?
├── 接口报 503 → 限流触发 or 连接队列满
└── 断言失败 → 业务逻辑问题
响应时间慢?
├── 数据库慢查询 → slow query log + explain
├── 连接池不够 → 增大 maximumPoolSize(见 [[连接池配置]])
├── GC 停顿 → jstat / JVM GC 日志
└── 外部调用慢 → 链路追踪定位(见 [[链路追踪]])
TPS 上不去?
├── CPU 打满 → 减少计算量 or 水平扩容
├── 锁竞争 → jstack 分析线程状态
└── 网络带宽瓶颈 → 压测机 vs 被压测机分离
分布式压测
单机 JMeter 线程有限,大规模压测需多 Controller + Slave 模式:
JMeter Controller(主控)
│ RMI 协议分发任务
├── JMeter Slave 1(云主机)
├── JMeter Slave 2(云主机)
└── JMeter Slave 3(云主机)
│
汇总结果 → Controller 生成统一报告
# Slave 启动(每台执行)
jmeter-server -Djava.rmi.server.hostname=<slave_ip>
# Controller 运行(指定 Slave 列表)
jmeter -n -t test-plan.jmx -R slave1_ip,slave2_ip,slave3_ip \
-l result.jtl -e -o report/