性能测试从入门到精通 JMeter完全指南(下)

Apache JMeter 性能测试完整教程

Head First 风格 | 从零基础到压测高手

想象你开了一家火爆的餐厅------如果同时涌入 1000 个顾客,你的厨房还能hold住吗?JMeter 就是那个帮你"模拟千军万马"的超级工具!


目录

  • [第1章 JMeter 简介](#第1章 JMeter 简介)
  • [第2章 安装与环境配置](#第2章 安装与环境配置)
  • [第3章 核心概念与术语](#第3章 核心概念与术语)
  • [第4章 JMeter 元件详解](#第4章 JMeter 元件详解)
  • [第5章 脚本编写实战](#第5章 脚本编写实战)
  • [第6章 完整电商系统压测Demo](#第6章 完整电商系统压测Demo)
  • [第7章 JMeter 分布式压测](#第7章 JMeter 分布式压测)
  • [第8章 常用插件介绍](#第8章 常用插件介绍)
  • [第9章 命令行执行与CI/CD集成](#第9章 命令行执行与CI/CD集成)
  • [第10章 最佳实践与常见问题](#第10章 最佳实践与常见问题)

第5章 脚本编写实战

5.1 HTTP 请求测试基础

实战场景:测试一个用户登录接口
#mermaid-svg-BY52Y0tfqihlrUt0{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-BY52Y0tfqihlrUt0 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-BY52Y0tfqihlrUt0 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-BY52Y0tfqihlrUt0 .error-icon{fill:#552222;}#mermaid-svg-BY52Y0tfqihlrUt0 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-BY52Y0tfqihlrUt0 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-BY52Y0tfqihlrUt0 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-BY52Y0tfqihlrUt0 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-BY52Y0tfqihlrUt0 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-BY52Y0tfqihlrUt0 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-BY52Y0tfqihlrUt0 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-BY52Y0tfqihlrUt0 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-BY52Y0tfqihlrUt0 .marker.cross{stroke:#333333;}#mermaid-svg-BY52Y0tfqihlrUt0 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-BY52Y0tfqihlrUt0 p{margin:0;}#mermaid-svg-BY52Y0tfqihlrUt0 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-BY52Y0tfqihlrUt0 .cluster-label text{fill:#333;}#mermaid-svg-BY52Y0tfqihlrUt0 .cluster-label span{color:#333;}#mermaid-svg-BY52Y0tfqihlrUt0 .cluster-label span p{background-color:transparent;}#mermaid-svg-BY52Y0tfqihlrUt0 .label text,#mermaid-svg-BY52Y0tfqihlrUt0 span{fill:#333;color:#333;}#mermaid-svg-BY52Y0tfqihlrUt0 .node rect,#mermaid-svg-BY52Y0tfqihlrUt0 .node circle,#mermaid-svg-BY52Y0tfqihlrUt0 .node ellipse,#mermaid-svg-BY52Y0tfqihlrUt0 .node polygon,#mermaid-svg-BY52Y0tfqihlrUt0 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-BY52Y0tfqihlrUt0 .rough-node .label text,#mermaid-svg-BY52Y0tfqihlrUt0 .node .label text,#mermaid-svg-BY52Y0tfqihlrUt0 .image-shape .label,#mermaid-svg-BY52Y0tfqihlrUt0 .icon-shape .label{text-anchor:middle;}#mermaid-svg-BY52Y0tfqihlrUt0 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-BY52Y0tfqihlrUt0 .rough-node .label,#mermaid-svg-BY52Y0tfqihlrUt0 .node .label,#mermaid-svg-BY52Y0tfqihlrUt0 .image-shape .label,#mermaid-svg-BY52Y0tfqihlrUt0 .icon-shape .label{text-align:center;}#mermaid-svg-BY52Y0tfqihlrUt0 .node.clickable{cursor:pointer;}#mermaid-svg-BY52Y0tfqihlrUt0 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-BY52Y0tfqihlrUt0 .arrowheadPath{fill:#333333;}#mermaid-svg-BY52Y0tfqihlrUt0 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-BY52Y0tfqihlrUt0 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-BY52Y0tfqihlrUt0 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-BY52Y0tfqihlrUt0 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-BY52Y0tfqihlrUt0 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-BY52Y0tfqihlrUt0 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-BY52Y0tfqihlrUt0 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-BY52Y0tfqihlrUt0 .cluster text{fill:#333;}#mermaid-svg-BY52Y0tfqihlrUt0 .cluster span{color:#333;}#mermaid-svg-BY52Y0tfqihlrUt0 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-BY52Y0tfqihlrUt0 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-BY52Y0tfqihlrUt0 rect.text{fill:none;stroke-width:0;}#mermaid-svg-BY52Y0tfqihlrUt0 .icon-shape,#mermaid-svg-BY52Y0tfqihlrUt0 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-BY52Y0tfqihlrUt0 .icon-shape p,#mermaid-svg-BY52Y0tfqihlrUt0 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-BY52Y0tfqihlrUt0 .icon-shape .label rect,#mermaid-svg-BY52Y0tfqihlrUt0 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-BY52Y0tfqihlrUt0 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-BY52Y0tfqihlrUt0 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-BY52Y0tfqihlrUt0 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 成功
失败
创建 Test Plan
添加 Thread Group
配置 Thread Group 参数
添加 HTTP Request Default
添加 HTTP Header Manager
添加第一个 HTTP Request
添加 View Results Tree
运行测试
检查结果
🎉 基础脚本完成!
调试排查

Step 1: 创建测试计划
复制代码
操作步骤:
1. 启动 JMeter
2. 左侧面板 → 右键 Test Plan → Add → Threads (Users) → Thread Group
3. 配置 Thread Group:
   - Number of Threads: 5(5个虚拟用户)
   - Ramp-up Period: 5(5秒内启动)
   - Loop Count: 1(执行1次)
Step 2: 添加 HTTP 请求
复制代码
操作步骤:
1. 右键 Thread Group → Add → Sampler → HTTP Request
2. 配置参数:
   - Name: 用户登录
   - Server Name: api.demo.com
   - Protocol: https
   - Port: 443
   - Method: POST
   - Path: /api/v1/auth/login
   - Body Data:
     {
       "username": "testuser",
       "password": "Test@12345"
     }
Step 3: 添加监听器
复制代码
操作步骤:
1. 右键 Thread Group → Add → Listener → View Results Tree
2. 运行测试(Ctrl+R 或点击绿色启动按钮)
3. 在 View Results Tree 中查看请求和响应

5.2 参数化(数据驱动测试)

餐厅比喻 :参数化就像让不同的顾客点不同的菜------而不是所有顾客都点同一道菜。每个顾客从花名册(CSV文件)中读取自己的信息。
#mermaid-svg-8hfKVHc8BCOyFWH7{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-8hfKVHc8BCOyFWH7 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-8hfKVHc8BCOyFWH7 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-8hfKVHc8BCOyFWH7 .error-icon{fill:#552222;}#mermaid-svg-8hfKVHc8BCOyFWH7 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-8hfKVHc8BCOyFWH7 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-8hfKVHc8BCOyFWH7 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-8hfKVHc8BCOyFWH7 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-8hfKVHc8BCOyFWH7 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-8hfKVHc8BCOyFWH7 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-8hfKVHc8BCOyFWH7 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-8hfKVHc8BCOyFWH7 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-8hfKVHc8BCOyFWH7 .marker.cross{stroke:#333333;}#mermaid-svg-8hfKVHc8BCOyFWH7 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-8hfKVHc8BCOyFWH7 p{margin:0;}#mermaid-svg-8hfKVHc8BCOyFWH7 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-8hfKVHc8BCOyFWH7 .cluster-label text{fill:#333;}#mermaid-svg-8hfKVHc8BCOyFWH7 .cluster-label span{color:#333;}#mermaid-svg-8hfKVHc8BCOyFWH7 .cluster-label span p{background-color:transparent;}#mermaid-svg-8hfKVHc8BCOyFWH7 .label text,#mermaid-svg-8hfKVHc8BCOyFWH7 span{fill:#333;color:#333;}#mermaid-svg-8hfKVHc8BCOyFWH7 .node rect,#mermaid-svg-8hfKVHc8BCOyFWH7 .node circle,#mermaid-svg-8hfKVHc8BCOyFWH7 .node ellipse,#mermaid-svg-8hfKVHc8BCOyFWH7 .node polygon,#mermaid-svg-8hfKVHc8BCOyFWH7 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-8hfKVHc8BCOyFWH7 .rough-node .label text,#mermaid-svg-8hfKVHc8BCOyFWH7 .node .label text,#mermaid-svg-8hfKVHc8BCOyFWH7 .image-shape .label,#mermaid-svg-8hfKVHc8BCOyFWH7 .icon-shape .label{text-anchor:middle;}#mermaid-svg-8hfKVHc8BCOyFWH7 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-8hfKVHc8BCOyFWH7 .rough-node .label,#mermaid-svg-8hfKVHc8BCOyFWH7 .node .label,#mermaid-svg-8hfKVHc8BCOyFWH7 .image-shape .label,#mermaid-svg-8hfKVHc8BCOyFWH7 .icon-shape .label{text-align:center;}#mermaid-svg-8hfKVHc8BCOyFWH7 .node.clickable{cursor:pointer;}#mermaid-svg-8hfKVHc8BCOyFWH7 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-8hfKVHc8BCOyFWH7 .arrowheadPath{fill:#333333;}#mermaid-svg-8hfKVHc8BCOyFWH7 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-8hfKVHc8BCOyFWH7 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-8hfKVHc8BCOyFWH7 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-8hfKVHc8BCOyFWH7 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-8hfKVHc8BCOyFWH7 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-8hfKVHc8BCOyFWH7 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-8hfKVHc8BCOyFWH7 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-8hfKVHc8BCOyFWH7 .cluster text{fill:#333;}#mermaid-svg-8hfKVHc8BCOyFWH7 .cluster span{color:#333;}#mermaid-svg-8hfKVHc8BCOyFWH7 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-8hfKVHc8BCOyFWH7 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-8hfKVHc8BCOyFWH7 rect.text{fill:none;stroke-width:0;}#mermaid-svg-8hfKVHc8BCOyFWH7 .icon-shape,#mermaid-svg-8hfKVHc8BCOyFWH7 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-8hfKVHc8BCOyFWH7 .icon-shape p,#mermaid-svg-8hfKVHc8BCOyFWH7 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-8hfKVHc8BCOyFWH7 .icon-shape .label rect,#mermaid-svg-8hfKVHc8BCOyFWH7 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-8hfKVHc8BCOyFWH7 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-8hfKVHc8BCOyFWH7 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-8hfKVHc8BCOyFWH7 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 参数化流程
准备CSV数据文件
添加CSV Data Set Config
在请求中使用变量
运行测试
每个用户使用不同数据

实战:登录接口参数化

1. 准备数据文件 (login_data.csv):

csv 复制代码
username,password,expected_code
admin,Admin@123,200
user01,User01@123,200
user02,User02@123,200
invalid,,401
locked,Locked@123,403

2. 添加 CSV Data Set Config:

复制代码
右键 Thread Group → Add → Config Element → CSV Data Set Config
配置:
- Filename: login_data.csv
- Variable Names: username,password,expected_code
- Delimiter: ,
- Recycle: true(数据用完后循环)
- Stop Thread: false

3. 在 HTTP Request 中使用变量:

json 复制代码
{
  "username": "${username}",
  "password": "${password}"
}

4. 添加断言验证:

复制代码
右键 HTTP Request → Add → Assertion → Response Assertion
- Field to Check: Response Data(响应数据)
- Pattern Match Rules: Contains
- Patterns to Test: "code":${expected_code}

5.3 关联(Correlation)

餐厅比喻 :关联就像用会员卡点菜------你先办会员卡(登录获取Token),然后每次点菜都要出示这张卡(在后续请求中携带Token)。如果卡丢了,你就点不了菜了。
服务器 虚拟用户 服务器 虚拟用户 #mermaid-svg-D2ocCSk76FmAT4ts{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-D2ocCSk76FmAT4ts .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-D2ocCSk76FmAT4ts .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-D2ocCSk76FmAT4ts .error-icon{fill:#552222;}#mermaid-svg-D2ocCSk76FmAT4ts .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-D2ocCSk76FmAT4ts .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-D2ocCSk76FmAT4ts .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-D2ocCSk76FmAT4ts .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-D2ocCSk76FmAT4ts .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-D2ocCSk76FmAT4ts .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-D2ocCSk76FmAT4ts .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-D2ocCSk76FmAT4ts .marker{fill:#333333;stroke:#333333;}#mermaid-svg-D2ocCSk76FmAT4ts .marker.cross{stroke:#333333;}#mermaid-svg-D2ocCSk76FmAT4ts svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-D2ocCSk76FmAT4ts p{margin:0;}#mermaid-svg-D2ocCSk76FmAT4ts .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-D2ocCSk76FmAT4ts text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-D2ocCSk76FmAT4ts .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-D2ocCSk76FmAT4ts .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-D2ocCSk76FmAT4ts .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-D2ocCSk76FmAT4ts .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-D2ocCSk76FmAT4ts #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-D2ocCSk76FmAT4ts .sequenceNumber{fill:white;}#mermaid-svg-D2ocCSk76FmAT4ts #sequencenumber{fill:#333;}#mermaid-svg-D2ocCSk76FmAT4ts #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-D2ocCSk76FmAT4ts .messageText{fill:#333;stroke:none;}#mermaid-svg-D2ocCSk76FmAT4ts .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-D2ocCSk76FmAT4ts .labelText,#mermaid-svg-D2ocCSk76FmAT4ts .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-D2ocCSk76FmAT4ts .loopText,#mermaid-svg-D2ocCSk76FmAT4ts .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-D2ocCSk76FmAT4ts .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-D2ocCSk76FmAT4ts .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-D2ocCSk76FmAT4ts .noteText,#mermaid-svg-D2ocCSk76FmAT4ts .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-D2ocCSk76FmAT4ts .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-D2ocCSk76FmAT4ts .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-D2ocCSk76FmAT4ts .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-D2ocCSk76FmAT4ts .actorPopupMenu{position:absolute;}#mermaid-svg-D2ocCSk76FmAT4ts .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-D2ocCSk76FmAT4ts .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-D2ocCSk76FmAT4ts .actor-man circle,#mermaid-svg-D2ocCSk76FmAT4ts line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-D2ocCSk76FmAT4ts :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 提取 token → 存入变量 1. POST /api/login {username, password} 2. 返回 {token: "abc123xyz"} 3. GET /api/orders Header: Authorization: Bearer abc123xyz 4. 返回订单列表 5. POST /api/orders Header: Authorization: Bearer abc123xyz {productId, quantity} 6. 返回创建的订单

实战:登录后获取 Token 并在后续请求中使用

Step 1: 登录请求 → 提取 Token

复制代码
添加 Post-Processor → JSON Extractor
- Reference Name: token
- JSON Path: $.data.accessToken
- Match Numbers: 1
- Default Value: TOKEN_NOT_FOUND

Step 2: 在后续请求中使用 Token

复制代码
添加 HTTP Header Manager(放在 Thread Group 级别)
- Name: Authorization
- Value: Bearer ${token}

Step 3: 提取更多变量(如用户ID)

复制代码
添加 JSON Extractor
- Reference Name: userId
- JSON Path: $.data.user.id

Step 4: 在后续请求中使用 userId

复制代码
GET /api/users/${userId}/profile
常见关联场景
场景 提取方式 提取内容
登录获取Token JSON Extractor $.data.token
获取动态ID JSON Extractor $.data.orderId
从CSRF Token Regex Extractor <meta name="csrf" content="(.+?)">
分页参数 JSON Extractor $.data.nextPageToken
Session ID Regex Extractor JSESSIONID=(\w+)

5.4 断言(验证响应)

餐厅比喻 :断言就像美食评论家------每次上菜后都要评价:这道菜是不是我点的?温度对不对?分量够不够?
#mermaid-svg-TGiPIZq3upLUpBvZ{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-TGiPIZq3upLUpBvZ .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-TGiPIZq3upLUpBvZ .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-TGiPIZq3upLUpBvZ .error-icon{fill:#552222;}#mermaid-svg-TGiPIZq3upLUpBvZ .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-TGiPIZq3upLUpBvZ .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-TGiPIZq3upLUpBvZ .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-TGiPIZq3upLUpBvZ .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-TGiPIZq3upLUpBvZ .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-TGiPIZq3upLUpBvZ .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-TGiPIZq3upLUpBvZ .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-TGiPIZq3upLUpBvZ .marker{fill:#333333;stroke:#333333;}#mermaid-svg-TGiPIZq3upLUpBvZ .marker.cross{stroke:#333333;}#mermaid-svg-TGiPIZq3upLUpBvZ svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-TGiPIZq3upLUpBvZ p{margin:0;}#mermaid-svg-TGiPIZq3upLUpBvZ .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-TGiPIZq3upLUpBvZ .cluster-label text{fill:#333;}#mermaid-svg-TGiPIZq3upLUpBvZ .cluster-label span{color:#333;}#mermaid-svg-TGiPIZq3upLUpBvZ .cluster-label span p{background-color:transparent;}#mermaid-svg-TGiPIZq3upLUpBvZ .label text,#mermaid-svg-TGiPIZq3upLUpBvZ span{fill:#333;color:#333;}#mermaid-svg-TGiPIZq3upLUpBvZ .node rect,#mermaid-svg-TGiPIZq3upLUpBvZ .node circle,#mermaid-svg-TGiPIZq3upLUpBvZ .node ellipse,#mermaid-svg-TGiPIZq3upLUpBvZ .node polygon,#mermaid-svg-TGiPIZq3upLUpBvZ .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-TGiPIZq3upLUpBvZ .rough-node .label text,#mermaid-svg-TGiPIZq3upLUpBvZ .node .label text,#mermaid-svg-TGiPIZq3upLUpBvZ .image-shape .label,#mermaid-svg-TGiPIZq3upLUpBvZ .icon-shape .label{text-anchor:middle;}#mermaid-svg-TGiPIZq3upLUpBvZ .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-TGiPIZq3upLUpBvZ .rough-node .label,#mermaid-svg-TGiPIZq3upLUpBvZ .node .label,#mermaid-svg-TGiPIZq3upLUpBvZ .image-shape .label,#mermaid-svg-TGiPIZq3upLUpBvZ .icon-shape .label{text-align:center;}#mermaid-svg-TGiPIZq3upLUpBvZ .node.clickable{cursor:pointer;}#mermaid-svg-TGiPIZq3upLUpBvZ .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-TGiPIZq3upLUpBvZ .arrowheadPath{fill:#333333;}#mermaid-svg-TGiPIZq3upLUpBvZ .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-TGiPIZq3upLUpBvZ .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-TGiPIZq3upLUpBvZ .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-TGiPIZq3upLUpBvZ .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-TGiPIZq3upLUpBvZ .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-TGiPIZq3upLUpBvZ .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-TGiPIZq3upLUpBvZ .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-TGiPIZq3upLUpBvZ .cluster text{fill:#333;}#mermaid-svg-TGiPIZq3upLUpBvZ .cluster span{color:#333;}#mermaid-svg-TGiPIZq3upLUpBvZ div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-TGiPIZq3upLUpBvZ .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-TGiPIZq3upLUpBvZ rect.text{fill:none;stroke-width:0;}#mermaid-svg-TGiPIZq3upLUpBvZ .icon-shape,#mermaid-svg-TGiPIZq3upLUpBvZ .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-TGiPIZq3upLUpBvZ .icon-shape p,#mermaid-svg-TGiPIZq3upLUpBvZ .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-TGiPIZq3upLUpBvZ .icon-shape .label rect,#mermaid-svg-TGiPIZq3upLUpBvZ .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-TGiPIZq3upLUpBvZ .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-TGiPIZq3upLUpBvZ .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-TGiPIZq3upLUpBvZ :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 断言策略
状态码断言

= 检查有没有上菜
HTTP 200/201/204
业务码断言

= 检查菜品对不对
code: 200
关键字断言

= 检查菜品里有没有关键食材
包含 'success'
JSON断言

= 精确检查菜品配方
$.data.status == 'active'
时间断言

= 检查上菜够不够快
响应时间 < 3000ms
大小断言

= 检查菜品分量
响应大小 > 100 bytes

实战:多层级断言
复制代码
场景:验证登录接口

断言1 - HTTP状态码断言
├── HTTP Response Assertion
├── Field to Check: Response Code
├── Pattern Match Rules: Equals
└── Pattern: 200

断言2 - 业务状态码断言
├── Response Assertion
├── Field to Check: Response Data
├── Pattern Match Rules: Contains
└── Pattern: "code":200

断言3 - 关键字段断言
├── JSON Assertion
├── Assert JSON Path: $.data.token
├── Expected Value: (不为空)
└── Additionally assert value: (勾选)

断言4 - 响应时间断言
├── Duration Assertion
└── Duration in milliseconds: 3000

5.5 定时器(控制请求频率)

餐厅比喻 :定时器就像餐厅的叫号系统------控制顾客多久点一次菜,避免所有人同时挤到柜台前。
#mermaid-svg-qEMJKXzncPahAuQP{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-qEMJKXzncPahAuQP .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-qEMJKXzncPahAuQP .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-qEMJKXzncPahAuQP .error-icon{fill:#552222;}#mermaid-svg-qEMJKXzncPahAuQP .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-qEMJKXzncPahAuQP .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-qEMJKXzncPahAuQP .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-qEMJKXzncPahAuQP .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-qEMJKXzncPahAuQP .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-qEMJKXzncPahAuQP .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-qEMJKXzncPahAuQP .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-qEMJKXzncPahAuQP .marker{fill:#333333;stroke:#333333;}#mermaid-svg-qEMJKXzncPahAuQP .marker.cross{stroke:#333333;}#mermaid-svg-qEMJKXzncPahAuQP svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-qEMJKXzncPahAuQP p{margin:0;}#mermaid-svg-qEMJKXzncPahAuQP .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-qEMJKXzncPahAuQP .cluster-label text{fill:#333;}#mermaid-svg-qEMJKXzncPahAuQP .cluster-label span{color:#333;}#mermaid-svg-qEMJKXzncPahAuQP .cluster-label span p{background-color:transparent;}#mermaid-svg-qEMJKXzncPahAuQP .label text,#mermaid-svg-qEMJKXzncPahAuQP span{fill:#333;color:#333;}#mermaid-svg-qEMJKXzncPahAuQP .node rect,#mermaid-svg-qEMJKXzncPahAuQP .node circle,#mermaid-svg-qEMJKXzncPahAuQP .node ellipse,#mermaid-svg-qEMJKXzncPahAuQP .node polygon,#mermaid-svg-qEMJKXzncPahAuQP .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-qEMJKXzncPahAuQP .rough-node .label text,#mermaid-svg-qEMJKXzncPahAuQP .node .label text,#mermaid-svg-qEMJKXzncPahAuQP .image-shape .label,#mermaid-svg-qEMJKXzncPahAuQP .icon-shape .label{text-anchor:middle;}#mermaid-svg-qEMJKXzncPahAuQP .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-qEMJKXzncPahAuQP .rough-node .label,#mermaid-svg-qEMJKXzncPahAuQP .node .label,#mermaid-svg-qEMJKXzncPahAuQP .image-shape .label,#mermaid-svg-qEMJKXzncPahAuQP .icon-shape .label{text-align:center;}#mermaid-svg-qEMJKXzncPahAuQP .node.clickable{cursor:pointer;}#mermaid-svg-qEMJKXzncPahAuQP .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-qEMJKXzncPahAuQP .arrowheadPath{fill:#333333;}#mermaid-svg-qEMJKXzncPahAuQP .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-qEMJKXzncPahAuQP .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-qEMJKXzncPahAuQP .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-qEMJKXzncPahAuQP .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-qEMJKXzncPahAuQP .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-qEMJKXzncPahAuQP .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-qEMJKXzncPahAuQP .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-qEMJKXzncPahAuQP .cluster text{fill:#333;}#mermaid-svg-qEMJKXzncPahAuQP .cluster span{color:#333;}#mermaid-svg-qEMJKXzncPahAuQP div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-qEMJKXzncPahAuQP .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-qEMJKXzncPahAuQP rect.text{fill:none;stroke-width:0;}#mermaid-svg-qEMJKXzncPahAuQP .icon-shape,#mermaid-svg-qEMJKXzncPahAuQP .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-qEMJKXzncPahAuQP .icon-shape p,#mermaid-svg-qEMJKXzncPahAuQP .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-qEMJKXzncPahAuQP .icon-shape .label rect,#mermaid-svg-qEMJKXzncPahAuQP .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-qEMJKXzncPahAuQP .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-qEMJKXzncPahAuQP .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-qEMJKXzncPahAuQP :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 没有定时器
所有用户同时发请求

= 开门瞬间所有人冲进来

不真实!
有定时器
用户按节奏发请求

= 顾客陆续到来

真实场景!

实战:配置合理的定时器
复制代码
场景:模拟真实用户浏览行为

推荐配置:
Thread Group → Add → Timer → Gaussian Random Timer
- Delay (deviation): 1000(毫秒)
- Range (offset): 2000(毫秒)
→ 实际等待时间 = 1000 + 随机(0~2000) 毫秒
→ 大部分请求间隔在 1000~3000ms 之间
→ 符合正态分布,模拟真实用户行为

不同场景的定时器推荐

场景 推荐定时器 配置
浏览页面 Gaussian Random Timer 1000~3000ms
提交表单 Uniform Random Timer 500~1500ms
API 调用 Constant Timer 100~500ms
突发压力 Synchronizing Timer 50个用户同步
精确吞吐量 Precise Throughput Timer 60次/分钟

5.6 综合实战脚本结构

#mermaid-svg-f7DTiUXhV6qMNtMh{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-f7DTiUXhV6qMNtMh .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-f7DTiUXhV6qMNtMh .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-f7DTiUXhV6qMNtMh .error-icon{fill:#552222;}#mermaid-svg-f7DTiUXhV6qMNtMh .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-f7DTiUXhV6qMNtMh .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-f7DTiUXhV6qMNtMh .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-f7DTiUXhV6qMNtMh .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-f7DTiUXhV6qMNtMh .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-f7DTiUXhV6qMNtMh .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-f7DTiUXhV6qMNtMh .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-f7DTiUXhV6qMNtMh .marker{fill:#333333;stroke:#333333;}#mermaid-svg-f7DTiUXhV6qMNtMh .marker.cross{stroke:#333333;}#mermaid-svg-f7DTiUXhV6qMNtMh svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-f7DTiUXhV6qMNtMh p{margin:0;}#mermaid-svg-f7DTiUXhV6qMNtMh .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-f7DTiUXhV6qMNtMh .cluster-label text{fill:#333;}#mermaid-svg-f7DTiUXhV6qMNtMh .cluster-label span{color:#333;}#mermaid-svg-f7DTiUXhV6qMNtMh .cluster-label span p{background-color:transparent;}#mermaid-svg-f7DTiUXhV6qMNtMh .label text,#mermaid-svg-f7DTiUXhV6qMNtMh span{fill:#333;color:#333;}#mermaid-svg-f7DTiUXhV6qMNtMh .node rect,#mermaid-svg-f7DTiUXhV6qMNtMh .node circle,#mermaid-svg-f7DTiUXhV6qMNtMh .node ellipse,#mermaid-svg-f7DTiUXhV6qMNtMh .node polygon,#mermaid-svg-f7DTiUXhV6qMNtMh .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-f7DTiUXhV6qMNtMh .rough-node .label text,#mermaid-svg-f7DTiUXhV6qMNtMh .node .label text,#mermaid-svg-f7DTiUXhV6qMNtMh .image-shape .label,#mermaid-svg-f7DTiUXhV6qMNtMh .icon-shape .label{text-anchor:middle;}#mermaid-svg-f7DTiUXhV6qMNtMh .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-f7DTiUXhV6qMNtMh .rough-node .label,#mermaid-svg-f7DTiUXhV6qMNtMh .node .label,#mermaid-svg-f7DTiUXhV6qMNtMh .image-shape .label,#mermaid-svg-f7DTiUXhV6qMNtMh .icon-shape .label{text-align:center;}#mermaid-svg-f7DTiUXhV6qMNtMh .node.clickable{cursor:pointer;}#mermaid-svg-f7DTiUXhV6qMNtMh .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-f7DTiUXhV6qMNtMh .arrowheadPath{fill:#333333;}#mermaid-svg-f7DTiUXhV6qMNtMh .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-f7DTiUXhV6qMNtMh .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-f7DTiUXhV6qMNtMh .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-f7DTiUXhV6qMNtMh .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-f7DTiUXhV6qMNtMh .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-f7DTiUXhV6qMNtMh .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-f7DTiUXhV6qMNtMh .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-f7DTiUXhV6qMNtMh .cluster text{fill:#333;}#mermaid-svg-f7DTiUXhV6qMNtMh .cluster span{color:#333;}#mermaid-svg-f7DTiUXhV6qMNtMh div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-f7DTiUXhV6qMNtMh .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-f7DTiUXhV6qMNtMh rect.text{fill:none;stroke-width:0;}#mermaid-svg-f7DTiUXhV6qMNtMh .icon-shape,#mermaid-svg-f7DTiUXhV6qMNtMh .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-f7DTiUXhV6qMNtMh .icon-shape p,#mermaid-svg-f7DTiUXhV6qMNtMh .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-f7DTiUXhV6qMNtMh .icon-shape .label rect,#mermaid-svg-f7DTiUXhV6qMNtMh .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-f7DTiUXhV6qMNtMh .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-f7DTiUXhV6qMNtMh .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-f7DTiUXhV6qMNtMh :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 完整的测试脚本结构
Test Plan

电商系统性能测试
User Defined Variables

baseUrl=https://api.shop.com

port=443
Thread Group

100 Users / 60s Ramp-up / 300s Duration
CSV Data Set Config

users.csv → username,password
HTTP Header Manager

Content-Type: application/json
Gaussian Random Timer

1000~3000ms
Transaction Controller

登录事务
POST /api/login

Body: {username, password}
JSON Extractor

$.data.token → token
JSON Extractor

$.data.userId → userId
Response Assertion

Contains: code:200
Transaction Controller

浏览商品事务
GET /api/products?page=1&size=20
JSON Extractor

$.data.totalPages → totalPages
Response Assertion

Contains: products
Transaction Controller

下单事务
POST /api/cart/add

Body: {productId, quantity}
POST /api/orders

Header: Bearer ${token}
JSON Assertion

$.data.orderId exists
Aggregate Report
Response Times Over Time
View Results Tree


第6章 完整电商系统压测 Demo

6.1 场景背景

故事背景:你是一家电商公司的测试工程师,"双11"大促即将来临。老板问你:"我们的系统能扛住 1000 个用户同时抢购吗?"你微微一笑,打开 JMeter...
#mermaid-svg-mC5cEBftnRwRYyHJ{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-mC5cEBftnRwRYyHJ .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-mC5cEBftnRwRYyHJ .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-mC5cEBftnRwRYyHJ .error-icon{fill:#552222;}#mermaid-svg-mC5cEBftnRwRYyHJ .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-mC5cEBftnRwRYyHJ .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-mC5cEBftnRwRYyHJ .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-mC5cEBftnRwRYyHJ .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-mC5cEBftnRwRYyHJ .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-mC5cEBftnRwRYyHJ .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-mC5cEBftnRwRYyHJ .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-mC5cEBftnRwRYyHJ .marker{fill:#333333;stroke:#333333;}#mermaid-svg-mC5cEBftnRwRYyHJ .marker.cross{stroke:#333333;}#mermaid-svg-mC5cEBftnRwRYyHJ svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-mC5cEBftnRwRYyHJ p{margin:0;}#mermaid-svg-mC5cEBftnRwRYyHJ .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-mC5cEBftnRwRYyHJ .cluster-label text{fill:#333;}#mermaid-svg-mC5cEBftnRwRYyHJ .cluster-label span{color:#333;}#mermaid-svg-mC5cEBftnRwRYyHJ .cluster-label span p{background-color:transparent;}#mermaid-svg-mC5cEBftnRwRYyHJ .label text,#mermaid-svg-mC5cEBftnRwRYyHJ span{fill:#333;color:#333;}#mermaid-svg-mC5cEBftnRwRYyHJ .node rect,#mermaid-svg-mC5cEBftnRwRYyHJ .node circle,#mermaid-svg-mC5cEBftnRwRYyHJ .node ellipse,#mermaid-svg-mC5cEBftnRwRYyHJ .node polygon,#mermaid-svg-mC5cEBftnRwRYyHJ .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-mC5cEBftnRwRYyHJ .rough-node .label text,#mermaid-svg-mC5cEBftnRwRYyHJ .node .label text,#mermaid-svg-mC5cEBftnRwRYyHJ .image-shape .label,#mermaid-svg-mC5cEBftnRwRYyHJ .icon-shape .label{text-anchor:middle;}#mermaid-svg-mC5cEBftnRwRYyHJ .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-mC5cEBftnRwRYyHJ .rough-node .label,#mermaid-svg-mC5cEBftnRwRYyHJ .node .label,#mermaid-svg-mC5cEBftnRwRYyHJ .image-shape .label,#mermaid-svg-mC5cEBftnRwRYyHJ .icon-shape .label{text-align:center;}#mermaid-svg-mC5cEBftnRwRYyHJ .node.clickable{cursor:pointer;}#mermaid-svg-mC5cEBftnRwRYyHJ .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-mC5cEBftnRwRYyHJ .arrowheadPath{fill:#333333;}#mermaid-svg-mC5cEBftnRwRYyHJ .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-mC5cEBftnRwRYyHJ .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-mC5cEBftnRwRYyHJ .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-mC5cEBftnRwRYyHJ .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-mC5cEBftnRwRYyHJ .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-mC5cEBftnRwRYyHJ .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-mC5cEBftnRwRYyHJ .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-mC5cEBftnRwRYyHJ .cluster text{fill:#333;}#mermaid-svg-mC5cEBftnRwRYyHJ .cluster span{color:#333;}#mermaid-svg-mC5cEBftnRwRYyHJ div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-mC5cEBftnRwRYyHJ .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-mC5cEBftnRwRYyHJ rect.text{fill:none;stroke-width:0;}#mermaid-svg-mC5cEBftnRwRYyHJ .icon-shape,#mermaid-svg-mC5cEBftnRwRYyHJ .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-mC5cEBftnRwRYyHJ .icon-shape p,#mermaid-svg-mC5cEBftnRwRYyHJ .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-mC5cEBftnRwRYyHJ .icon-shape .label rect,#mermaid-svg-mC5cEBftnRwRYyHJ .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-mC5cEBftnRwRYyHJ .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-mC5cEBftnRwRYyHJ .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-mC5cEBftnRwRYyHJ :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 电商系统架构
API Gateway

网关
User Service

用户服务
Product Service

商品服务
Order Service

订单服务
Payment Service

支付服务
Database

数据库
Redis

缓存
MQ

消息队列

6.2 测试计划设计

业务场景分析

#mermaid-svg-BtVQUdbkpMVqyXXX{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-BtVQUdbkpMVqyXXX .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-BtVQUdbkpMVqyXXX .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-BtVQUdbkpMVqyXXX .error-icon{fill:#552222;}#mermaid-svg-BtVQUdbkpMVqyXXX .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-BtVQUdbkpMVqyXXX .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-BtVQUdbkpMVqyXXX .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-BtVQUdbkpMVqyXXX .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-BtVQUdbkpMVqyXXX .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-BtVQUdbkpMVqyXXX .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-BtVQUdbkpMVqyXXX .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-BtVQUdbkpMVqyXXX .marker{fill:#333333;stroke:#333333;}#mermaid-svg-BtVQUdbkpMVqyXXX .marker.cross{stroke:#333333;}#mermaid-svg-BtVQUdbkpMVqyXXX svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-BtVQUdbkpMVqyXXX p{margin:0;}#mermaid-svg-BtVQUdbkpMVqyXXX .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-BtVQUdbkpMVqyXXX .cluster-label text{fill:#333;}#mermaid-svg-BtVQUdbkpMVqyXXX .cluster-label span{color:#333;}#mermaid-svg-BtVQUdbkpMVqyXXX .cluster-label span p{background-color:transparent;}#mermaid-svg-BtVQUdbkpMVqyXXX .label text,#mermaid-svg-BtVQUdbkpMVqyXXX span{fill:#333;color:#333;}#mermaid-svg-BtVQUdbkpMVqyXXX .node rect,#mermaid-svg-BtVQUdbkpMVqyXXX .node circle,#mermaid-svg-BtVQUdbkpMVqyXXX .node ellipse,#mermaid-svg-BtVQUdbkpMVqyXXX .node polygon,#mermaid-svg-BtVQUdbkpMVqyXXX .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-BtVQUdbkpMVqyXXX .rough-node .label text,#mermaid-svg-BtVQUdbkpMVqyXXX .node .label text,#mermaid-svg-BtVQUdbkpMVqyXXX .image-shape .label,#mermaid-svg-BtVQUdbkpMVqyXXX .icon-shape .label{text-anchor:middle;}#mermaid-svg-BtVQUdbkpMVqyXXX .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-BtVQUdbkpMVqyXXX .rough-node .label,#mermaid-svg-BtVQUdbkpMVqyXXX .node .label,#mermaid-svg-BtVQUdbkpMVqyXXX .image-shape .label,#mermaid-svg-BtVQUdbkpMVqyXXX .icon-shape .label{text-align:center;}#mermaid-svg-BtVQUdbkpMVqyXXX .node.clickable{cursor:pointer;}#mermaid-svg-BtVQUdbkpMVqyXXX .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-BtVQUdbkpMVqyXXX .arrowheadPath{fill:#333333;}#mermaid-svg-BtVQUdbkpMVqyXXX .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-BtVQUdbkpMVqyXXX .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-BtVQUdbkpMVqyXXX .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-BtVQUdbkpMVqyXXX .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-BtVQUdbkpMVqyXXX .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-BtVQUdbkpMVqyXXX .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-BtVQUdbkpMVqyXXX .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-BtVQUdbkpMVqyXXX .cluster text{fill:#333;}#mermaid-svg-BtVQUdbkpMVqyXXX .cluster span{color:#333;}#mermaid-svg-BtVQUdbkpMVqyXXX div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-BtVQUdbkpMVqyXXX .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-BtVQUdbkpMVqyXXX rect.text{fill:none;stroke-width:0;}#mermaid-svg-BtVQUdbkpMVqyXXX .icon-shape,#mermaid-svg-BtVQUdbkpMVqyXXX .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-BtVQUdbkpMVqyXXX .icon-shape p,#mermaid-svg-BtVQUdbkpMVqyXXX .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-BtVQUdbkpMVqyXXX .icon-shape .label rect,#mermaid-svg-BtVQUdbkpMVqyXXX .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-BtVQUdbkpMVqyXXX .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-BtVQUdbkpMVqyXXX .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-BtVQUdbkpMVqyXXX :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 电商系统压测场景
场景1: 用户登录

占比: 20%
场景2: 浏览商品

占比: 40%
场景3: 搜索商品

占比: 15%
场景4: 下单支付

占比: 15%
场景5: 查看订单

占比: 10%
POST /api/v1/auth/login
GET /api/v1/products
GET /api/v1/products/search
POST /api/v1/orders
GET /api/v1/orders

性能指标目标
接口 目标TPS 90%响应时间 错误率
登录 50 < 500ms < 0.1%
浏览商品 200 < 300ms < 0.1%
搜索商品 100 < 500ms < 0.5%
下单支付 80 < 1000ms < 0.1%
查看订单 100 < 200ms < 0.1%

6.3 创建测试脚本

Step 1: 全局配置
xml 复制代码
<!-- Test Plan 级别的变量 -->
<TestPlan>
  <stringProp name="TestPlan.comments">双11电商系统压测</stringProp>
  <boolProp name="TestPlan.functional_mode">false</boolProp>
  <boolProp name="TestPlan.serialize_threadgroups">false</boolProp>
</TestPlan>

<!-- User Defined Variables -->
<Arguments guiclass="ArgumentsPanel" testclass="Arguments" testname="全局变量">
  <collectionProp name="Arguments.arguments">
    <elementProp name="BASE_URL" elementType="Argument">
      <stringProp name="Argument.name">BASE_URL</stringProp>
      <stringProp name="Argument.value">api.shop.example.com</stringProp>
    </elementProp>
    <elementProp name="BASE_PORT" elementType="Argument">
      <stringProp name="Argument.name">BASE_PORT</stringProp>
      <stringProp name="Argument.value">443</stringProp>
    </elementProp>
    <elementProp name="BASE_PROTOCOL" elementType="Argument">
      <stringProp name="Argument.name">BASE_PROTOCOL</stringProp>
      <stringProp name="Argument.value">https</stringProp>
    </elementProp>
  </collectionProp>
</Arguments>
Step 2: 线程组配置
复制代码
Thread Group 配置:
- Name: 电商压测-1000用户
- Number of Threads: 1000
- Ramp-up Period: 120(2分钟内启动所有用户)
- Loop Count: Forever(永远循环)
- Scheduler: 勾选
- Duration: 600(运行10分钟)
- Startup Delay: 10(10秒后开始)
Step 3: 准备测试数据

用户数据 (test_users.csv):

csv 复制代码
username,password
buyer001,Buyer@001
buyer002,Buyer@002
buyer003,Buyer@003
buyer004,Buyer@004
buyer005,Buyer@005
...
buyer200,Buyer@200

商品数据 (test_products.csv):

csv 复制代码
product_id,product_name,price,category
1001,iPhone 15 Pro,8999,手机
1002,MacBook Pro,14999,电脑
1003,AirPods Pro,1899,配件
1004,iPad Air,4799,平板
1005,Apple Watch,2999,手表
...
Step 4: 完整脚本结构

#mermaid-svg-5WGBQ8pEM3DN8df9{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-5WGBQ8pEM3DN8df9 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-5WGBQ8pEM3DN8df9 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-5WGBQ8pEM3DN8df9 .error-icon{fill:#552222;}#mermaid-svg-5WGBQ8pEM3DN8df9 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-5WGBQ8pEM3DN8df9 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-5WGBQ8pEM3DN8df9 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-5WGBQ8pEM3DN8df9 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-5WGBQ8pEM3DN8df9 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-5WGBQ8pEM3DN8df9 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-5WGBQ8pEM3DN8df9 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-5WGBQ8pEM3DN8df9 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-5WGBQ8pEM3DN8df9 .marker.cross{stroke:#333333;}#mermaid-svg-5WGBQ8pEM3DN8df9 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-5WGBQ8pEM3DN8df9 p{margin:0;}#mermaid-svg-5WGBQ8pEM3DN8df9 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-5WGBQ8pEM3DN8df9 .cluster-label text{fill:#333;}#mermaid-svg-5WGBQ8pEM3DN8df9 .cluster-label span{color:#333;}#mermaid-svg-5WGBQ8pEM3DN8df9 .cluster-label span p{background-color:transparent;}#mermaid-svg-5WGBQ8pEM3DN8df9 .label text,#mermaid-svg-5WGBQ8pEM3DN8df9 span{fill:#333;color:#333;}#mermaid-svg-5WGBQ8pEM3DN8df9 .node rect,#mermaid-svg-5WGBQ8pEM3DN8df9 .node circle,#mermaid-svg-5WGBQ8pEM3DN8df9 .node ellipse,#mermaid-svg-5WGBQ8pEM3DN8df9 .node polygon,#mermaid-svg-5WGBQ8pEM3DN8df9 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-5WGBQ8pEM3DN8df9 .rough-node .label text,#mermaid-svg-5WGBQ8pEM3DN8df9 .node .label text,#mermaid-svg-5WGBQ8pEM3DN8df9 .image-shape .label,#mermaid-svg-5WGBQ8pEM3DN8df9 .icon-shape .label{text-anchor:middle;}#mermaid-svg-5WGBQ8pEM3DN8df9 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-5WGBQ8pEM3DN8df9 .rough-node .label,#mermaid-svg-5WGBQ8pEM3DN8df9 .node .label,#mermaid-svg-5WGBQ8pEM3DN8df9 .image-shape .label,#mermaid-svg-5WGBQ8pEM3DN8df9 .icon-shape .label{text-align:center;}#mermaid-svg-5WGBQ8pEM3DN8df9 .node.clickable{cursor:pointer;}#mermaid-svg-5WGBQ8pEM3DN8df9 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-5WGBQ8pEM3DN8df9 .arrowheadPath{fill:#333333;}#mermaid-svg-5WGBQ8pEM3DN8df9 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-5WGBQ8pEM3DN8df9 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-5WGBQ8pEM3DN8df9 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-5WGBQ8pEM3DN8df9 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-5WGBQ8pEM3DN8df9 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-5WGBQ8pEM3DN8df9 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-5WGBQ8pEM3DN8df9 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-5WGBQ8pEM3DN8df9 .cluster text{fill:#333;}#mermaid-svg-5WGBQ8pEM3DN8df9 .cluster span{color:#333;}#mermaid-svg-5WGBQ8pEM3DN8df9 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-5WGBQ8pEM3DN8df9 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-5WGBQ8pEM3DN8df9 rect.text{fill:none;stroke-width:0;}#mermaid-svg-5WGBQ8pEM3DN8df9 .icon-shape,#mermaid-svg-5WGBQ8pEM3DN8df9 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-5WGBQ8pEM3DN8df9 .icon-shape p,#mermaid-svg-5WGBQ8pEM3DN8df9 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-5WGBQ8pEM3DN8df9 .icon-shape .label rect,#mermaid-svg-5WGBQ8pEM3DN8df9 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-5WGBQ8pEM3DN8df9 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-5WGBQ8pEM3DN8df9 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-5WGBQ8pEM3DN8df9 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Test Plan: 电商系统压测
User Defined Variables

BASE_URL, BASE_PORT, BASE_PROTOCOL
Thread Group: 1000 Users
CSV Data Set Config

test_users.csv → username,password
CSV Data Set Config

test_products.csv → product_id,price
HTTP Header Manager

Content-Type: application/json

Accept: application/json
HTTP Cookie Manager
HTTP Cache Manager

清除缓存每迭代
Gaussian Random Timer

500~1500ms
Throughput Controller

控制各场景比例
场景1: 登录 (20%)
场景2: 浏览商品 (40%)
场景3: 搜索商品 (15%)
场景4: 下单 (15%)
场景5: 查看订单 (10%)

Step 5: 场景1 - 用户登录
复制代码
Transaction Controller: 登录事务
├── HTTP Request: 用户登录
│   ├── Method: POST
│   ├── Path: /api/v1/auth/login
│   ├── Body: {"username":"${username}","password":"${password}"}
│   └── Post-Processors:
│       ├── JSON Extractor: token → $.data.accessToken
│       └── JSON Extractor: userId → $.data.user.id
├── Response Assertion: 验证登录成功
│   └── Contains: "code":200
└── Duration Assertion: 响应时间 < 500ms

Groovy 脚本增强(JSR223 PreProcessor):

groovy 复制代码
// 生成随机设备信息
import java.util.Random

def random = new Random()
def devices = ["iPhone 15", "Samsung S24", "Pixel 8", "Xiaomi 14"]
def osVersions = ["iOS 17.0", "Android 14", "iOS 16.5", "Android 13"]

def device = devices[random.nextInt(devices.size())]
def os = osVersions[random.nextInt(osVersions.size())]

vars.put("deviceInfo", device)
vars.put("osVersion", os)
vars.put("requestId", UUID.randomUUID().toString())
Step 6: 场景2 - 浏览商品
复制代码
Transaction Controller: 浏览商品事务
├── HTTP Request: 获取商品列表
│   ├── Method: GET
│   ├── Path: /api/v1/products?page=${pageNum}&size=20&category=${category}
│   └── Post-Processors:
│       └── JSON Extractor: totalPages → $.data.pagination.totalPages
├── Throughput Controller: 50% 概率翻页
│   └── Loop Controller: 循环 3 次
│       ├── JSR223 PreProcessor: pageNum++
│       └── HTTP Request: 下一页
│           ├── Method: GET
│           └── Path: /api/v1/products?page=${pageNum}&size=20
└── Response Assertion: Contains "products"
Step 7: 场景4 - 下单支付

支付系统 消息队列 数据库 电商API 虚拟用户 支付系统 消息队列 数据库 电商API 虚拟用户 #mermaid-svg-p3zs5UxkUjJpw2sn{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-p3zs5UxkUjJpw2sn .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-p3zs5UxkUjJpw2sn .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-p3zs5UxkUjJpw2sn .error-icon{fill:#552222;}#mermaid-svg-p3zs5UxkUjJpw2sn .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-p3zs5UxkUjJpw2sn .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-p3zs5UxkUjJpw2sn .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-p3zs5UxkUjJpw2sn .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-p3zs5UxkUjJpw2sn .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-p3zs5UxkUjJpw2sn .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-p3zs5UxkUjJpw2sn .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-p3zs5UxkUjJpw2sn .marker{fill:#333333;stroke:#333333;}#mermaid-svg-p3zs5UxkUjJpw2sn .marker.cross{stroke:#333333;}#mermaid-svg-p3zs5UxkUjJpw2sn svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-p3zs5UxkUjJpw2sn p{margin:0;}#mermaid-svg-p3zs5UxkUjJpw2sn .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-p3zs5UxkUjJpw2sn text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-p3zs5UxkUjJpw2sn .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-p3zs5UxkUjJpw2sn .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-p3zs5UxkUjJpw2sn .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-p3zs5UxkUjJpw2sn .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-p3zs5UxkUjJpw2sn #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-p3zs5UxkUjJpw2sn .sequenceNumber{fill:white;}#mermaid-svg-p3zs5UxkUjJpw2sn #sequencenumber{fill:#333;}#mermaid-svg-p3zs5UxkUjJpw2sn #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-p3zs5UxkUjJpw2sn .messageText{fill:#333;stroke:none;}#mermaid-svg-p3zs5UxkUjJpw2sn .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-p3zs5UxkUjJpw2sn .labelText,#mermaid-svg-p3zs5UxkUjJpw2sn .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-p3zs5UxkUjJpw2sn .loopText,#mermaid-svg-p3zs5UxkUjJpw2sn .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-p3zs5UxkUjJpw2sn .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-p3zs5UxkUjJpw2sn .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-p3zs5UxkUjJpw2sn .noteText,#mermaid-svg-p3zs5UxkUjJpw2sn .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-p3zs5UxkUjJpw2sn .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-p3zs5UxkUjJpw2sn .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-p3zs5UxkUjJpw2sn .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-p3zs5UxkUjJpw2sn .actorPopupMenu{position:absolute;}#mermaid-svg-p3zs5UxkUjJpw2sn .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-p3zs5UxkUjJpw2sn .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-p3zs5UxkUjJpw2sn .actor-man circle,#mermaid-svg-p3zs5UxkUjJpw2sn line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-p3zs5UxkUjJpw2sn :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 1. POST /api/v1/cart/add {productId, quantity} 2. 保存购物车 3. 返回购物车信息 4. POST /api/v1/orders/create Header: Bearer {token} {items, address} 5. 创建订单 6. 发送订单事件 7. 返回订单号 orderId 8. POST /api/v1/payments Header: Bearer {token} {orderId, payMethod} 9. 调用支付 10. 支付成功 11. 返回支付结果

复制代码
Transaction Controller: 下单支付事务
├── HTTP Request: 加入购物车
│   ├── Method: POST
│   ├── Path: /api/v1/cart/add
│   ├── Header: Authorization: Bearer ${token}
│   └── Body: {"productId":${product_id},"quantity":1}
├── JSON Extractor: cartId → $.data.cartId
│
├── HTTP Request: 创建订单
│   ├── Method: POST
│   ├── Path: /api/v1/orders/create
│   ├── Header: Authorization: Bearer ${token}
│   └── Body: {"cartId":"${cartId}","addressId":"${addressId}"}
├── JSON Extractor: orderId → $.data.orderId
│
├── HTTP Request: 模拟支付
│   ├── Method: POST
│   ├── Path: /api/v1/payments
│   ├── Header: Authorization: Bearer ${token}
│   └── Body: {"orderId":"${orderId}","payMethod":"alipay"}
├── JSON Extractor: payStatus → $.data.status
│
└── Response Assertion: Contains "success"
Step 8: 添加监听器
复制代码
Thread Group 下添加以下监听器:

1. Aggregate Report(聚合报告)
   → 查看每个事务的统计数据

2. Response Times Over Time
   → 查看响应时间趋势

3. Transactions per Second
   → 查看 TPS 趋势

4. Summary Report(汇总报告)
   → 快速查看总体指标

5. View Results Tree(调试用)
   → 压测时建议禁用!

6.4 执行压测

#mermaid-svg-ySKxoo21jckwIPcI{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-ySKxoo21jckwIPcI .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-ySKxoo21jckwIPcI .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-ySKxoo21jckwIPcI .error-icon{fill:#552222;}#mermaid-svg-ySKxoo21jckwIPcI .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-ySKxoo21jckwIPcI .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-ySKxoo21jckwIPcI .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-ySKxoo21jckwIPcI .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-ySKxoo21jckwIPcI .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-ySKxoo21jckwIPcI .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-ySKxoo21jckwIPcI .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-ySKxoo21jckwIPcI .marker{fill:#333333;stroke:#333333;}#mermaid-svg-ySKxoo21jckwIPcI .marker.cross{stroke:#333333;}#mermaid-svg-ySKxoo21jckwIPcI svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-ySKxoo21jckwIPcI p{margin:0;}#mermaid-svg-ySKxoo21jckwIPcI .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-ySKxoo21jckwIPcI .cluster-label text{fill:#333;}#mermaid-svg-ySKxoo21jckwIPcI .cluster-label span{color:#333;}#mermaid-svg-ySKxoo21jckwIPcI .cluster-label span p{background-color:transparent;}#mermaid-svg-ySKxoo21jckwIPcI .label text,#mermaid-svg-ySKxoo21jckwIPcI span{fill:#333;color:#333;}#mermaid-svg-ySKxoo21jckwIPcI .node rect,#mermaid-svg-ySKxoo21jckwIPcI .node circle,#mermaid-svg-ySKxoo21jckwIPcI .node ellipse,#mermaid-svg-ySKxoo21jckwIPcI .node polygon,#mermaid-svg-ySKxoo21jckwIPcI .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-ySKxoo21jckwIPcI .rough-node .label text,#mermaid-svg-ySKxoo21jckwIPcI .node .label text,#mermaid-svg-ySKxoo21jckwIPcI .image-shape .label,#mermaid-svg-ySKxoo21jckwIPcI .icon-shape .label{text-anchor:middle;}#mermaid-svg-ySKxoo21jckwIPcI .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-ySKxoo21jckwIPcI .rough-node .label,#mermaid-svg-ySKxoo21jckwIPcI .node .label,#mermaid-svg-ySKxoo21jckwIPcI .image-shape .label,#mermaid-svg-ySKxoo21jckwIPcI .icon-shape .label{text-align:center;}#mermaid-svg-ySKxoo21jckwIPcI .node.clickable{cursor:pointer;}#mermaid-svg-ySKxoo21jckwIPcI .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-ySKxoo21jckwIPcI .arrowheadPath{fill:#333333;}#mermaid-svg-ySKxoo21jckwIPcI .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-ySKxoo21jckwIPcI .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-ySKxoo21jckwIPcI .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ySKxoo21jckwIPcI .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-ySKxoo21jckwIPcI .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ySKxoo21jckwIPcI .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-ySKxoo21jckwIPcI .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-ySKxoo21jckwIPcI .cluster text{fill:#333;}#mermaid-svg-ySKxoo21jckwIPcI .cluster span{color:#333;}#mermaid-svg-ySKxoo21jckwIPcI div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-ySKxoo21jckwIPcI .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-ySKxoo21jckwIPcI rect.text{fill:none;stroke-width:0;}#mermaid-svg-ySKxoo21jckwIPcI .icon-shape,#mermaid-svg-ySKxoo21jckwIPcI .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ySKxoo21jckwIPcI .icon-shape p,#mermaid-svg-ySKxoo21jckwIPcI .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-ySKxoo21jckwIPcI .icon-shape .label rect,#mermaid-svg-ySKxoo21jckwIPcI .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ySKxoo21jckwIPcI .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-ySKxoo21jckwIPcI .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-ySKxoo21jckwIPcI :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 是





压测前检查清单
CSV文件就绪?
服务器监控开启?
准备测试数据
JMeter内存足够?
启动服务器监控
关闭GUI监听器

(除必要的外)
调整JVM内存
使用非GUI模式执行
收集结果
生成HTML报告
分析结果

非GUI模式执行
bash 复制代码
# 基本执行命令
jmeter -n -t ecommerce_test.jmx -l results.jtl

# 完整执行命令(推荐)
jmeter -n \
  -t ecommerce_test.jmx \
  -l results_$(date +%Y%m%d_%H%M%S).jtl \
  -e -o html_report/ \
  -J HEAP=-Xms4g -Xmx8g

# 参数说明:
# -n : 非GUI模式
# -t : 测试计划文件路径
# -l : 结果输出文件(JTL格式)
# -e : 测试结束后自动生成HTML报告
# -o : HTML报告输出目录

6.5 生成 HTML 报告

bash 复制代码
# 方式1:测试结束后自动生成(-e -o 参数)
jmeter -n -t test.jmx -l results.jtl -e -o report/

# 方式2:从已有 JTL 文件生成
jmeter --generate-html report/ --input-jtl results.jtl

# 方式3:使用非默认模板
jmeter --generate-html report/ --input-jtl results.jtl --reportatitle "双11压测报告"
HTML 报告关键指标解读

#mermaid-svg-nxfQmCxnYHOsosYk{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-nxfQmCxnYHOsosYk .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-nxfQmCxnYHOsosYk .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-nxfQmCxnYHOsosYk .error-icon{fill:#552222;}#mermaid-svg-nxfQmCxnYHOsosYk .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-nxfQmCxnYHOsosYk .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-nxfQmCxnYHOsosYk .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-nxfQmCxnYHOsosYk .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-nxfQmCxnYHOsosYk .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-nxfQmCxnYHOsosYk .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-nxfQmCxnYHOsosYk .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-nxfQmCxnYHOsosYk .marker{fill:#333333;stroke:#333333;}#mermaid-svg-nxfQmCxnYHOsosYk .marker.cross{stroke:#333333;}#mermaid-svg-nxfQmCxnYHOsosYk svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-nxfQmCxnYHOsosYk p{margin:0;}#mermaid-svg-nxfQmCxnYHOsosYk .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-nxfQmCxnYHOsosYk .cluster-label text{fill:#333;}#mermaid-svg-nxfQmCxnYHOsosYk .cluster-label span{color:#333;}#mermaid-svg-nxfQmCxnYHOsosYk .cluster-label span p{background-color:transparent;}#mermaid-svg-nxfQmCxnYHOsosYk .label text,#mermaid-svg-nxfQmCxnYHOsosYk span{fill:#333;color:#333;}#mermaid-svg-nxfQmCxnYHOsosYk .node rect,#mermaid-svg-nxfQmCxnYHOsosYk .node circle,#mermaid-svg-nxfQmCxnYHOsosYk .node ellipse,#mermaid-svg-nxfQmCxnYHOsosYk .node polygon,#mermaid-svg-nxfQmCxnYHOsosYk .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-nxfQmCxnYHOsosYk .rough-node .label text,#mermaid-svg-nxfQmCxnYHOsosYk .node .label text,#mermaid-svg-nxfQmCxnYHOsosYk .image-shape .label,#mermaid-svg-nxfQmCxnYHOsosYk .icon-shape .label{text-anchor:middle;}#mermaid-svg-nxfQmCxnYHOsosYk .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-nxfQmCxnYHOsosYk .rough-node .label,#mermaid-svg-nxfQmCxnYHOsosYk .node .label,#mermaid-svg-nxfQmCxnYHOsosYk .image-shape .label,#mermaid-svg-nxfQmCxnYHOsosYk .icon-shape .label{text-align:center;}#mermaid-svg-nxfQmCxnYHOsosYk .node.clickable{cursor:pointer;}#mermaid-svg-nxfQmCxnYHOsosYk .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-nxfQmCxnYHOsosYk .arrowheadPath{fill:#333333;}#mermaid-svg-nxfQmCxnYHOsosYk .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-nxfQmCxnYHOsosYk .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-nxfQmCxnYHOsosYk .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-nxfQmCxnYHOsosYk .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-nxfQmCxnYHOsosYk .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-nxfQmCxnYHOsosYk .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-nxfQmCxnYHOsosYk .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-nxfQmCxnYHOsosYk .cluster text{fill:#333;}#mermaid-svg-nxfQmCxnYHOsosYk .cluster span{color:#333;}#mermaid-svg-nxfQmCxnYHOsosYk div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-nxfQmCxnYHOsosYk .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-nxfQmCxnYHOsosYk rect.text{fill:none;stroke-width:0;}#mermaid-svg-nxfQmCxnYHOsosYk .icon-shape,#mermaid-svg-nxfQmCxnYHOsosYk .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-nxfQmCxnYHOsosYk .icon-shape p,#mermaid-svg-nxfQmCxnYHOsosYk .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-nxfQmCxnYHOsosYk .icon-shape .label rect,#mermaid-svg-nxfQmCxnYHOsosYk .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-nxfQmCxnYHOsosYk .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-nxfQmCxnYHOsosYk .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-nxfQmCxnYHOsosYk :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} HTML 报告关键页面
Dashboard

= 仪表盘

总体概览
Transactions

= 事务详情

每个接口的统计
Errors

= 错误分析

失败请求详情
Statistics

= 统计表格

百分位数等
Over Time

= 趋势图

TPS/响应时间变化

6.6 结果分析

性能测试报告模板
复制代码
=== 电商系统性能测试报告 ===

一、测试环境
- 服务器:4C8G × 3 台
- 数据库:MySQL 8.0 主从
- 缓存:Redis 6.0 集群
- JMeter:5.6.3,4C8G,非GUI模式

二、测试场景
- 并发用户:1000
- 启动时间:120秒
- 持续时间:600秒
- 定时器:Gaussian Random 500~1500ms

三、测试结果
| 接口 | TPS | Avg(ms) | 90%(ms) | 99%(ms) | Error% |
|------|-----|---------|---------|---------|--------|
| 登录 | 52 | 180 | 350 | 680 | 0.02% |
| 浏览商品 | 210 | 85 | 180 | 420 | 0.01% |
| 搜索商品 | 95 | 220 | 480 | 950 | 0.08% |
| 下单支付 | 78 | 450 | 880 | 1800 | 0.05% |
| 查看订单 | 105 | 65 | 130 | 280 | 0.01% |

四、结论
- 所有接口 TPS 达标 ✅
- 90% 响应时间达标 ✅
- 错误率均低于 0.1% ✅
- 下单接口 99% 线偏高,建议优化

五、建议
1. 下单接口增加 Redis 缓存
2. 数据库添加读写分离
3. 支付接口增加异步处理

第7章 JMeter 分布式压测

7.1 为什么需要分布式压测?

餐厅比喻:如果一家餐厅要测试能否接待 10000 个顾客,一个服务员(一台机器)可能忙不过来。你需要雇佣多个服务员(多台机器),每个人负责一部分顾客,然后汇总结果。
#mermaid-svg-8qXdF1IIUjEziOEi{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-8qXdF1IIUjEziOEi .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-8qXdF1IIUjEziOEi .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-8qXdF1IIUjEziOEi .error-icon{fill:#552222;}#mermaid-svg-8qXdF1IIUjEziOEi .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-8qXdF1IIUjEziOEi .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-8qXdF1IIUjEziOEi .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-8qXdF1IIUjEziOEi .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-8qXdF1IIUjEziOEi .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-8qXdF1IIUjEziOEi .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-8qXdF1IIUjEziOEi .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-8qXdF1IIUjEziOEi .marker{fill:#333333;stroke:#333333;}#mermaid-svg-8qXdF1IIUjEziOEi .marker.cross{stroke:#333333;}#mermaid-svg-8qXdF1IIUjEziOEi svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-8qXdF1IIUjEziOEi p{margin:0;}#mermaid-svg-8qXdF1IIUjEziOEi .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-8qXdF1IIUjEziOEi .cluster-label text{fill:#333;}#mermaid-svg-8qXdF1IIUjEziOEi .cluster-label span{color:#333;}#mermaid-svg-8qXdF1IIUjEziOEi .cluster-label span p{background-color:transparent;}#mermaid-svg-8qXdF1IIUjEziOEi .label text,#mermaid-svg-8qXdF1IIUjEziOEi span{fill:#333;color:#333;}#mermaid-svg-8qXdF1IIUjEziOEi .node rect,#mermaid-svg-8qXdF1IIUjEziOEi .node circle,#mermaid-svg-8qXdF1IIUjEziOEi .node ellipse,#mermaid-svg-8qXdF1IIUjEziOEi .node polygon,#mermaid-svg-8qXdF1IIUjEziOEi .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-8qXdF1IIUjEziOEi .rough-node .label text,#mermaid-svg-8qXdF1IIUjEziOEi .node .label text,#mermaid-svg-8qXdF1IIUjEziOEi .image-shape .label,#mermaid-svg-8qXdF1IIUjEziOEi .icon-shape .label{text-anchor:middle;}#mermaid-svg-8qXdF1IIUjEziOEi .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-8qXdF1IIUjEziOEi .rough-node .label,#mermaid-svg-8qXdF1IIUjEziOEi .node .label,#mermaid-svg-8qXdF1IIUjEziOEi .image-shape .label,#mermaid-svg-8qXdF1IIUjEziOEi .icon-shape .label{text-align:center;}#mermaid-svg-8qXdF1IIUjEziOEi .node.clickable{cursor:pointer;}#mermaid-svg-8qXdF1IIUjEziOEi .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-8qXdF1IIUjEziOEi .arrowheadPath{fill:#333333;}#mermaid-svg-8qXdF1IIUjEziOEi .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-8qXdF1IIUjEziOEi .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-8qXdF1IIUjEziOEi .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-8qXdF1IIUjEziOEi .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-8qXdF1IIUjEziOEi .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-8qXdF1IIUjEziOEi .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-8qXdF1IIUjEziOEi .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-8qXdF1IIUjEziOEi .cluster text{fill:#333;}#mermaid-svg-8qXdF1IIUjEziOEi .cluster span{color:#333;}#mermaid-svg-8qXdF1IIUjEziOEi div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-8qXdF1IIUjEziOEi .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-8qXdF1IIUjEziOEi rect.text{fill:none;stroke-width:0;}#mermaid-svg-8qXdF1IIUjEziOEi .icon-shape,#mermaid-svg-8qXdF1IIUjEziOEi .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-8qXdF1IIUjEziOEi .icon-shape p,#mermaid-svg-8qXdF1IIUjEziOEi .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-8qXdF1IIUjEziOEi .icon-shape .label rect,#mermaid-svg-8qXdF1IIUjEziOEi .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-8qXdF1IIUjEziOEi .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-8qXdF1IIUjEziOEi .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-8qXdF1IIUjEziOEi :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 分布式压测
总计
Master(指挥官)
Slave1: 500用户
Slave2: 500用户
Slave3: 500用户
Slave4: 500用户
2000+用户
单机压测
模拟
瓶颈
1台JMeter
1000用户
CPU/内存/网络不足

单机限制

因素 典型限制 说明
CPU 300~500 线程 JMeter 每个线程需要 CPU
内存 每线程 ~1MB 1000 线程 ≈ 1GB
网络 带宽限制 大量并发连接
文件句柄 默认 1024 Linux 需要调大

7.2 Master-Slave 架构

#mermaid-svg-EBJsZ05St3OqLKOc{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-EBJsZ05St3OqLKOc .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-EBJsZ05St3OqLKOc .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-EBJsZ05St3OqLKOc .error-icon{fill:#552222;}#mermaid-svg-EBJsZ05St3OqLKOc .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-EBJsZ05St3OqLKOc .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-EBJsZ05St3OqLKOc .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-EBJsZ05St3OqLKOc .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-EBJsZ05St3OqLKOc .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-EBJsZ05St3OqLKOc .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-EBJsZ05St3OqLKOc .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-EBJsZ05St3OqLKOc .marker{fill:#333333;stroke:#333333;}#mermaid-svg-EBJsZ05St3OqLKOc .marker.cross{stroke:#333333;}#mermaid-svg-EBJsZ05St3OqLKOc svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-EBJsZ05St3OqLKOc p{margin:0;}#mermaid-svg-EBJsZ05St3OqLKOc .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-EBJsZ05St3OqLKOc .cluster-label text{fill:#333;}#mermaid-svg-EBJsZ05St3OqLKOc .cluster-label span{color:#333;}#mermaid-svg-EBJsZ05St3OqLKOc .cluster-label span p{background-color:transparent;}#mermaid-svg-EBJsZ05St3OqLKOc .label text,#mermaid-svg-EBJsZ05St3OqLKOc span{fill:#333;color:#333;}#mermaid-svg-EBJsZ05St3OqLKOc .node rect,#mermaid-svg-EBJsZ05St3OqLKOc .node circle,#mermaid-svg-EBJsZ05St3OqLKOc .node ellipse,#mermaid-svg-EBJsZ05St3OqLKOc .node polygon,#mermaid-svg-EBJsZ05St3OqLKOc .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-EBJsZ05St3OqLKOc .rough-node .label text,#mermaid-svg-EBJsZ05St3OqLKOc .node .label text,#mermaid-svg-EBJsZ05St3OqLKOc .image-shape .label,#mermaid-svg-EBJsZ05St3OqLKOc .icon-shape .label{text-anchor:middle;}#mermaid-svg-EBJsZ05St3OqLKOc .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-EBJsZ05St3OqLKOc .rough-node .label,#mermaid-svg-EBJsZ05St3OqLKOc .node .label,#mermaid-svg-EBJsZ05St3OqLKOc .image-shape .label,#mermaid-svg-EBJsZ05St3OqLKOc .icon-shape .label{text-align:center;}#mermaid-svg-EBJsZ05St3OqLKOc .node.clickable{cursor:pointer;}#mermaid-svg-EBJsZ05St3OqLKOc .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-EBJsZ05St3OqLKOc .arrowheadPath{fill:#333333;}#mermaid-svg-EBJsZ05St3OqLKOc .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-EBJsZ05St3OqLKOc .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-EBJsZ05St3OqLKOc .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-EBJsZ05St3OqLKOc .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-EBJsZ05St3OqLKOc .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-EBJsZ05St3OqLKOc .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-EBJsZ05St3OqLKOc .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-EBJsZ05St3OqLKOc .cluster text{fill:#333;}#mermaid-svg-EBJsZ05St3OqLKOc .cluster span{color:#333;}#mermaid-svg-EBJsZ05St3OqLKOc div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-EBJsZ05St3OqLKOc .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-EBJsZ05St3OqLKOc rect.text{fill:none;stroke-width:0;}#mermaid-svg-EBJsZ05St3OqLKOc .icon-shape,#mermaid-svg-EBJsZ05St3OqLKOc .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-EBJsZ05St3OqLKOc .icon-shape p,#mermaid-svg-EBJsZ05St3OqLKOc .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-EBJsZ05St3OqLKOc .icon-shape .label rect,#mermaid-svg-EBJsZ05St3OqLKOc .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-EBJsZ05St3OqLKOc .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-EBJsZ05St3OqLKOc .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-EBJsZ05St3OqLKOc :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 分布式压测架构
Slave 节点群
分发测试计划
分发测试计划
分发测试计划
分发测试计划
回传结果
回传结果
回传结果
回传结果
Slave 1

192.168.1.101

500 用户
Master 节点

(指挥中心)
Slave 2

192.168.1.102

500 用户
Slave 3

192.168.1.103

500 用户
Slave 4

192.168.1.104

500 用户
汇总结果

生成报告

7.3 配置步骤

Step 1: 所有节点安装 JMeter
bash 复制代码
# 在 Master 和所有 Slave 上执行相同操作

# 1. 安装 JDK 17
sudo apt install -y openjdk-17-jdk

# 2. 下载安装 JMeter
wget https://dlcdn.apache.org/jmeter/binaries/apache-jmeter-5.6.3.tgz
tar -xzf apache-jmeter-5.6.3.tgz -C /opt/
export JMETER_HOME=/opt/apache-jmeter-5.6.3
export PATH=$JMETER_HOME/bin:$PATH

# 3. 安装插件管理器(所有节点)
cp jmeter-plugins-manager-1.9.jar $JMETER_HOME/lib/ext/

# 4. 确保所有节点的 JMeter 版本和插件完全一致!
jmeter --version
Step 2: 配置 Slave 节点
bash 复制代码
# 编辑 Slave 的 JMETER_HOME/bin/jmeter.properties

# 1. 设置 Server 端口(默认 1099)
server_port=1099

# 2. 设置 Server 主机(0.0.0.0 允许所有IP连接)
server.rmi.localport=1099

# 3. 如果有防火墙,需要开放端口
sudo firewall-cmd --add-port=1099/tcp --permanent
sudo firewall-cmd --reload
Step 3: 启动 Slave 服务
bash 复制代码
# Linux/Mac - 启动 Slave 服务
cd /opt/apache-jmeter-5.6.3/bin
./jmeter-server

# Windows - 启动 Slave 服务
jmeter-server.bat

# 如果需要指定 JVM 参数
JVM_ARGS="-Xms2g -Xmx4g" ./jmeter-server

# 后台运行(Linux)
nohup ./jmeter-server > jmeter-server.log 2>&1 &

# 验证 Slave 是否启动
netstat -an | grep 1099
# 应该看到 LISTENING 状态
Step 4: 配置 Master 节点
bash 复制代码
# 编辑 Master 的 JMETER_HOME/bin/jmeter.properties

# 添加 Slave IP 列表
remote_hosts=192.168.1.101:1099,192.168.1.102:1099,192.168.1.103:1099,192.168.1.104:1099

# 或者编辑 JMETER_HOME/bin/remote_hosts(一行一个IP)
# 192.168.1.101
# 192.168.1.102
# 192.168.1.103
# 192.168.1.104
Step 5: 从 Master 启动分布式测试
bash 复制代码
# GUI 模式启动(调试用)
jmeter
# 菜单:Run → Remote Start → 选择 Slave 节点
# 或 Run → Remote Start All

# 非GUI模式启动(正式压测)
# 启动所有 Slave
jmeter -n -t test_plan.jmx -r -l results.jtl

# 启动指定 Slave
jmeter -n -t test_plan.jmx -R 192.168.1.101:1099,192.168.1.102:1099 -l results.jtl

# 参数说明:
# -r : 启动 remote_hosts 中所有 Slave
# -R : 启动指定的 Slave(逗号分隔)

7.4 分布式压测注意事项

#mermaid-svg-RvWojQCguls3d83E{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-RvWojQCguls3d83E .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-RvWojQCguls3d83E .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-RvWojQCguls3d83E .error-icon{fill:#552222;}#mermaid-svg-RvWojQCguls3d83E .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-RvWojQCguls3d83E .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-RvWojQCguls3d83E .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-RvWojQCguls3d83E .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-RvWojQCguls3d83E .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-RvWojQCguls3d83E .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-RvWojQCguls3d83E .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-RvWojQCguls3d83E .marker{fill:#333333;stroke:#333333;}#mermaid-svg-RvWojQCguls3d83E .marker.cross{stroke:#333333;}#mermaid-svg-RvWojQCguls3d83E svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-RvWojQCguls3d83E p{margin:0;}#mermaid-svg-RvWojQCguls3d83E .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-RvWojQCguls3d83E .cluster-label text{fill:#333;}#mermaid-svg-RvWojQCguls3d83E .cluster-label span{color:#333;}#mermaid-svg-RvWojQCguls3d83E .cluster-label span p{background-color:transparent;}#mermaid-svg-RvWojQCguls3d83E .label text,#mermaid-svg-RvWojQCguls3d83E span{fill:#333;color:#333;}#mermaid-svg-RvWojQCguls3d83E .node rect,#mermaid-svg-RvWojQCguls3d83E .node circle,#mermaid-svg-RvWojQCguls3d83E .node ellipse,#mermaid-svg-RvWojQCguls3d83E .node polygon,#mermaid-svg-RvWojQCguls3d83E .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-RvWojQCguls3d83E .rough-node .label text,#mermaid-svg-RvWojQCguls3d83E .node .label text,#mermaid-svg-RvWojQCguls3d83E .image-shape .label,#mermaid-svg-RvWojQCguls3d83E .icon-shape .label{text-anchor:middle;}#mermaid-svg-RvWojQCguls3d83E .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-RvWojQCguls3d83E .rough-node .label,#mermaid-svg-RvWojQCguls3d83E .node .label,#mermaid-svg-RvWojQCguls3d83E .image-shape .label,#mermaid-svg-RvWojQCguls3d83E .icon-shape .label{text-align:center;}#mermaid-svg-RvWojQCguls3d83E .node.clickable{cursor:pointer;}#mermaid-svg-RvWojQCguls3d83E .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-RvWojQCguls3d83E .arrowheadPath{fill:#333333;}#mermaid-svg-RvWojQCguls3d83E .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-RvWojQCguls3d83E .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-RvWojQCguls3d83E .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-RvWojQCguls3d83E .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-RvWojQCguls3d83E .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-RvWojQCguls3d83E .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-RvWojQCguls3d83E .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-RvWojQCguls3d83E .cluster text{fill:#333;}#mermaid-svg-RvWojQCguls3d83E .cluster span{color:#333;}#mermaid-svg-RvWojQCguls3d83E div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-RvWojQCguls3d83E .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-RvWojQCguls3d83E rect.text{fill:none;stroke-width:0;}#mermaid-svg-RvWojQCguls3d83E .icon-shape,#mermaid-svg-RvWojQCguls3d83E .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-RvWojQCguls3d83E .icon-shape p,#mermaid-svg-RvWojQCguls3d83E .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-RvWojQCguls3d83E .icon-shape .label rect,#mermaid-svg-RvWojQCguls3d83E .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-RvWojQCguls3d83E .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-RvWojQCguls3d83E .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-RvWojQCguls3d83E :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 分布式压测注意事项
时间同步
文件一致
网络通畅
数据文件
结果分离
所有节点 NTP 时间同步

ntpdate pool.ntp.org
JMeter 版本一致

插件版本一致

测试计划文件一致
防火墙开放端口

1099 + 额外端口范围
CSV文件放在每个Slave

相同路径相同内容
每个Slave结果自动汇总

或使用 -l 分别保存

常见问题排查

问题 原因 解决方案
Connection refused 防火墙/端口未开放 开放 1099 端口
RMI Exception JDK 版本不一致 统一 JDK 版本
结果文件为空 Slave 未收到测试计划 检查 remote_hosts 配置
性能不均匀 Slave 硬件差异 均匀分配用户数
CSV 数据重复 所有 Slave 读同一文件 每台 Slave 使用不同起始行

第8章 常用插件介绍

8.1 插件生态概览

餐厅比喻 :JMeter 的插件就像餐厅的扩展设备------你可以安装自动洗碗机(PerfMon)、外卖系统(WebDriver)、高级点菜机(Custom Thread Groups)等。
#mermaid-svg-wzuM9TTl9RRsrjhj{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-wzuM9TTl9RRsrjhj .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-wzuM9TTl9RRsrjhj .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-wzuM9TTl9RRsrjhj .error-icon{fill:#552222;}#mermaid-svg-wzuM9TTl9RRsrjhj .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-wzuM9TTl9RRsrjhj .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-wzuM9TTl9RRsrjhj .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-wzuM9TTl9RRsrjhj .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-wzuM9TTl9RRsrjhj .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-wzuM9TTl9RRsrjhj .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-wzuM9TTl9RRsrjhj .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-wzuM9TTl9RRsrjhj .marker{fill:#333333;stroke:#333333;}#mermaid-svg-wzuM9TTl9RRsrjhj .marker.cross{stroke:#333333;}#mermaid-svg-wzuM9TTl9RRsrjhj svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-wzuM9TTl9RRsrjhj p{margin:0;}#mermaid-svg-wzuM9TTl9RRsrjhj .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-wzuM9TTl9RRsrjhj .cluster-label text{fill:#333;}#mermaid-svg-wzuM9TTl9RRsrjhj .cluster-label span{color:#333;}#mermaid-svg-wzuM9TTl9RRsrjhj .cluster-label span p{background-color:transparent;}#mermaid-svg-wzuM9TTl9RRsrjhj .label text,#mermaid-svg-wzuM9TTl9RRsrjhj span{fill:#333;color:#333;}#mermaid-svg-wzuM9TTl9RRsrjhj .node rect,#mermaid-svg-wzuM9TTl9RRsrjhj .node circle,#mermaid-svg-wzuM9TTl9RRsrjhj .node ellipse,#mermaid-svg-wzuM9TTl9RRsrjhj .node polygon,#mermaid-svg-wzuM9TTl9RRsrjhj .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-wzuM9TTl9RRsrjhj .rough-node .label text,#mermaid-svg-wzuM9TTl9RRsrjhj .node .label text,#mermaid-svg-wzuM9TTl9RRsrjhj .image-shape .label,#mermaid-svg-wzuM9TTl9RRsrjhj .icon-shape .label{text-anchor:middle;}#mermaid-svg-wzuM9TTl9RRsrjhj .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-wzuM9TTl9RRsrjhj .rough-node .label,#mermaid-svg-wzuM9TTl9RRsrjhj .node .label,#mermaid-svg-wzuM9TTl9RRsrjhj .image-shape .label,#mermaid-svg-wzuM9TTl9RRsrjhj .icon-shape .label{text-align:center;}#mermaid-svg-wzuM9TTl9RRsrjhj .node.clickable{cursor:pointer;}#mermaid-svg-wzuM9TTl9RRsrjhj .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-wzuM9TTl9RRsrjhj .arrowheadPath{fill:#333333;}#mermaid-svg-wzuM9TTl9RRsrjhj .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-wzuM9TTl9RRsrjhj .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-wzuM9TTl9RRsrjhj .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-wzuM9TTl9RRsrjhj .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-wzuM9TTl9RRsrjhj .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-wzuM9TTl9RRsrjhj .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-wzuM9TTl9RRsrjhj .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-wzuM9TTl9RRsrjhj .cluster text{fill:#333;}#mermaid-svg-wzuM9TTl9RRsrjhj .cluster span{color:#333;}#mermaid-svg-wzuM9TTl9RRsrjhj div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-wzuM9TTl9RRsrjhj .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-wzuM9TTl9RRsrjhj rect.text{fill:none;stroke-width:0;}#mermaid-svg-wzuM9TTl9RRsrjhj .icon-shape,#mermaid-svg-wzuM9TTl9RRsrjhj .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-wzuM9TTl9RRsrjhj .icon-shape p,#mermaid-svg-wzuM9TTl9RRsrjhj .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-wzuM9TTl9RRsrjhj .icon-shape .label rect,#mermaid-svg-wzuM9TTl9RRsrjhj .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-wzuM9TTl9RRsrjhj .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-wzuM9TTl9RRsrjhj .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-wzuM9TTl9RRsrjhj :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} JMeter 插件生态
Plugins Manager

插件管理器
Custom Thread Groups

自定义线程组
PerfMon Metrics Collector

服务器性能监控
WebDriver Plugin

浏览器级测试
Dummy Sampler

调试工具
JSON Path Extractor

JSON提取增强
SQL Query Counter

SQL统计
Synthesis Report

综合报告

8.2 Custom Thread Groups(自定义线程组)

餐厅比喻:普通 Thread Group 像是"开门后所有顾客一起涌入"。Custom Thread Groups 像是"阶梯式迎客"------先来 50 个,再增加到 100,最后到 500,看看每一步餐厅是否扛得住。
#mermaid-svg-UzFBF4zL8deKXvh0{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-UzFBF4zL8deKXvh0 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-UzFBF4zL8deKXvh0 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-UzFBF4zL8deKXvh0 .error-icon{fill:#552222;}#mermaid-svg-UzFBF4zL8deKXvh0 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-UzFBF4zL8deKXvh0 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-UzFBF4zL8deKXvh0 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-UzFBF4zL8deKXvh0 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-UzFBF4zL8deKXvh0 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-UzFBF4zL8deKXvh0 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-UzFBF4zL8deKXvh0 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-UzFBF4zL8deKXvh0 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-UzFBF4zL8deKXvh0 .marker.cross{stroke:#333333;}#mermaid-svg-UzFBF4zL8deKXvh0 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-UzFBF4zL8deKXvh0 p{margin:0;}#mermaid-svg-UzFBF4zL8deKXvh0 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-UzFBF4zL8deKXvh0 .cluster-label text{fill:#333;}#mermaid-svg-UzFBF4zL8deKXvh0 .cluster-label span{color:#333;}#mermaid-svg-UzFBF4zL8deKXvh0 .cluster-label span p{background-color:transparent;}#mermaid-svg-UzFBF4zL8deKXvh0 .label text,#mermaid-svg-UzFBF4zL8deKXvh0 span{fill:#333;color:#333;}#mermaid-svg-UzFBF4zL8deKXvh0 .node rect,#mermaid-svg-UzFBF4zL8deKXvh0 .node circle,#mermaid-svg-UzFBF4zL8deKXvh0 .node ellipse,#mermaid-svg-UzFBF4zL8deKXvh0 .node polygon,#mermaid-svg-UzFBF4zL8deKXvh0 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-UzFBF4zL8deKXvh0 .rough-node .label text,#mermaid-svg-UzFBF4zL8deKXvh0 .node .label text,#mermaid-svg-UzFBF4zL8deKXvh0 .image-shape .label,#mermaid-svg-UzFBF4zL8deKXvh0 .icon-shape .label{text-anchor:middle;}#mermaid-svg-UzFBF4zL8deKXvh0 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-UzFBF4zL8deKXvh0 .rough-node .label,#mermaid-svg-UzFBF4zL8deKXvh0 .node .label,#mermaid-svg-UzFBF4zL8deKXvh0 .image-shape .label,#mermaid-svg-UzFBF4zL8deKXvh0 .icon-shape .label{text-align:center;}#mermaid-svg-UzFBF4zL8deKXvh0 .node.clickable{cursor:pointer;}#mermaid-svg-UzFBF4zL8deKXvh0 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-UzFBF4zL8deKXvh0 .arrowheadPath{fill:#333333;}#mermaid-svg-UzFBF4zL8deKXvh0 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-UzFBF4zL8deKXvh0 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-UzFBF4zL8deKXvh0 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-UzFBF4zL8deKXvh0 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-UzFBF4zL8deKXvh0 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-UzFBF4zL8deKXvh0 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-UzFBF4zL8deKXvh0 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-UzFBF4zL8deKXvh0 .cluster text{fill:#333;}#mermaid-svg-UzFBF4zL8deKXvh0 .cluster span{color:#333;}#mermaid-svg-UzFBF4zL8deKXvh0 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-UzFBF4zL8deKXvh0 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-UzFBF4zL8deKXvh0 rect.text{fill:none;stroke-width:0;}#mermaid-svg-UzFBF4zL8deKXvh0 .icon-shape,#mermaid-svg-UzFBF4zL8deKXvh0 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-UzFBF4zL8deKXvh0 .icon-shape p,#mermaid-svg-UzFBF4zL8deKXvh0 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-UzFBF4zL8deKXvh0 .icon-shape .label rect,#mermaid-svg-UzFBF4zL8deKXvh0 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-UzFBF4zL8deKXvh0 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-UzFBF4zL8deKXvh0 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-UzFBF4zL8deKXvh0 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Concurrency Thread Group

(并发线程组)
目标并发数: 500

启动时间: 120秒

持续: 300秒

关闭时间: 60秒
Ultimate Thread Group(终极线程组)
阶段1: 预热

50用户 × 60秒
阶段2: 加压

200用户 × 120秒
阶段3: 峰值

500用户 × 180秒
阶段4: 减压

200用户 × 120秒
阶段5: 恢复

50用户 × 60秒

Ultimate Thread Group 配置示例
复制代码
安装:Options → Plugins Manager → Available → Custom Thread Groups → Apply

配置阶梯加压:
┌─────────────────────────────────────────────────┐
│ Ultimate Thread Group                            │
│                                                  │
│ 线程1:                                           │
│   Start Threads: 50                              │
│   Initial Delay: 0 秒                            │
│   Startup Time: 30 秒                           │
│   Hold Load For: 60 秒                          │
│   Shutdown Time: 10 秒                          │
│                                                  │
│ 线程2:                                           │
│   Start Threads: 150                             │
│   Initial Delay: 30 秒(等线程1启动完)          │
│   Startup Time: 60 秒                           │
│   Hold Load For: 120 秒                         │
│   Shutdown Time: 20 秒                          │
│                                                  │
│ 线程3:                                           │
│   Start Threads: 300                             │
│   Initial Delay: 90 秒                           │
│   Startup Time: 120 秒                          │
│   Hold Load For: 180 秒                         │
│   Shutdown Time: 30 秒                          │
└─────────────────────────────────────────────────┘

8.3 PerfMon(服务器性能监控)

餐厅比喻 :PerfMon 就像餐厅的温度计和监控摄像头------实时监控厨房(CPU)、仓库(内存)、通道(网络)的状态。
#mermaid-svg-1O091v9CSZYnwS5O{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-1O091v9CSZYnwS5O .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-1O091v9CSZYnwS5O .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-1O091v9CSZYnwS5O .error-icon{fill:#552222;}#mermaid-svg-1O091v9CSZYnwS5O .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-1O091v9CSZYnwS5O .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-1O091v9CSZYnwS5O .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-1O091v9CSZYnwS5O .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-1O091v9CSZYnwS5O .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-1O091v9CSZYnwS5O .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-1O091v9CSZYnwS5O .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-1O091v9CSZYnwS5O .marker{fill:#333333;stroke:#333333;}#mermaid-svg-1O091v9CSZYnwS5O .marker.cross{stroke:#333333;}#mermaid-svg-1O091v9CSZYnwS5O svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-1O091v9CSZYnwS5O p{margin:0;}#mermaid-svg-1O091v9CSZYnwS5O .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-1O091v9CSZYnwS5O .cluster-label text{fill:#333;}#mermaid-svg-1O091v9CSZYnwS5O .cluster-label span{color:#333;}#mermaid-svg-1O091v9CSZYnwS5O .cluster-label span p{background-color:transparent;}#mermaid-svg-1O091v9CSZYnwS5O .label text,#mermaid-svg-1O091v9CSZYnwS5O span{fill:#333;color:#333;}#mermaid-svg-1O091v9CSZYnwS5O .node rect,#mermaid-svg-1O091v9CSZYnwS5O .node circle,#mermaid-svg-1O091v9CSZYnwS5O .node ellipse,#mermaid-svg-1O091v9CSZYnwS5O .node polygon,#mermaid-svg-1O091v9CSZYnwS5O .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-1O091v9CSZYnwS5O .rough-node .label text,#mermaid-svg-1O091v9CSZYnwS5O .node .label text,#mermaid-svg-1O091v9CSZYnwS5O .image-shape .label,#mermaid-svg-1O091v9CSZYnwS5O .icon-shape .label{text-anchor:middle;}#mermaid-svg-1O091v9CSZYnwS5O .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-1O091v9CSZYnwS5O .rough-node .label,#mermaid-svg-1O091v9CSZYnwS5O .node .label,#mermaid-svg-1O091v9CSZYnwS5O .image-shape .label,#mermaid-svg-1O091v9CSZYnwS5O .icon-shape .label{text-align:center;}#mermaid-svg-1O091v9CSZYnwS5O .node.clickable{cursor:pointer;}#mermaid-svg-1O091v9CSZYnwS5O .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-1O091v9CSZYnwS5O .arrowheadPath{fill:#333333;}#mermaid-svg-1O091v9CSZYnwS5O .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-1O091v9CSZYnwS5O .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-1O091v9CSZYnwS5O .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-1O091v9CSZYnwS5O .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-1O091v9CSZYnwS5O .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-1O091v9CSZYnwS5O .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-1O091v9CSZYnwS5O .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-1O091v9CSZYnwS5O .cluster text{fill:#333;}#mermaid-svg-1O091v9CSZYnwS5O .cluster span{color:#333;}#mermaid-svg-1O091v9CSZYnwS5O div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-1O091v9CSZYnwS5O .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-1O091v9CSZYnwS5O rect.text{fill:none;stroke-width:0;}#mermaid-svg-1O091v9CSZYnwS5O .icon-shape,#mermaid-svg-1O091v9CSZYnwS5O .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-1O091v9CSZYnwS5O .icon-shape p,#mermaid-svg-1O091v9CSZYnwS5O .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-1O091v9CSZYnwS5O .icon-shape .label rect,#mermaid-svg-1O091v9CSZYnwS5O .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-1O091v9CSZYnwS5O .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-1O091v9CSZYnwS5O .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-1O091v9CSZYnwS5O :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 服务器端
JMeter 端
JMX 协议
JMeter

PerfMon Listener
ServerAgent

轻量级采集器
CPU 使用率
内存使用率
磁盘 I/O
网络流量
TCP 连接数

安装与配置
bash 复制代码
# 1. 下载 ServerAgent
# https://github.com/undera/perfmon-agent
# 解压到服务器上

# 2. 启动 ServerAgent(Linux)
cd serveragent/
./startAgent.sh

# 指定端口启动
./startAgent.sh --port 3456
./startAgent.sh --udp-port 3457 --tcp-port 3456

# 后台运行
nohup ./startAgent.sh > agent.log 2>&1 &

# 3. Windows 启动
startAgent.bat
JMeter 端配置
复制代码
Thread Group → Add → Listener → jp@gc - PerfMon Metrics Collector

配置:
1. 点击 "Add" 添加监控项
2. 选择监控指标:
   - CPU: cpu.idle(空闲率)/ cpu.user(用户态使用率)
   - Memory: mem.free(空闲内存)
   - Disk I/O: disk.io
   - Network: net.bytes
3. 配置服务器连接:
   - Host: 192.168.1.100
   - Port: 4444(默认)

8.4 WebDriver Plugin(浏览器级测试)

餐厅比喻 :普通 HTTP Sampler 只能模拟"点菜"这个动作。WebDriver Plugin 则能模拟整个顾客体验------走进餐厅、看菜单、等上菜、品尝、评价。
#mermaid-svg-dOklk2a5sR97t5My{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-dOklk2a5sR97t5My .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-dOklk2a5sR97t5My .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-dOklk2a5sR97t5My .error-icon{fill:#552222;}#mermaid-svg-dOklk2a5sR97t5My .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-dOklk2a5sR97t5My .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-dOklk2a5sR97t5My .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-dOklk2a5sR97t5My .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-dOklk2a5sR97t5My .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-dOklk2a5sR97t5My .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-dOklk2a5sR97t5My .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-dOklk2a5sR97t5My .marker{fill:#333333;stroke:#333333;}#mermaid-svg-dOklk2a5sR97t5My .marker.cross{stroke:#333333;}#mermaid-svg-dOklk2a5sR97t5My svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-dOklk2a5sR97t5My p{margin:0;}#mermaid-svg-dOklk2a5sR97t5My .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-dOklk2a5sR97t5My .cluster-label text{fill:#333;}#mermaid-svg-dOklk2a5sR97t5My .cluster-label span{color:#333;}#mermaid-svg-dOklk2a5sR97t5My .cluster-label span p{background-color:transparent;}#mermaid-svg-dOklk2a5sR97t5My .label text,#mermaid-svg-dOklk2a5sR97t5My span{fill:#333;color:#333;}#mermaid-svg-dOklk2a5sR97t5My .node rect,#mermaid-svg-dOklk2a5sR97t5My .node circle,#mermaid-svg-dOklk2a5sR97t5My .node ellipse,#mermaid-svg-dOklk2a5sR97t5My .node polygon,#mermaid-svg-dOklk2a5sR97t5My .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-dOklk2a5sR97t5My .rough-node .label text,#mermaid-svg-dOklk2a5sR97t5My .node .label text,#mermaid-svg-dOklk2a5sR97t5My .image-shape .label,#mermaid-svg-dOklk2a5sR97t5My .icon-shape .label{text-anchor:middle;}#mermaid-svg-dOklk2a5sR97t5My .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-dOklk2a5sR97t5My .rough-node .label,#mermaid-svg-dOklk2a5sR97t5My .node .label,#mermaid-svg-dOklk2a5sR97t5My .image-shape .label,#mermaid-svg-dOklk2a5sR97t5My .icon-shape .label{text-align:center;}#mermaid-svg-dOklk2a5sR97t5My .node.clickable{cursor:pointer;}#mermaid-svg-dOklk2a5sR97t5My .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-dOklk2a5sR97t5My .arrowheadPath{fill:#333333;}#mermaid-svg-dOklk2a5sR97t5My .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-dOklk2a5sR97t5My .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-dOklk2a5sR97t5My .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-dOklk2a5sR97t5My .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-dOklk2a5sR97t5My .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-dOklk2a5sR97t5My .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-dOklk2a5sR97t5My .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-dOklk2a5sR97t5My .cluster text{fill:#333;}#mermaid-svg-dOklk2a5sR97t5My .cluster span{color:#333;}#mermaid-svg-dOklk2a5sR97t5My div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-dOklk2a5sR97t5My .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-dOklk2a5sR97t5My rect.text{fill:none;stroke-width:0;}#mermaid-svg-dOklk2a5sR97t5My .icon-shape,#mermaid-svg-dOklk2a5sR97t5My .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-dOklk2a5sR97t5My .icon-shape p,#mermaid-svg-dOklk2a5sR97t5My .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-dOklk2a5sR97t5My .icon-shape .label rect,#mermaid-svg-dOklk2a5sR97t5My .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-dOklk2a5sR97t5My .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-dOklk2a5sR97t5My .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-dOklk2a5sR97t5My :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} WebDriver vs HTTP Sampler
WebDriver
启动真实浏览器
执行JavaScript
渲染完整页面
速度慢
资源消耗高
HTTP Sampler
只发送HTTP请求
不执行JavaScript
不渲染页面
速度快
资源消耗低

使用场景

  • 需要测试前端渲染性能
  • 需要测试 JavaScript 逻辑
  • 需要模拟真实浏览器行为
  • 需要截图或获取页面 DOM

注意:WebDriver 测试非常消耗资源,每个线程需要一个浏览器实例。建议并发数不超过 10。

8.5 其他实用插件

Dummy Sampler(调试利器)
复制代码
用途:快速调试脚本逻辑,不需要真实服务器

配置:
├── Sampler Name: 模拟登录响应
├── Response Data: {"code":200,"data":{"token":"test_token_123"}}
├── Response Code: 200
├── Response Time: ${__Random(100,500)}(随机响应时间)
└── Status: OK

使用场景:
1. 在没有测试环境时调试脚本
2. 验证参数化和关联逻辑
3. 演示和培训
Synthesis Report(综合报告)
复制代码
用途:生成更详细的 HTML 报告

特点:
- 比 JMeter 自带报告更美观
- 包含更多图表
- 支持自定义报告模板
- 可以对比多次测试结果

第9章 命令行执行与 CI/CD 集成

9.1 命令行执行详解

餐厅比喻 :GUI 模式就像手动经营餐厅 ------你站在大堂里亲自指挥。命令行模式就像设定好自动运营程序------你写好运营手册,让系统自动执行。
渲染错误: Mermaid 渲染失败: Lexical error on line 8. Unrecognized text. ...ubgraph "非GUI模式(推荐)"] E"命令行执行" -----------------------^

完整命令参数
bash 复制代码
jmeter [options] [JMX file]

# 核心参数
-n                    # 非GUI模式
-t testplan.jmx       # 指定测试计划文件
-l results.jtl         # 指定结果输出文件
-e                    # 测试结束后生成HTML报告
-o output_dir         # HTML报告输出目录
-r                    # 启动所有远程Slave
-R server_list        # 启动指定远程Slave

# JVM 参数
-J HEAP="-Xms4g -Xmx8g"    # 设置堆内存
-J java.awt.headless=true   # 无头模式

# 日志参数
-L [category=]level         # 设置日志级别
-j log_file                 # 日志输出文件

# 属性覆盖
-D property=value            # 覆盖JMeter属性
-J jmeter.property=value     # 覆盖JMeter属性
-J user.properties=value    # 覆盖用户属性

# 其他
-H                          # 显示帮助信息
-v                          # 显示版本信息
常用命令示例
bash 复制代码
# 1. 基本非GUI执行
jmeter -n -t my_test.jmx -l results.jtl

# 2. 执行并生成HTML报告
jmeter -n -t my_test.jmx -l results.jtl -e -o report/

# 3. 分布式执行
jmeter -n -t my_test.jmx -r -l results.jtl

# 4. 指定Slave执行
jmeter -n -t my_test.jmx -R 192.168.1.101,192.168.1.102 -l results.jtl

# 5. 覆盖线程数和持续时间(无需修改JMX文件)
jmeter -n -t my_test.jmx -l results.jtl \
  -J threads=500 \
  -J duration=600 \
  -J rampup=60

# 6. 指定JVM内存
jmeter -n -t my_test.jmx -l results.jtl \
  -J HEAP="-Xms4g -Xmx8g -XX:+UseG1GC"

# 7. 从已有JTL生成HTML报告
jmeter --generate-html report/ --input-jtl results.jtl
在JMX中使用属性变量
xml 复制代码
<!-- 在 JMX 文件中使用 ${__P(property, default)} 引用属性 -->
<ThreadGroup>
  <stringProp name="ThreadGroup.num_threads">${__P(threads,100)}</stringProp>
  <stringProp name="ThreadGroup.ramp_time">${__P(rampup,10)}</stringProp>
</ThreadGroup>
复制代码
这样就可以通过命令行动态修改参数,无需修改 JMX 文件:
jmeter -n -t test.jmx -l results.jtl -Jthreads=500 -Jrampup=60

9.2 Jenkins CI/CD 集成

餐厅比喻 :CI/CD 集成就像自动化巡检系统------每次有新菜品上线,自动请一批"神秘顾客"来品尝,如果味道不对就自动报警。
#mermaid-svg-c79fA6Hn66SkByp4{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-c79fA6Hn66SkByp4 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-c79fA6Hn66SkByp4 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-c79fA6Hn66SkByp4 .error-icon{fill:#552222;}#mermaid-svg-c79fA6Hn66SkByp4 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-c79fA6Hn66SkByp4 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-c79fA6Hn66SkByp4 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-c79fA6Hn66SkByp4 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-c79fA6Hn66SkByp4 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-c79fA6Hn66SkByp4 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-c79fA6Hn66SkByp4 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-c79fA6Hn66SkByp4 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-c79fA6Hn66SkByp4 .marker.cross{stroke:#333333;}#mermaid-svg-c79fA6Hn66SkByp4 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-c79fA6Hn66SkByp4 p{margin:0;}#mermaid-svg-c79fA6Hn66SkByp4 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-c79fA6Hn66SkByp4 .cluster-label text{fill:#333;}#mermaid-svg-c79fA6Hn66SkByp4 .cluster-label span{color:#333;}#mermaid-svg-c79fA6Hn66SkByp4 .cluster-label span p{background-color:transparent;}#mermaid-svg-c79fA6Hn66SkByp4 .label text,#mermaid-svg-c79fA6Hn66SkByp4 span{fill:#333;color:#333;}#mermaid-svg-c79fA6Hn66SkByp4 .node rect,#mermaid-svg-c79fA6Hn66SkByp4 .node circle,#mermaid-svg-c79fA6Hn66SkByp4 .node ellipse,#mermaid-svg-c79fA6Hn66SkByp4 .node polygon,#mermaid-svg-c79fA6Hn66SkByp4 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-c79fA6Hn66SkByp4 .rough-node .label text,#mermaid-svg-c79fA6Hn66SkByp4 .node .label text,#mermaid-svg-c79fA6Hn66SkByp4 .image-shape .label,#mermaid-svg-c79fA6Hn66SkByp4 .icon-shape .label{text-anchor:middle;}#mermaid-svg-c79fA6Hn66SkByp4 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-c79fA6Hn66SkByp4 .rough-node .label,#mermaid-svg-c79fA6Hn66SkByp4 .node .label,#mermaid-svg-c79fA6Hn66SkByp4 .image-shape .label,#mermaid-svg-c79fA6Hn66SkByp4 .icon-shape .label{text-align:center;}#mermaid-svg-c79fA6Hn66SkByp4 .node.clickable{cursor:pointer;}#mermaid-svg-c79fA6Hn66SkByp4 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-c79fA6Hn66SkByp4 .arrowheadPath{fill:#333333;}#mermaid-svg-c79fA6Hn66SkByp4 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-c79fA6Hn66SkByp4 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-c79fA6Hn66SkByp4 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-c79fA6Hn66SkByp4 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-c79fA6Hn66SkByp4 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-c79fA6Hn66SkByp4 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-c79fA6Hn66SkByp4 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-c79fA6Hn66SkByp4 .cluster text{fill:#333;}#mermaid-svg-c79fA6Hn66SkByp4 .cluster span{color:#333;}#mermaid-svg-c79fA6Hn66SkByp4 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-c79fA6Hn66SkByp4 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-c79fA6Hn66SkByp4 rect.text{fill:none;stroke-width:0;}#mermaid-svg-c79fA6Hn66SkByp4 .icon-shape,#mermaid-svg-c79fA6Hn66SkByp4 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-c79fA6Hn66SkByp4 .icon-shape p,#mermaid-svg-c79fA6Hn66SkByp4 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-c79fA6Hn66SkByp4 .icon-shape .label rect,#mermaid-svg-c79fA6Hn66SkByp4 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-c79fA6Hn66SkByp4 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-c79fA6Hn66SkByp4 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-c79fA6Hn66SkByp4 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} CI/CD 流水线


开发提交代码
CI 构建
部署到测试环境
执行性能测试
测试通过?
生成报告
通知团队
合并代码
标记失败
通知开发修复

Jenkins Pipeline 脚本
groovy 复制代码
// Jenkinsfile - JMeter 性能测试 Pipeline
pipeline {
    agent any
    
    environment {
        JMETER_HOME = '/opt/apache-jmeter-5.6.3'
        TEST_PLAN = 'performance_tests/ecommerce_test.jmx'
        RESULTS_DIR = 'performance_tests/results'
        REPORT_DIR = 'performance_tests/report'
    }
    
    stages {
        stage('准备环境') {
            steps {
                echo '安装 JMeter 和插件...'
                sh '''
                    if [ ! -d "$JMETER_HOME" ]; then
                        wget -q https://dlcdn.apache.org/jmeter/binaries/apache-jmeter-5.6.3.tgz
                        tar -xzf apache-jmeter-5.6.3.tgz -C /opt/
                    fi
                '''
            }
        }
        
        stage('等待应用就绪') {
            steps {
                echo '等待应用启动...'
                sh '''
                    # 等待应用健康检查通过
                    for i in $(seq 1 30); do
                        if curl -s http://api.shop.example.com/health | grep -q "UP"; then
                            echo "应用已就绪"
                            exit 0
                        fi
                        echo "等待应用启动... ($i/30)"
                        sleep 10
                    done
                    echo "应用启动超时!"
                    exit 1
                '''
            }
        }
        
        stage('执行性能测试') {
            steps {
                echo '执行 JMeter 压测...'
                sh '''
                    mkdir -p ${RESULTS_DIR} ${REPORT_DIR}
                    
                    ${JMETER_HOME}/bin/jmeter -n \
                        -t ${TEST_PLAN} \
                        -l ${RESULTS_DIR}/results_${BUILD_NUMBER}.jtl \
                        -e -o ${REPORT_DIR}/report_${BUILD_NUMBER} \
                        -J threads=${THREADS:-100} \
                        -J duration=${DURATION:-300} \
                        -J rampup=${RAMPUP:-30}
                '''
            }
        }
        
        stage('分析结果') {
            steps {
                echo '分析测试结果...'
                sh '''
                    # 检查错误率
                    ERROR_RATE=$(grep -oP 'errorRate="[^"]*"' \
                        ${RESULTS_DIR}/results_${BUILD_NUMBER}.jtl | \
                        head -1 | cut -d'"' -f2)
                    
                    echo "错误率: ${ERROR_RATE}%"
                    
                    # 如果错误率超过阈值,构建失败
                    if (( $(echo "${ERROR_RATE} > 1.0" | bc -l) )); then
                        echo "错误率超过 1%,测试失败!"
                        exit 1
                    fi
                '''
            }
        }
        
        stage('发布报告') {
            steps {
                echo '发布 HTML 报告...'
                publishHTML(target: [
                    allowMissing: false,
                    alwaysLinkToLastBuild: false,
                    keepAll: true,
                    reportDir: "${REPORT_DIR}/report_${BUILD_NUMBER}",
                    reportFiles: 'index.html',
                    reportName: 'JMeter 性能测试报告',
                    reportTitles: ''
                ])
            }
        }
    }
    
    post {
        always {
            echo '清理临时文件...'
            archiveArtifacts artifacts: "${RESULTS_DIR}/*.jtl", allowEmptyArchive: true
        }
        failure {
            echo '性能测试失败!通知团队...'
            mail to: 'qa-team@example.com',
                subject: "性能测试失败 - ${env.JOB_NAME} #${env.BUILD_NUMBER}",
                body: "性能测试未通过,请查看报告:${env.BUILD_URL}JMeter_20Report/"
        }
    }
}
GitHub Actions 集成
yaml 复制代码
# .github/workflows/performance-test.yml
name: Performance Test

on:
  pull_request:
    branches: [main, develop]
  schedule:
    - cron: '0 2 * * *'  # 每天凌晨2点执行

jobs:
  performance-test:
    runs-on: ubuntu-latest
    
    steps:
      - uses: actions/checkout@v4
      
      - name: Setup JDK 17
        uses: actions/setup-java@v4
        with:
          java-version: '17'
          distribution: 'temurin'
      
      - name: Install JMeter
        run: |
          wget -q https://dlcdn.apache.org/jmeter/binaries/apache-jmeter-5.6.3.tgz
          tar -xzf apache-jmeter-5.6.3.tgz
          export JMETER_HOME=$PWD/apache-jmeter-5.6.3
          
          # 安装插件管理器
          wget -q -O $JMETER_HOME/lib/ext/jmeter-plugins-manager.jar \
            https://jmeter-plugins.org/get/
      
      - name: Wait for application
        run: |
          for i in $(seq 1 30); do
            if curl -sf http://api.shop.example.com/health > /dev/null; then
              echo "Application is ready"
              exit 0
            fi
            sleep 10
          done
          exit 1
      
      - name: Run Performance Test
        run: |
          ./apache-jmeter-5.6.3/bin/jmeter -n \
            -t performance_tests/ecommerce_test.jmx \
            -l results.jtl \
            -e -o html_report/ \
            -J threads=100 \
            -J duration=300
      
      - name: Check Results
        run: |
          # 检查错误率
          TOTAL=$(grep -c ',' results.jtl || true)
          FAILURES=$(grep ',false,' results.jtl | wc -l || true)
          echo "Total: $TOTAL, Failures: $FAILURES"
          
          if [ "$FAILURES" -gt 0 ]; then
            echo "有失败的请求!"
            exit 1
          fi
      
      - name: Upload Report
        uses: actions/upload-artifact@v4
        with:
          name: jmeter-report
          path: html_report/
      
      - name: Publish Report to GitHub Pages
        uses: peaceiris/actions-gh-pages@v3
        with:
          github_token: ${{ secrets.GITHUB_TOKEN }}
          publish_dir: ./html_report

9.3 自动化测试框架集成

#mermaid-svg-TVED4DN4ZxSPG9Mo{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-TVED4DN4ZxSPG9Mo .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-TVED4DN4ZxSPG9Mo .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-TVED4DN4ZxSPG9Mo .error-icon{fill:#552222;}#mermaid-svg-TVED4DN4ZxSPG9Mo .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-TVED4DN4ZxSPG9Mo .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-TVED4DN4ZxSPG9Mo .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-TVED4DN4ZxSPG9Mo .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-TVED4DN4ZxSPG9Mo .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-TVED4DN4ZxSPG9Mo .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-TVED4DN4ZxSPG9Mo .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-TVED4DN4ZxSPG9Mo .marker{fill:#333333;stroke:#333333;}#mermaid-svg-TVED4DN4ZxSPG9Mo .marker.cross{stroke:#333333;}#mermaid-svg-TVED4DN4ZxSPG9Mo svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-TVED4DN4ZxSPG9Mo p{margin:0;}#mermaid-svg-TVED4DN4ZxSPG9Mo .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-TVED4DN4ZxSPG9Mo .cluster-label text{fill:#333;}#mermaid-svg-TVED4DN4ZxSPG9Mo .cluster-label span{color:#333;}#mermaid-svg-TVED4DN4ZxSPG9Mo .cluster-label span p{background-color:transparent;}#mermaid-svg-TVED4DN4ZxSPG9Mo .label text,#mermaid-svg-TVED4DN4ZxSPG9Mo span{fill:#333;color:#333;}#mermaid-svg-TVED4DN4ZxSPG9Mo .node rect,#mermaid-svg-TVED4DN4ZxSPG9Mo .node circle,#mermaid-svg-TVED4DN4ZxSPG9Mo .node ellipse,#mermaid-svg-TVED4DN4ZxSPG9Mo .node polygon,#mermaid-svg-TVED4DN4ZxSPG9Mo .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-TVED4DN4ZxSPG9Mo .rough-node .label text,#mermaid-svg-TVED4DN4ZxSPG9Mo .node .label text,#mermaid-svg-TVED4DN4ZxSPG9Mo .image-shape .label,#mermaid-svg-TVED4DN4ZxSPG9Mo .icon-shape .label{text-anchor:middle;}#mermaid-svg-TVED4DN4ZxSPG9Mo .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-TVED4DN4ZxSPG9Mo .rough-node .label,#mermaid-svg-TVED4DN4ZxSPG9Mo .node .label,#mermaid-svg-TVED4DN4ZxSPG9Mo .image-shape .label,#mermaid-svg-TVED4DN4ZxSPG9Mo .icon-shape .label{text-align:center;}#mermaid-svg-TVED4DN4ZxSPG9Mo .node.clickable{cursor:pointer;}#mermaid-svg-TVED4DN4ZxSPG9Mo .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-TVED4DN4ZxSPG9Mo .arrowheadPath{fill:#333333;}#mermaid-svg-TVED4DN4ZxSPG9Mo .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-TVED4DN4ZxSPG9Mo .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-TVED4DN4ZxSPG9Mo .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-TVED4DN4ZxSPG9Mo .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-TVED4DN4ZxSPG9Mo .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-TVED4DN4ZxSPG9Mo .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-TVED4DN4ZxSPG9Mo .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-TVED4DN4ZxSPG9Mo .cluster text{fill:#333;}#mermaid-svg-TVED4DN4ZxSPG9Mo .cluster span{color:#333;}#mermaid-svg-TVED4DN4ZxSPG9Mo div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-TVED4DN4ZxSPG9Mo .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-TVED4DN4ZxSPG9Mo rect.text{fill:none;stroke-width:0;}#mermaid-svg-TVED4DN4ZxSPG9Mo .icon-shape,#mermaid-svg-TVED4DN4ZxSPG9Mo .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-TVED4DN4ZxSPG9Mo .icon-shape p,#mermaid-svg-TVED4DN4ZxSPG9Mo .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-TVED4DN4ZxSPG9Mo .icon-shape .label rect,#mermaid-svg-TVED4DN4ZxSPG9Mo .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-TVED4DN4ZxSPG9Mo .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-TVED4DN4ZxSPG9Mo .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-TVED4DN4ZxSPG9Mo :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} JMeter 自动化测试架构
Git 仓库

JMX + CSV + 脚本
CI/CD 平台

Jenkins/GitHub Actions
JMeter 执行引擎
HTML 报告
Grafana 仪表盘
告警系统

Shell 脚本封装
bash 复制代码
#!/bin/bash
# run_performance_test.sh - JMeter 自动化测试脚本

set -e

# ============ 配置 ============
JMETER_HOME="/opt/apache-jmeter-5.6.3"
TEST_PLAN_DIR="./test_plans"
RESULTS_DIR="./results"
REPORT_DIR="./reports"
TIMESTAMP=$(date +%Y%m%d_%H%M%S)

# 默认参数
THREADS=${THREADS:-100}
DURATION=${DURATION:-300}
RAMPUP=${RAMPUP:-30}
ENV=${ENV:-staging}

# ============ 函数 ============
usage() {
    echo "Usage: $0 [OPTIONS]"
    echo "  -t, --threads    并发线程数 (默认: 100)"
    echo "  -d, --duration   持续时间秒 (默认: 300)"
    echo "  -r, --rampup     启动时间秒 (默认: 30)"
    echo "  -e, --env        环境: staging/production (默认: staging)"
    echo "  -p, --plan       测试计划名称"
    echo "  -h, --help       显示帮助"
    exit 1
}

run_test() {
    local plan_name=$1
    local jmx_file="${TEST_PLAN_DIR}/${plan_name}.jmx"
    local jtl_file="${RESULTS_DIR}/${plan_name}_${TIMESTAMP}.jtl"
    local report_dir="${REPORT_DIR}/${plan_name}_${TIMESTAMP}"
    
    echo "========================================"
    echo "开始执行性能测试"
    echo "测试计划: ${plan_name}"
    echo "并发线程: ${THREADS}"
    echo "持续时间: ${DURATION}s"
    echo "启动时间: ${RAMPUP}s"
    echo "环境: ${ENV}"
    echo "========================================"
    
    # 创建目录
    mkdir -p "${RESULTS_DIR}" "${report_dir}"
    
    # 执行测试
    ${JMETER_HOME}/bin/jmeter -n \
        -t "${jmx_file}" \
        -l "${jtl_file}" \
        -e -o "${report_dir}" \
        -J threads=${THREADS} \
        -J duration=${DURATION} \
        -J rampup=${RAMPUP} \
        -J env=${ENV}
    
    # 分析结果
    analyze_results "${jtl_file}"
}

analyze_results() {
    local jtl_file=$1
    echo ""
    echo "========================================"
    echo "测试结果分析"
    echo "========================================"
    
    # 统计总请求数
    local total=$(tail -n +2 "${jtl_file}" | wc -l)
    echo "总请求数: ${total}"
    
    # 统计失败数
    local failures=$(grep ',false,' "${jtl_file}" | wc -l || echo 0)
    echo "失败请求数: ${failures}"
    
    # 计算错误率
    if [ "$total" -gt 0 ]; then
        local error_rate=$(echo "scale=2; ${failures} * 100 / ${total}" | bc)
        echo "错误率: ${error_rate}%"
        
        if (( $(echo "${error_rate} > 1.0" | bc -l) )); then
            echo "❌ 错误率超过 1%,测试失败!"
            return 1
        else
            echo "✅ 错误率在可接受范围内"
        fi
    fi
    
    echo ""
    echo "HTML 报告已生成,请查看: ${report_dir}/index.html"
}

# ============ 主流程 ============
while [[ $# -gt 0 ]]; do
    case $1 in
        -t|--threads) THREADS="$2"; shift 2 ;;
        -d|--duration) DURATION="$2"; shift 2 ;;
        -r|--rampup) RAMPUP="$2"; shift 2 ;;
        -e|--env) ENV="$2"; shift 2 ;;
        -p|--plan) PLAN="$2"; shift 2 ;;
        -h|--help) usage ;;
        *) echo "未知参数: $1"; usage ;;
    esac
done

if [ -z "${PLAN}" ]; then
    echo "请指定测试计划: -p plan_name"
    usage
fi

run_test "${PLAN}"

第10章 最佳实践与常见问题

10.1 JMeter 最佳实践

餐厅比喻 :最佳实践就像米其林餐厅的经营秘诀------遵循这些规则,你的压测结果会更准确、更可靠。
#mermaid-svg-Ktbi8gNuIIto0GzO{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-Ktbi8gNuIIto0GzO .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-Ktbi8gNuIIto0GzO .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-Ktbi8gNuIIto0GzO .error-icon{fill:#552222;}#mermaid-svg-Ktbi8gNuIIto0GzO .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Ktbi8gNuIIto0GzO .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-Ktbi8gNuIIto0GzO .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Ktbi8gNuIIto0GzO .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Ktbi8gNuIIto0GzO .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-Ktbi8gNuIIto0GzO .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Ktbi8gNuIIto0GzO .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Ktbi8gNuIIto0GzO .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Ktbi8gNuIIto0GzO .marker.cross{stroke:#333333;}#mermaid-svg-Ktbi8gNuIIto0GzO svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Ktbi8gNuIIto0GzO p{margin:0;}#mermaid-svg-Ktbi8gNuIIto0GzO .edge{stroke-width:3;}#mermaid-svg-Ktbi8gNuIIto0GzO .section--1 rect,#mermaid-svg-Ktbi8gNuIIto0GzO .section--1 path,#mermaid-svg-Ktbi8gNuIIto0GzO .section--1 circle,#mermaid-svg-Ktbi8gNuIIto0GzO .section--1 polygon,#mermaid-svg-Ktbi8gNuIIto0GzO .section--1 path{fill:hsl(240, 100%, 76.2745098039%);}#mermaid-svg-Ktbi8gNuIIto0GzO .section--1 text{fill:#ffffff;}#mermaid-svg-Ktbi8gNuIIto0GzO .node-icon--1{font-size:40px;color:#ffffff;}#mermaid-svg-Ktbi8gNuIIto0GzO .section-edge--1{stroke:hsl(240, 100%, 76.2745098039%);}#mermaid-svg-Ktbi8gNuIIto0GzO .edge-depth--1{stroke-width:17;}#mermaid-svg-Ktbi8gNuIIto0GzO .section--1 line{stroke:hsl(60, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-Ktbi8gNuIIto0GzO .disabled,#mermaid-svg-Ktbi8gNuIIto0GzO .disabled circle,#mermaid-svg-Ktbi8gNuIIto0GzO .disabled text{fill:lightgray;}#mermaid-svg-Ktbi8gNuIIto0GzO .disabled text{fill:#efefef;}#mermaid-svg-Ktbi8gNuIIto0GzO .section-0 rect,#mermaid-svg-Ktbi8gNuIIto0GzO .section-0 path,#mermaid-svg-Ktbi8gNuIIto0GzO .section-0 circle,#mermaid-svg-Ktbi8gNuIIto0GzO .section-0 polygon,#mermaid-svg-Ktbi8gNuIIto0GzO .section-0 path{fill:hsl(60, 100%, 73.5294117647%);}#mermaid-svg-Ktbi8gNuIIto0GzO .section-0 text{fill:black;}#mermaid-svg-Ktbi8gNuIIto0GzO .node-icon-0{font-size:40px;color:black;}#mermaid-svg-Ktbi8gNuIIto0GzO .section-edge-0{stroke:hsl(60, 100%, 73.5294117647%);}#mermaid-svg-Ktbi8gNuIIto0GzO .edge-depth-0{stroke-width:14;}#mermaid-svg-Ktbi8gNuIIto0GzO .section-0 line{stroke:hsl(240, 100%, 83.5294117647%);stroke-width:3;}#mermaid-svg-Ktbi8gNuIIto0GzO .disabled,#mermaid-svg-Ktbi8gNuIIto0GzO .disabled circle,#mermaid-svg-Ktbi8gNuIIto0GzO .disabled text{fill:lightgray;}#mermaid-svg-Ktbi8gNuIIto0GzO .disabled text{fill:#efefef;}#mermaid-svg-Ktbi8gNuIIto0GzO .section-1 rect,#mermaid-svg-Ktbi8gNuIIto0GzO .section-1 path,#mermaid-svg-Ktbi8gNuIIto0GzO .section-1 circle,#mermaid-svg-Ktbi8gNuIIto0GzO .section-1 polygon,#mermaid-svg-Ktbi8gNuIIto0GzO .section-1 path{fill:hsl(80, 100%, 76.2745098039%);}#mermaid-svg-Ktbi8gNuIIto0GzO .section-1 text{fill:black;}#mermaid-svg-Ktbi8gNuIIto0GzO .node-icon-1{font-size:40px;color:black;}#mermaid-svg-Ktbi8gNuIIto0GzO .section-edge-1{stroke:hsl(80, 100%, 76.2745098039%);}#mermaid-svg-Ktbi8gNuIIto0GzO .edge-depth-1{stroke-width:11;}#mermaid-svg-Ktbi8gNuIIto0GzO .section-1 line{stroke:hsl(260, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-Ktbi8gNuIIto0GzO .disabled,#mermaid-svg-Ktbi8gNuIIto0GzO .disabled circle,#mermaid-svg-Ktbi8gNuIIto0GzO .disabled text{fill:lightgray;}#mermaid-svg-Ktbi8gNuIIto0GzO .disabled text{fill:#efefef;}#mermaid-svg-Ktbi8gNuIIto0GzO .section-2 rect,#mermaid-svg-Ktbi8gNuIIto0GzO .section-2 path,#mermaid-svg-Ktbi8gNuIIto0GzO .section-2 circle,#mermaid-svg-Ktbi8gNuIIto0GzO .section-2 polygon,#mermaid-svg-Ktbi8gNuIIto0GzO .section-2 path{fill:hsl(270, 100%, 76.2745098039%);}#mermaid-svg-Ktbi8gNuIIto0GzO .section-2 text{fill:#ffffff;}#mermaid-svg-Ktbi8gNuIIto0GzO .node-icon-2{font-size:40px;color:#ffffff;}#mermaid-svg-Ktbi8gNuIIto0GzO .section-edge-2{stroke:hsl(270, 100%, 76.2745098039%);}#mermaid-svg-Ktbi8gNuIIto0GzO .edge-depth-2{stroke-width:8;}#mermaid-svg-Ktbi8gNuIIto0GzO .section-2 line{stroke:hsl(90, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-Ktbi8gNuIIto0GzO .disabled,#mermaid-svg-Ktbi8gNuIIto0GzO .disabled circle,#mermaid-svg-Ktbi8gNuIIto0GzO .disabled text{fill:lightgray;}#mermaid-svg-Ktbi8gNuIIto0GzO .disabled text{fill:#efefef;}#mermaid-svg-Ktbi8gNuIIto0GzO .section-3 rect,#mermaid-svg-Ktbi8gNuIIto0GzO .section-3 path,#mermaid-svg-Ktbi8gNuIIto0GzO .section-3 circle,#mermaid-svg-Ktbi8gNuIIto0GzO .section-3 polygon,#mermaid-svg-Ktbi8gNuIIto0GzO .section-3 path{fill:hsl(300, 100%, 76.2745098039%);}#mermaid-svg-Ktbi8gNuIIto0GzO .section-3 text{fill:black;}#mermaid-svg-Ktbi8gNuIIto0GzO .node-icon-3{font-size:40px;color:black;}#mermaid-svg-Ktbi8gNuIIto0GzO .section-edge-3{stroke:hsl(300, 100%, 76.2745098039%);}#mermaid-svg-Ktbi8gNuIIto0GzO .edge-depth-3{stroke-width:5;}#mermaid-svg-Ktbi8gNuIIto0GzO .section-3 line{stroke:hsl(120, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-Ktbi8gNuIIto0GzO .disabled,#mermaid-svg-Ktbi8gNuIIto0GzO .disabled circle,#mermaid-svg-Ktbi8gNuIIto0GzO .disabled text{fill:lightgray;}#mermaid-svg-Ktbi8gNuIIto0GzO .disabled text{fill:#efefef;}#mermaid-svg-Ktbi8gNuIIto0GzO .section-4 rect,#mermaid-svg-Ktbi8gNuIIto0GzO .section-4 path,#mermaid-svg-Ktbi8gNuIIto0GzO .section-4 circle,#mermaid-svg-Ktbi8gNuIIto0GzO .section-4 polygon,#mermaid-svg-Ktbi8gNuIIto0GzO .section-4 path{fill:hsl(330, 100%, 76.2745098039%);}#mermaid-svg-Ktbi8gNuIIto0GzO .section-4 text{fill:black;}#mermaid-svg-Ktbi8gNuIIto0GzO .node-icon-4{font-size:40px;color:black;}#mermaid-svg-Ktbi8gNuIIto0GzO .section-edge-4{stroke:hsl(330, 100%, 76.2745098039%);}#mermaid-svg-Ktbi8gNuIIto0GzO .edge-depth-4{stroke-width:2;}#mermaid-svg-Ktbi8gNuIIto0GzO .section-4 line{stroke:hsl(150, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-Ktbi8gNuIIto0GzO .disabled,#mermaid-svg-Ktbi8gNuIIto0GzO .disabled circle,#mermaid-svg-Ktbi8gNuIIto0GzO .disabled text{fill:lightgray;}#mermaid-svg-Ktbi8gNuIIto0GzO .disabled text{fill:#efefef;}#mermaid-svg-Ktbi8gNuIIto0GzO .section-5 rect,#mermaid-svg-Ktbi8gNuIIto0GzO .section-5 path,#mermaid-svg-Ktbi8gNuIIto0GzO .section-5 circle,#mermaid-svg-Ktbi8gNuIIto0GzO .section-5 polygon,#mermaid-svg-Ktbi8gNuIIto0GzO .section-5 path{fill:hsl(0, 100%, 76.2745098039%);}#mermaid-svg-Ktbi8gNuIIto0GzO .section-5 text{fill:black;}#mermaid-svg-Ktbi8gNuIIto0GzO .node-icon-5{font-size:40px;color:black;}#mermaid-svg-Ktbi8gNuIIto0GzO .section-edge-5{stroke:hsl(0, 100%, 76.2745098039%);}#mermaid-svg-Ktbi8gNuIIto0GzO .edge-depth-5{stroke-width:-1;}#mermaid-svg-Ktbi8gNuIIto0GzO .section-5 line{stroke:hsl(180, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-Ktbi8gNuIIto0GzO .disabled,#mermaid-svg-Ktbi8gNuIIto0GzO .disabled circle,#mermaid-svg-Ktbi8gNuIIto0GzO .disabled text{fill:lightgray;}#mermaid-svg-Ktbi8gNuIIto0GzO .disabled text{fill:#efefef;}#mermaid-svg-Ktbi8gNuIIto0GzO .section-6 rect,#mermaid-svg-Ktbi8gNuIIto0GzO .section-6 path,#mermaid-svg-Ktbi8gNuIIto0GzO .section-6 circle,#mermaid-svg-Ktbi8gNuIIto0GzO .section-6 polygon,#mermaid-svg-Ktbi8gNuIIto0GzO .section-6 path{fill:hsl(30, 100%, 76.2745098039%);}#mermaid-svg-Ktbi8gNuIIto0GzO .section-6 text{fill:black;}#mermaid-svg-Ktbi8gNuIIto0GzO .node-icon-6{font-size:40px;color:black;}#mermaid-svg-Ktbi8gNuIIto0GzO .section-edge-6{stroke:hsl(30, 100%, 76.2745098039%);}#mermaid-svg-Ktbi8gNuIIto0GzO .edge-depth-6{stroke-width:-4;}#mermaid-svg-Ktbi8gNuIIto0GzO .section-6 line{stroke:hsl(210, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-Ktbi8gNuIIto0GzO .disabled,#mermaid-svg-Ktbi8gNuIIto0GzO .disabled circle,#mermaid-svg-Ktbi8gNuIIto0GzO .disabled text{fill:lightgray;}#mermaid-svg-Ktbi8gNuIIto0GzO .disabled text{fill:#efefef;}#mermaid-svg-Ktbi8gNuIIto0GzO .section-7 rect,#mermaid-svg-Ktbi8gNuIIto0GzO .section-7 path,#mermaid-svg-Ktbi8gNuIIto0GzO .section-7 circle,#mermaid-svg-Ktbi8gNuIIto0GzO .section-7 polygon,#mermaid-svg-Ktbi8gNuIIto0GzO .section-7 path{fill:hsl(90, 100%, 76.2745098039%);}#mermaid-svg-Ktbi8gNuIIto0GzO .section-7 text{fill:black;}#mermaid-svg-Ktbi8gNuIIto0GzO .node-icon-7{font-size:40px;color:black;}#mermaid-svg-Ktbi8gNuIIto0GzO .section-edge-7{stroke:hsl(90, 100%, 76.2745098039%);}#mermaid-svg-Ktbi8gNuIIto0GzO .edge-depth-7{stroke-width:-7;}#mermaid-svg-Ktbi8gNuIIto0GzO .section-7 line{stroke:hsl(270, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-Ktbi8gNuIIto0GzO .disabled,#mermaid-svg-Ktbi8gNuIIto0GzO .disabled circle,#mermaid-svg-Ktbi8gNuIIto0GzO .disabled text{fill:lightgray;}#mermaid-svg-Ktbi8gNuIIto0GzO .disabled text{fill:#efefef;}#mermaid-svg-Ktbi8gNuIIto0GzO .section-8 rect,#mermaid-svg-Ktbi8gNuIIto0GzO .section-8 path,#mermaid-svg-Ktbi8gNuIIto0GzO .section-8 circle,#mermaid-svg-Ktbi8gNuIIto0GzO .section-8 polygon,#mermaid-svg-Ktbi8gNuIIto0GzO .section-8 path{fill:hsl(150, 100%, 76.2745098039%);}#mermaid-svg-Ktbi8gNuIIto0GzO .section-8 text{fill:black;}#mermaid-svg-Ktbi8gNuIIto0GzO .node-icon-8{font-size:40px;color:black;}#mermaid-svg-Ktbi8gNuIIto0GzO .section-edge-8{stroke:hsl(150, 100%, 76.2745098039%);}#mermaid-svg-Ktbi8gNuIIto0GzO .edge-depth-8{stroke-width:-10;}#mermaid-svg-Ktbi8gNuIIto0GzO .section-8 line{stroke:hsl(330, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-Ktbi8gNuIIto0GzO .disabled,#mermaid-svg-Ktbi8gNuIIto0GzO .disabled circle,#mermaid-svg-Ktbi8gNuIIto0GzO .disabled text{fill:lightgray;}#mermaid-svg-Ktbi8gNuIIto0GzO .disabled text{fill:#efefef;}#mermaid-svg-Ktbi8gNuIIto0GzO .section-9 rect,#mermaid-svg-Ktbi8gNuIIto0GzO .section-9 path,#mermaid-svg-Ktbi8gNuIIto0GzO .section-9 circle,#mermaid-svg-Ktbi8gNuIIto0GzO .section-9 polygon,#mermaid-svg-Ktbi8gNuIIto0GzO .section-9 path{fill:hsl(180, 100%, 76.2745098039%);}#mermaid-svg-Ktbi8gNuIIto0GzO .section-9 text{fill:black;}#mermaid-svg-Ktbi8gNuIIto0GzO .node-icon-9{font-size:40px;color:black;}#mermaid-svg-Ktbi8gNuIIto0GzO .section-edge-9{stroke:hsl(180, 100%, 76.2745098039%);}#mermaid-svg-Ktbi8gNuIIto0GzO .edge-depth-9{stroke-width:-13;}#mermaid-svg-Ktbi8gNuIIto0GzO .section-9 line{stroke:hsl(0, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-Ktbi8gNuIIto0GzO .disabled,#mermaid-svg-Ktbi8gNuIIto0GzO .disabled circle,#mermaid-svg-Ktbi8gNuIIto0GzO .disabled text{fill:lightgray;}#mermaid-svg-Ktbi8gNuIIto0GzO .disabled text{fill:#efefef;}#mermaid-svg-Ktbi8gNuIIto0GzO .section-10 rect,#mermaid-svg-Ktbi8gNuIIto0GzO .section-10 path,#mermaid-svg-Ktbi8gNuIIto0GzO .section-10 circle,#mermaid-svg-Ktbi8gNuIIto0GzO .section-10 polygon,#mermaid-svg-Ktbi8gNuIIto0GzO .section-10 path{fill:hsl(210, 100%, 76.2745098039%);}#mermaid-svg-Ktbi8gNuIIto0GzO .section-10 text{fill:black;}#mermaid-svg-Ktbi8gNuIIto0GzO .node-icon-10{font-size:40px;color:black;}#mermaid-svg-Ktbi8gNuIIto0GzO .section-edge-10{stroke:hsl(210, 100%, 76.2745098039%);}#mermaid-svg-Ktbi8gNuIIto0GzO .edge-depth-10{stroke-width:-16;}#mermaid-svg-Ktbi8gNuIIto0GzO .section-10 line{stroke:hsl(30, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-Ktbi8gNuIIto0GzO .disabled,#mermaid-svg-Ktbi8gNuIIto0GzO .disabled circle,#mermaid-svg-Ktbi8gNuIIto0GzO .disabled text{fill:lightgray;}#mermaid-svg-Ktbi8gNuIIto0GzO .disabled text{fill:#efefef;}#mermaid-svg-Ktbi8gNuIIto0GzO .section-root rect,#mermaid-svg-Ktbi8gNuIIto0GzO .section-root path,#mermaid-svg-Ktbi8gNuIIto0GzO .section-root circle,#mermaid-svg-Ktbi8gNuIIto0GzO .section-root polygon{fill:hsl(240, 100%, 46.2745098039%);}#mermaid-svg-Ktbi8gNuIIto0GzO .section-root text{fill:#ffffff;}#mermaid-svg-Ktbi8gNuIIto0GzO .section-root span{color:#ffffff;}#mermaid-svg-Ktbi8gNuIIto0GzO .section-2 span{color:#ffffff;}#mermaid-svg-Ktbi8gNuIIto0GzO .icon-container{height:100%;display:flex;justify-content:center;align-items:center;}#mermaid-svg-Ktbi8gNuIIto0GzO .edge{fill:none;}#mermaid-svg-Ktbi8gNuIIto0GzO .mindmap-node-label{dy:1em;alignment-baseline:middle;text-anchor:middle;dominant-baseline:middle;text-align:center;}#mermaid-svg-Ktbi8gNuIIto0GzO :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} JMeter 最佳实践
脚本设计
使用合理的定时器
避免过度使用断言
合理使用参数化
使用事务控制器
关联要准确
执行策略
始终使用非GUI模式
分布式压测
分阶段加压
先调试后压测
资源管理
调整JVM内存
禁用不必要的监听器
使用CSV而非内存变量
控制线程数
结果分析
关注90%线而非平均值
使用HTML报告
保存原始JTL文件
多次测试取平均值
维护管理
版本控制JMX文件
模块化脚本
文档化测试场景
定期更新JMeter版本

1. 脚本编写最佳实践
复制代码
✅ DO(推荐做法):
├── 使用 Throughput Controller 控制业务比例
├── 使用 Transaction Controller 统计事务时间
├── 使用 Gaussian Random Timer 模拟真实用户
├── 使用 JSR223 + Groovy(而非 BeanShell)
├── 使用 CSV Data Set Config 进行参数化
├── 使用 JSON Path 提取 JSON 响应
├── 在非GUI模式下执行压测
├── 将 JMX 文件纳入版本控制
└── 使用变量管理环境配置

❌ DON'T(避免做法):
├── 不要在压测时启用 View Results Tree
├── 不要使用 Constant Timer 模拟真实用户
├── 不要在压测时使用 Functional Mode
├── 不要忽略 Ramp-up 时间(设为0)
├── 不要使用 BeanShell(性能差)
├── 不要在 Sampler 中硬编码数据
├── 不要忽略错误率
├── 不要只看平均响应时间
└── 不要在 GUI 模式下跑大规模压测
2. 性能优化技巧
groovy 复制代码
// ✅ 推荐:JSR223 + Groovy(勾选 Cache compiled script)
// 性能比 BeanShell 快 10 倍以上!

// JSR223 PreProcessor - Groovy
def startTime = System.currentTimeMillis()
// ... 你的逻辑 ...
def elapsed = System.currentTimeMillis() - startTime
log.info("处理耗时: ${elapsed}ms")

// ❌ 避免:BeanShell(每次都要解释执行)
// 性能差,不推荐
复制代码
资源优化清单:
┌──────────────────────────────────────────────────┐
│ 1. JVM 内存:-Xms4g -Xmx8g -XX:+UseG1GC       │
│ 2. 禁用监听器:压测时只保留必要的监听器          │
│ 3. 关闭 CSS/JPEG 过滤:不需要下载静态资源       │
│ 4. 使用 CSV 而非 User Defined Variables          │
│ 5. 减少 Assertion 数量(压测时)               │
│ 6. 使用分布式压测分散压力                      │
│ 7. 增加 Linux 文件句柄限制                     │
│    ulimit -n 65535                             │
│ 8. 使用 HTTP Keep-Alive 复用连接               │
│ 9. 关闭 DNS 缓存问题                           │
│ 10. 使用最新的 JMeter 版本                     │
└──────────────────────────────────────────────────┘
3. 测试数据准备

#mermaid-svg-J7EklT1vcR3iCgJC{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-J7EklT1vcR3iCgJC .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-J7EklT1vcR3iCgJC .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-J7EklT1vcR3iCgJC .error-icon{fill:#552222;}#mermaid-svg-J7EklT1vcR3iCgJC .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-J7EklT1vcR3iCgJC .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-J7EklT1vcR3iCgJC .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-J7EklT1vcR3iCgJC .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-J7EklT1vcR3iCgJC .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-J7EklT1vcR3iCgJC .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-J7EklT1vcR3iCgJC .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-J7EklT1vcR3iCgJC .marker{fill:#333333;stroke:#333333;}#mermaid-svg-J7EklT1vcR3iCgJC .marker.cross{stroke:#333333;}#mermaid-svg-J7EklT1vcR3iCgJC svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-J7EklT1vcR3iCgJC p{margin:0;}#mermaid-svg-J7EklT1vcR3iCgJC .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-J7EklT1vcR3iCgJC .cluster-label text{fill:#333;}#mermaid-svg-J7EklT1vcR3iCgJC .cluster-label span{color:#333;}#mermaid-svg-J7EklT1vcR3iCgJC .cluster-label span p{background-color:transparent;}#mermaid-svg-J7EklT1vcR3iCgJC .label text,#mermaid-svg-J7EklT1vcR3iCgJC span{fill:#333;color:#333;}#mermaid-svg-J7EklT1vcR3iCgJC .node rect,#mermaid-svg-J7EklT1vcR3iCgJC .node circle,#mermaid-svg-J7EklT1vcR3iCgJC .node ellipse,#mermaid-svg-J7EklT1vcR3iCgJC .node polygon,#mermaid-svg-J7EklT1vcR3iCgJC .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-J7EklT1vcR3iCgJC .rough-node .label text,#mermaid-svg-J7EklT1vcR3iCgJC .node .label text,#mermaid-svg-J7EklT1vcR3iCgJC .image-shape .label,#mermaid-svg-J7EklT1vcR3iCgJC .icon-shape .label{text-anchor:middle;}#mermaid-svg-J7EklT1vcR3iCgJC .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-J7EklT1vcR3iCgJC .rough-node .label,#mermaid-svg-J7EklT1vcR3iCgJC .node .label,#mermaid-svg-J7EklT1vcR3iCgJC .image-shape .label,#mermaid-svg-J7EklT1vcR3iCgJC .icon-shape .label{text-align:center;}#mermaid-svg-J7EklT1vcR3iCgJC .node.clickable{cursor:pointer;}#mermaid-svg-J7EklT1vcR3iCgJC .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-J7EklT1vcR3iCgJC .arrowheadPath{fill:#333333;}#mermaid-svg-J7EklT1vcR3iCgJC .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-J7EklT1vcR3iCgJC .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-J7EklT1vcR3iCgJC .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-J7EklT1vcR3iCgJC .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-J7EklT1vcR3iCgJC .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-J7EklT1vcR3iCgJC .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-J7EklT1vcR3iCgJC .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-J7EklT1vcR3iCgJC .cluster text{fill:#333;}#mermaid-svg-J7EklT1vcR3iCgJC .cluster span{color:#333;}#mermaid-svg-J7EklT1vcR3iCgJC div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-J7EklT1vcR3iCgJC .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-J7EklT1vcR3iCgJC rect.text{fill:none;stroke-width:0;}#mermaid-svg-J7EklT1vcR3iCgJC .icon-shape,#mermaid-svg-J7EklT1vcR3iCgJC .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-J7EklT1vcR3iCgJC .icon-shape p,#mermaid-svg-J7EklT1vcR3iCgJC .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-J7EklT1vcR3iCgJC .icon-shape .label rect,#mermaid-svg-J7EklT1vcR3iCgJC .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-J7EklT1vcR3iCgJC .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-J7EklT1vcR3iCgJC .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-J7EklT1vcR3iCgJC :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 测试数据策略
数据量
数据多样性
数据隔离
CSV数据量 ≥ 线程数 × 循环次数
避免数据用完后循环导致缓存命中
覆盖各种边界值
包含正常和异常数据
使用独立的测试数据
避免影响生产数据
测试后清理数据

10.2 常见问题与解决方案

问题1:内存溢出 (OutOfMemoryError)
复制代码
现象:
java.lang.OutOfMemoryError: Java heap space

原因:
1. JVM 堆内存不够
2. 监听器保存了太多响应数据
3. 脚本中创建了大量对象

解决方案:
1. 增大 JVM 内存
   HEAP="-Xms4g -Xmx8g"
   
2. 压测时禁用 View Results Tree
   只保留 Aggregate Report
   
3. 在 Listener 中限制保存的数据量
   Configure → 只保存错误样本
   
4. 使用 JSR223 脚本时注意释放资源
问题2:CPU 使用率过高
复制代码
现象:
JMeter 进程 CPU 100%,但吞吐量很低

原因:
1. 线程数太多(超过 CPU 核心数太多)
2. 使用了 BeanShell 脚本
3. 正则表达式太复杂
4. 启用了太多监听器

解决方案:
1. 减少单机线程数(推荐 200~500)
2. 改用 JSR223 + Groovy
3. 优化正则表达式
4. 使用分布式压测
5. 禁用不必要的监听器
问题3:响应时间不准确
复制代码
现象:
JMeter 报告的响应时间比实际用户体验慢很多

原因:
1. 没有使用定时器(请求间隔为0)
2. 服务器端有缓存
3. DNS 解析耗时
4. SSL 握手耗时

解决方案:
1. 添加合理的定时器
2. 使用 HTTP Cache Manager
3. 在 DNS Config 中配置 DNS 服务器
4. 使用 HTTP Request Defaults 配置 Keep-Alive
5. 注意区分:
   - JMeter 响应时间 = 从发送请求到收到完整响应
   - 用户体验时间 = 还包括页面渲染时间
问题4:分布式测试连接失败
复制代码
现象:
java.rmi.ConnectException: Connection refused

排查步骤:
1. 检查 Slave 的 jmeter-server 是否启动
   ps aux | grep jmeter-server
   
2. 检查防火墙是否开放端口
   sudo firewall-cmd --list-ports
   sudo firewall-cmd --add-port=1099/tcp
   
3. 检查 remote_hosts 配置是否正确
   cat jmeter.properties | grep remote_hosts
   
4. 检查网络连通性
   telnet slave_ip 1099
   
5. 检查 JDK 版本是否一致
   java -version(Master 和 Slave)
   
6. 检查 JMeter 版本是否一致
   jmeter --version
问题5:CSV 数据读取问题
复制代码
现象:
所有用户使用了相同的数据 / 数据读取不到

原因:
1. Sharing mode 设置不正确
2. CSV 文件路径不对
3. 变量名与 CSV 列头不匹配
4. 文件编码问题

解决方案:
1. Sharing mode 选择:
   - All threads: 所有线程共享(轮流使用不同行)
   - Current thread group: 当前线程组共享
   - Current thread: 每个线程独立使用(每个用户从第一行开始)
   
2. 使用绝对路径或相对于 JMX 的路径
   
3. 确保 Variable Names 与 CSV 列头一致
   
4. 确保 CSV 文件为 UTF-8 编码

10.3 性能测试流程总结

#mermaid-svg-dBuxHZlV24VpynZw{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-dBuxHZlV24VpynZw .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-dBuxHZlV24VpynZw .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-dBuxHZlV24VpynZw .error-icon{fill:#552222;}#mermaid-svg-dBuxHZlV24VpynZw .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-dBuxHZlV24VpynZw .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-dBuxHZlV24VpynZw .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-dBuxHZlV24VpynZw .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-dBuxHZlV24VpynZw .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-dBuxHZlV24VpynZw .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-dBuxHZlV24VpynZw .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-dBuxHZlV24VpynZw .marker{fill:#333333;stroke:#333333;}#mermaid-svg-dBuxHZlV24VpynZw .marker.cross{stroke:#333333;}#mermaid-svg-dBuxHZlV24VpynZw svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-dBuxHZlV24VpynZw p{margin:0;}#mermaid-svg-dBuxHZlV24VpynZw .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-dBuxHZlV24VpynZw .cluster-label text{fill:#333;}#mermaid-svg-dBuxHZlV24VpynZw .cluster-label span{color:#333;}#mermaid-svg-dBuxHZlV24VpynZw .cluster-label span p{background-color:transparent;}#mermaid-svg-dBuxHZlV24VpynZw .label text,#mermaid-svg-dBuxHZlV24VpynZw span{fill:#333;color:#333;}#mermaid-svg-dBuxHZlV24VpynZw .node rect,#mermaid-svg-dBuxHZlV24VpynZw .node circle,#mermaid-svg-dBuxHZlV24VpynZw .node ellipse,#mermaid-svg-dBuxHZlV24VpynZw .node polygon,#mermaid-svg-dBuxHZlV24VpynZw .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-dBuxHZlV24VpynZw .rough-node .label text,#mermaid-svg-dBuxHZlV24VpynZw .node .label text,#mermaid-svg-dBuxHZlV24VpynZw .image-shape .label,#mermaid-svg-dBuxHZlV24VpynZw .icon-shape .label{text-anchor:middle;}#mermaid-svg-dBuxHZlV24VpynZw .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-dBuxHZlV24VpynZw .rough-node .label,#mermaid-svg-dBuxHZlV24VpynZw .node .label,#mermaid-svg-dBuxHZlV24VpynZw .image-shape .label,#mermaid-svg-dBuxHZlV24VpynZw .icon-shape .label{text-align:center;}#mermaid-svg-dBuxHZlV24VpynZw .node.clickable{cursor:pointer;}#mermaid-svg-dBuxHZlV24VpynZw .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-dBuxHZlV24VpynZw .arrowheadPath{fill:#333333;}#mermaid-svg-dBuxHZlV24VpynZw .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-dBuxHZlV24VpynZw .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-dBuxHZlV24VpynZw .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-dBuxHZlV24VpynZw .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-dBuxHZlV24VpynZw .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-dBuxHZlV24VpynZw .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-dBuxHZlV24VpynZw .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-dBuxHZlV24VpynZw .cluster text{fill:#333;}#mermaid-svg-dBuxHZlV24VpynZw .cluster span{color:#333;}#mermaid-svg-dBuxHZlV24VpynZw div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-dBuxHZlV24VpynZw .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-dBuxHZlV24VpynZw rect.text{fill:none;stroke-width:0;}#mermaid-svg-dBuxHZlV24VpynZw .icon-shape,#mermaid-svg-dBuxHZlV24VpynZw .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-dBuxHZlV24VpynZw .icon-shape p,#mermaid-svg-dBuxHZlV24VpynZw .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-dBuxHZlV24VpynZw .icon-shape .label rect,#mermaid-svg-dBuxHZlV24VpynZw .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-dBuxHZlV24VpynZw .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-dBuxHZlV24VpynZw .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-dBuxHZlV24VpynZw :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 是

  1. 需求分析

确定测试目标
2. 测试设计

设计场景和指标
3. 环境准备

搭建测试环境
4. 脚本开发

编写JMeter脚本
5. 脚本调试

单用户验证
6. 预跑验证

小规模试跑
7. 正式压测

执行测试
8. 结果分析

分析数据
达标?
9. 出具报告

总结归档
10. 调优

分析瓶颈

10.4 JMeter 属性速查表

bash 复制代码
# jmeter.properties 常用配置

# 远程主机列表
remote_hosts=127.0.0.1

# 服务器端口
server_port=1099

# HTTP 客户端
httpclient.version=4.5.13
httpclient.socket.timeout=30000
httpclient.connect.timeout=10000

# 缓存
# 清除缓存每个迭代
cache_manager.clear_each_iter=true

# DNS
dns_resolver=java.net.InetAddress.getAllByName

# 结果文件
jmeter.save.saveservice.output_format=csv
jmeter.save.saveservice.timestamp_format=yyyy/MM/dd HH:mm:ss

# 要保存的字段(节省磁盘空间)
jmeter.save.saveservice.assertion_results_failure_message=true
jmeter.save.saveservice.thread_counts=true
jmeter.save.saveservice.latency=true

10.5 学习路线图

#mermaid-svg-DhIo8j85u5idqlFx{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-DhIo8j85u5idqlFx .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-DhIo8j85u5idqlFx .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-DhIo8j85u5idqlFx .error-icon{fill:#552222;}#mermaid-svg-DhIo8j85u5idqlFx .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-DhIo8j85u5idqlFx .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-DhIo8j85u5idqlFx .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-DhIo8j85u5idqlFx .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-DhIo8j85u5idqlFx .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-DhIo8j85u5idqlFx .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-DhIo8j85u5idqlFx .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-DhIo8j85u5idqlFx .marker{fill:#333333;stroke:#333333;}#mermaid-svg-DhIo8j85u5idqlFx .marker.cross{stroke:#333333;}#mermaid-svg-DhIo8j85u5idqlFx svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-DhIo8j85u5idqlFx p{margin:0;}#mermaid-svg-DhIo8j85u5idqlFx .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-DhIo8j85u5idqlFx .cluster-label text{fill:#333;}#mermaid-svg-DhIo8j85u5idqlFx .cluster-label span{color:#333;}#mermaid-svg-DhIo8j85u5idqlFx .cluster-label span p{background-color:transparent;}#mermaid-svg-DhIo8j85u5idqlFx .label text,#mermaid-svg-DhIo8j85u5idqlFx span{fill:#333;color:#333;}#mermaid-svg-DhIo8j85u5idqlFx .node rect,#mermaid-svg-DhIo8j85u5idqlFx .node circle,#mermaid-svg-DhIo8j85u5idqlFx .node ellipse,#mermaid-svg-DhIo8j85u5idqlFx .node polygon,#mermaid-svg-DhIo8j85u5idqlFx .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-DhIo8j85u5idqlFx .rough-node .label text,#mermaid-svg-DhIo8j85u5idqlFx .node .label text,#mermaid-svg-DhIo8j85u5idqlFx .image-shape .label,#mermaid-svg-DhIo8j85u5idqlFx .icon-shape .label{text-anchor:middle;}#mermaid-svg-DhIo8j85u5idqlFx .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-DhIo8j85u5idqlFx .rough-node .label,#mermaid-svg-DhIo8j85u5idqlFx .node .label,#mermaid-svg-DhIo8j85u5idqlFx .image-shape .label,#mermaid-svg-DhIo8j85u5idqlFx .icon-shape .label{text-align:center;}#mermaid-svg-DhIo8j85u5idqlFx .node.clickable{cursor:pointer;}#mermaid-svg-DhIo8j85u5idqlFx .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-DhIo8j85u5idqlFx .arrowheadPath{fill:#333333;}#mermaid-svg-DhIo8j85u5idqlFx .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-DhIo8j85u5idqlFx .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-DhIo8j85u5idqlFx .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-DhIo8j85u5idqlFx .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-DhIo8j85u5idqlFx .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-DhIo8j85u5idqlFx .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-DhIo8j85u5idqlFx .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-DhIo8j85u5idqlFx .cluster text{fill:#333;}#mermaid-svg-DhIo8j85u5idqlFx .cluster span{color:#333;}#mermaid-svg-DhIo8j85u5idqlFx div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-DhIo8j85u5idqlFx .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-DhIo8j85u5idqlFx rect.text{fill:none;stroke-width:0;}#mermaid-svg-DhIo8j85u5idqlFx .icon-shape,#mermaid-svg-DhIo8j85u5idqlFx .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-DhIo8j85u5idqlFx .icon-shape p,#mermaid-svg-DhIo8j85u5idqlFx .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-DhIo8j85u5idqlFx .icon-shape .label rect,#mermaid-svg-DhIo8j85u5idqlFx .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-DhIo8j85u5idqlFx .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-DhIo8j85u5idqlFx .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-DhIo8j85u5idqlFx :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 专家阶段 (持续)
高级阶段 (1-2月)
进阶阶段 (2-4周)
入门阶段 (1-2周)
安装配置
HTTP请求测试
参数化
断言
关联
逻辑控制器
定时器
脚本调试
分布式压测
插件使用
Groovy脚本
CI/CD集成
自定义插件开发
性能调优
测试平台建设
团队培训

10.6 参考资源

资源 地址 说明
JMeter 官网 https://jmeter.apache.org/ 官方文档和下载
JMeter Wiki https://cwiki.apache.org/confluence/display/JMETER 详细文档
JMeter 插件 https://jmeter-plugins.org/ 插件市场
JMeter GitHub https://github.com/apache/jmeter 源代码
JMeter 邮件列表 jmeter-user@apache.org 社区讨论

附录 A:JMeter XML 配置模板

完整的 HTTP 测试计划模板

xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<jmeterTestPlan version="1.2" properties="5.0" jmeter="5.6.3">
  <hashTree>
    <TestPlan guiclass="TestPlanGui" testclass="TestPlan" testname="HTTP 测试模板">
      <boolProp name="TestPlan.functional_mode">false</boolProp>
      <boolProp name="TestPlan.serialize_threadgroups">false</boolProp>
      <stringProp name="TestPlan.comments">JMeter HTTP 性能测试模板</stringProp>
    </TestPlan>
    <hashTree>
      <!-- 全局变量 -->
      <Arguments guiclass="ArgumentsPanel" testclass="Arguments" testname="全局变量">
        <collectionProp name="Arguments.arguments">
          <elementProp name="BASE_URL" elementType="Argument">
            <stringProp name="Argument.name">BASE_URL</stringProp>
            <stringProp name="Argument.value">api.example.com</stringProp>
          </elementProp>
          <elementProp name="BASE_PORT" elementType="Argument">
            <stringProp name="Argument.name">BASE_PORT</stringProp>
            <stringProp name="Argument.value">443</stringProp>
          </elementProp>
          <elementProp name="BASE_PROTOCOL" elementType="Argument">
            <stringProp name="Argument.name">BASE_PROTOCOL</stringProp>
            <stringProp name="Argument.value">https</stringProp>
          </elementProp>
        </collectionProp>
      </Arguments>
      <hashTree/>
      
      <!-- 线程组 -->
      <ThreadGroup guiclass="ThreadGroupGui" testclass="ThreadGroup" testname="用户组">
        <stringProp name="ThreadGroup.num_threads">100</stringProp>
        <stringProp name="ThreadGroup.ramp_time">10</stringProp>
        <boolProp name="ThreadGroup.scheduler">true</boolProp>
        <stringProp name="ThreadGroup.duration">300</stringProp>
        <stringProp name="ThreadGroup.delay">5</stringProp>
      </ThreadGroup>
      <hashTree>
        <!-- CSV 数据 -->
        <CSVDataSet guiclass="TestBeanGUI" testclass="CSVDataSet" testname="测试数据">
          <stringProp name="delimiter">,</stringProp>
          <stringProp name="filename">data/test_data.csv</stringProp>
          <stringProp name="variableNames">param1,param2</stringProp>
          <boolProp name="recycle">true</boolProp>
        </CSVDataSet>
        <hashTree/>
        
        <!-- HTTP 请求默认值 -->
        <ConfigTestElement guiclass="HttpDefaultsGui" testclass="ConfigTestElement" testname="HTTP默认值">
          <stringProp name="HTTPSampler.domain">${BASE_URL}</stringProp>
          <stringProp name="HTTPSampler.port">${BASE_PORT}</stringProp>
          <stringProp name="HTTPSampler.protocol">${BASE_PROTOCOL}</stringProp>
          <stringProp name="HTTPSampler.contentEncoding">UTF-8</stringProp>
        </ConfigTestElement>
        <hashTree/>
        
        <!-- HTTP Header Manager -->
        <HeaderManager guiclass="HeaderPanel" testclass="HeaderManager" testname="HTTP Headers">
          <collectionProp name="HeaderManager.headers">
            <elementProp name="" elementType="Header">
              <stringProp name="Header.name">Content-Type</stringProp>
              <stringProp name="Header.value">application/json</stringProp>
            </elementProp>
          </collectionProp>
        </HeaderManager>
        <hashTree/>
        
        <!-- 定时器 -->
        <GaussianRandomTimer guiclass="GaussianRandomTimerGui" testclass="GaussianRandomTimer" testname="随机定时器">
          <stringProp name="ConstantTimer.delay">1000</stringProp>
          <stringProp name="RandomTimer.range">2000</stringProp>
        </GaussianRandomTimer>
        <hashTree/>
        
        <!-- HTTP 请求 -->
        <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="示例请求">
          <stringProp name="HTTPSampler.path">/api/v1/resource</stringProp>
          <stringProp name="HTTPSampler.method">GET</stringProp>
        </HTTPSamplerProxy>
        <hashTree>
          <!-- 断言 -->
          <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="验证响应">
            <collectionProp name="Asserion.test_strings">
              <stringProp name="49586">"success"</stringProp>
            </collectionProp>
            <stringProp name="Assertion.test_field">Assertion.response_data</stringProp>
            <intProp name="Assertion.test_type">16</intProp>
          </ResponseAssertion>
          <hashTree/>
        </hashTree>
        
        <!-- 聚合报告 -->
        <ResultCollector guiclass="StatVisualizer" testclass="ResultCollector" testname="聚合报告">
          <boolProp name="ResultCollector.error_logging">false</boolProp>
          <objProp>
            <name>saveConfig</name>
            <value class="SampleSaveConfiguration">
              <time>true</time>
              <latency>true</latency>
              <timestamp>true</timestamp>
              <success>true</success>
              <label>true</label>
              <code>true</code>
              <message>true</message>
              <threadName>true</threadName>
              <dataType>true</dataType>
              <encoding>false</encoding>
              <assertions>true</assertions>
              <subresults>true</subresults>
              <responseData>false</responseData>
              <samplerData>false</samplerData>
              <xml>false</xml>
              <fieldNames>true</fieldNames>
              <responseHeaders>false</responseHeaders>
              <requestHeaders>false</requestHeaders>
            </value>
          </objProp>
          <stringProp name="filename"></stringProp>
        </ResultCollector>
        <hashTree/>
      </hashTree>
    </hashTree>
  </hashTree>
</jmeterTestPlan>

附录 B:Groovy 脚本常用代码片段

B.1 生成随机数据

groovy 复制代码
// 随机手机号
def prefix = ['130','131','132','133','134','135','136','137','138','139']
def phone = prefix[new Random().nextInt(prefix.size())] + (100000000 + new Random().nextInt(900000000))
vars.put("phone", phone.toString())

// 随机邮箱
def domains = ['qq.com', '163.com', 'gmail.com', 'outlook.com']
def email = "test${System.currentTimeMillis()}@${domains[new Random().nextInt(domains.size())]}"
vars.put("email", email)

// 随机身份证号(简化版)
def areaCode = '110101'
def year = 1980 + new Random().nextInt(20)
def month = String.format('%02d', 1 + new Random().nextInt(12))
def day = String.format('%02d', 1 + new Random().nextInt(28))
def seq = String.format('%03d', new Random().nextInt(999))
def idCard = "${areaCode}${year}${month}${day}${seq}X"
vars.put("idCard", idCard)

// UUID
vars.put("requestId", UUID.randomUUID().toString())

// 时间戳
vars.put("timestamp", System.currentTimeMillis().toString())

B.2 数据处理

groovy 复制代码
// JSON 解析
import groovy.json.JsonSlurper

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

// 提取字段
vars.put("userId", json.data.user.id.toString())
vars.put("userName", json.data.user.name)

// 列表遍历
json.data.items.each { item ->
    log.info("商品ID: ${item.id}, 名称: ${item.name}")
}

// 条件判断
if (json.code == 200) {
    log.info("请求成功")
    vars.put("status", "success")
} else {
    log.warn("请求失败: ${json.message}")
    vars.put("status", "failed")
}

B.3 文件操作

groovy 复制代码
// 读取文件
def file = new File('/path/to/data.json')
def content = file.text

// 写入文件
new File('/path/to/output.txt').append("${vars.get("token")}\n")

// 读取CSV
def csv = new File('/path/to/data.csv')
def lines = csv.readLines()
def randomLine = lines[new Random().nextInt(lines.size())]
def fields = randomLine.split(',')
vars.put("field1", fields[0])
vars.put("field2", fields[1])

恭喜你读完了整篇教程! 🎉

现在你已经掌握了 JMeter 从入门到精通的全部知识。记住,性能测试不仅仅是"跑个工具看数据",更重要的是理解业务场景、设计合理的测试方案、准确分析结果并给出优化建议

正如米其林餐厅不是靠一个好厨师就能成功的,优秀的性能测试也需要工具技能 + 业务理解 + 分析能力的综合素质。继续练习,你会越来越强!

Happy Testing! 🚀