4. 测试脚本开发:JMeter脚本编写、参数化配置、断言设置、监听器使用
脚本开发是吞吐测试的“手艺活”。工具摆在那,但能不能压出真实数据,全看脚本写得细不细。我见过太多人把JMeter当“点一下就跑”的工具,结果压出来的曲线跟业务实际差了十万八千里。
这一章,咱们把脚本开发的四个核心环节拆开揉碎。你跟着走一遍,基本就能应付90%的吞吐测试场景了。
4.1 脚本编写:从录制到手工构建
JMeter脚本怎么来?无非两条路:录,或者写。
录制适合快速上手。用Badboy或者JMeter自带的HTTP(S) Test Script Recorder,浏览器上点一圈,脚本就出来了。但我个人习惯是:录完一定得手工清理。录制脚本里全是静态资源的请求——图片、CSS、JS,这些在吞吐测试里通常要过滤掉。你想想看,压测目标是测接口吞吐,不是测CDN。
手工构建才是正经做法。我建议你直接建一个空的测试计划,然后按业务逻辑一步步加Sampler。核心步骤就这几步:
- 右键测试计划 → Add → Threads (Users) → Thread Group
- 右键Thread Group → Add → Config Element → HTTP Request Defaults(填协议、域名、端口)
- 右键Thread Group → Add → Sampler → HTTP Request(填路径、方法、参数)
- 右键Thread Group → Add → Listener → View Results Tree(调试用)
举个例子,一个简单的登录接口脚本:
Thread Group
├── HTTP Request Defaults
│ ├── Protocol: https
│ ├── Server Name: api.example.com
│ └── Port: 443
├── HTTP Request
│ ├── Method: POST
│ ├── Path: /v1/login
│ ├── Parameters:
│ │ ├── username: test_user
│ │ └── password: 123456
└── View Results Tree
嗯,这里要注意:别把密码硬编码在脚本里。后面我们会用参数化来解决。
4.2 参数化配置:让脚本“活”起来
吞吐测试最忌讳什么?所有用户用同一套数据。数据库里唯一索引一冲突,压测全废。参数化就是让每个虚拟用户用不同的数据去请求。
JMeter里参数化有四种常用方式,我按推荐度排个序:
| 方式 | 适用场景 | 数据量 | 我踩过的坑 |
|---|---|---|---|
| CSV Data Set Config | 大量用户、不同账号 | 百万级 | 文件路径写相对路径,换机器就找不到 |
| User Defined Variables | 少量固定参数(如域名) | 几个 | 线程间共享,别用来做用户隔离 |
| 函数助手(__Random等) | 随机数值、时间戳 | 无限 | 随机范围太大,数据分布不均匀 |
| JDBC PreProcessor | 从数据库实时取数据 | 动态 | 连接池耗尽,压测变数据库压测 |
CSV Data Set Config 是我最常用的。配置起来很简单:
Filename: /data/users.csv
Variable Names: username,password,token
Delimiter: ,
Recycle on EOF: False
Stop thread on EOF: True
然后你在HTTP Request的参数里写 ${username}、${password} 就行了。每个线程取一行,用完即止。
4.3 断言设置:别让错误数据污染结果
断言是什么?说白了就是“检查服务器返回的东西对不对”。没有断言,你看到的TPS可能是99%的报错请求堆出来的。我见过一个项目,TPS跑了5000,结果断言一加,实际成功的只有200。
JMeter里常用的断言有这几种:
- Response Assertion:检查响应文本是否包含某个字符串。最常用,比如检查返回里有没有
"code":0。 - Duration Assertion:检查响应时间是否超过阈值。超过就算失败,适合做超时监控。
- Size Assertion:检查响应体大小。有时候服务器返回空数据也算成功,但业务上其实是失败的。
- JSON Assertion:专门解析JSON响应,检查某个字段的值。比Response Assertion更精确。
配置示例(Response Assertion):
Apply to: Main sample only
Test Field: Response Text
Pattern Matching Rules: Contains
Patterns to Test:
"status": "success"
我个人习惯是:每个Sampler至少加一个断言。哪怕只是检查HTTP 200,也比不检查强。你想想看,如果服务器挂了返回502,你的TPS曲线还是一条直线,那数据还有什么意义?
4.4 监听器使用:看懂数据才是关键
监听器是JMeter的“仪表盘”。但很多人一上来就加十几个监听器,结果跑完一看,该看的数据一个没看对。
我常用的监听器就三个,足够了:
| 监听器 | 用途 | 什么时候用 |
|---|---|---|
| View Results Tree | 调试脚本,看单个请求的请求/响应详情 | 脚本开发阶段 |
| Summary Report | 查看吞吐量、平均响应时间、错误率 | 压测执行阶段 |
| Aggregate Report | 按标签分组统计,看每个接口的详细指标 | 结果分析阶段 |
View Results Tree 只在调试时打开。正式压测一定要关掉,否则它会吃掉大量内存,影响压测结果。我记得有一次压测,开着View Results Tree跑了10分钟,结果JMeter直接OOM了。
Summary Report 是压测时的主力。它实时显示:
- #Samples:总请求数
- Average:平均响应时间(ms)
- Min/Max:最小/最大响应时间
- Std. Dev.:标准差(响应时间波动越大,值越大)
- Error %:错误率
- Throughput:吞吐量(req/s)
- Received KB/sec:接收速率
- Sent KB/sec:发送速率
嗯,这里要注意:Throughput才是你真正要盯的指标。Average响应时间只是参考,别被它牵着走。
4.5 脚本开发全流程:一张图看懂
说了这么多,咱们用一张SVG图把整个流程串起来。从创建测试计划到最终输出报告,每一步都离不开前面讲的内容。
这张图把脚本开发的七个步骤串起来了。你每次写脚本的时候,按这个流程走一遍,基本不会漏东西。我个人习惯是:每写完一个Sampler,立刻加断言和参数化,最后再加监听器。这样调试的时候就能看到真实数据,不用回头补。
4.6 实战小贴士:脚本调试三板斧
脚本写完了,怎么知道对不对?我一般用这三招:
- 单线程跑一次:线程数设1,循环次数设1,打开View Results Tree看请求和响应。这一步能发现90%的配置错误。
- 加Debug Sampler:在参数化之后加一个Debug Sampler,可以打印出所有变量的值。看看${username}是不是真的取到了数据。
- 检查断言日志:如果断言失败,JMeter会在日志里写清楚失败原因。别只看“失败”两个字,点开看看具体是哪个字段没匹配上。
脚本开发这块,说白了就是“细心活”。参数化、断言、监听器,每一个环节都直接影响最终数据的可信度。你按这个流程走,脚本质量不会差。