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,pass789

JMeter 配置:

  • 文件名: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=120

HTML 报告位于 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 / P9990%/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/

相关链接