很多人一开始接触性能测试,第一反应就是“上工具、造数据、跑起来看数字”,但真到了压测现场,往往会发现工具越熟练,问题越说不清。JMeter确实是最常用的压测工具之一,但真正用好它,不只是会拖几个组件、填几个参数,而是要知道你在测什么、为什么这么测、结果出来之后又该怎么读。这篇文章我会从实际项目的角度,把JMeter从头到尾拆一遍,包括环境搭建、脚本设计、参数化、结果分析、常见坑点,以及一些只有踩过坑才会总结出来的经验,尽量让你看完之后能直接上手,而不是停留在“会点鼠标”的阶段。
1. 性能测试不是堆机器,而是先想清楚测什么
1.1 我见过的“伪性能测试”
先聊聊我遇到过的一种很典型的场景。某项目上线前,负责人很着急地拉了个测试同学,说“赶紧用JMeter压一下接口,看看能扛多少并发”。于是测试同学开了一台4核8G的机器,用JMeter起了500个线程去压一个登录接口,跑了两分钟,盯着聚合报告里的吞吐量,说性能不错,几千的TPS。但仔细一问,这个接口背后要查数据库、还要调外部鉴权服务,压测机上同时有负载生成、结果采样、报告输出,线程一多,先扛不住的反而是那台测试机自己。最后得出的数据,既不能说明系统能抗多少并发,也解释不了瓶颈到底在哪。
这就是典型的“伪性能测试”——只关注工具操作,忽略了测试设计。JMeter只是工具,真正决定测试质量的是你在压测之前对系统、对场景、对目标的理解。性能测试第一步,不是开JMeter,而是明确一个问题:这次压测要回答什么?是验证系统能不能达到某个指标,还是找出系统的最大承载能力,还是排查某个具体接口的耗时瓶颈?目标不同,脚本设计、压力模型、结果分析方式都会完全不同。
1.2 性能测试的三种基本类型
从目标来分,性能测试大概有三类,理解了它们,你就知道自己该做什么了。
第一类是负载测试,也是最常见的。它的核心是验证在预期的并发用户数、请求频率下,系统指标是否达标。比如业务目标说登录接口需要支持500并发,平均响应时间小于500毫秒,那你就压到500并发,观察各项指标是否符合要求。这类测试不追求压垮系统,重点是“过不过得了关”。
第二类是压力测试,也叫容量测试。它更关注系统到底能扛到多少,通常会逐步增加压力,直到系统出现性能拐点或者资源耗尽。这类测试用来找上限、找瓶颈,很多时候会暴露数据库连接池不够、线程池排队严重这类问题。压力测试的结果直接决定后续容量规划和扩容策略。
第三类是稳定性测试,也叫疲劳测试。它是在中等负载下长时间运行(比如4小时、8小时甚至24小时),验证系统有没有内存泄漏、连接泄漏、缓存失效加速这些问题。很多系统短期跑得很漂亮,一跑到后半夜就出现响应时间慢慢变长、报错率上升,都是稳定性测试没做到位。
理解了这三种场景,你再看JMeter里的线程组设计,思路就会清楚很多:负载测试可以固定线程数跑5分钟,压力测试要设置阶梯增长,稳定性测试则需要长时间稳定负载。工具操作都一样,区别在于你怎么设计。
1.3 JMeter为什么能成为首选工具
市面上压测工具其实不少,商业的有LoadRunner,开源的除了JMeter还有Locust、Gatling等。但JMeter在很长时间里都是国内性能测试工具的事实标准,原因也很实际:
- 开源免费,插件丰富:不限制并发数,不限制时间,官方和各路大神贡献的插件,基本覆盖了主流协议和扩展场景。
- 协议支持广:HTTP、HTTPS、JDBC、JMS、FTP、TCP,甚至通过插件支持WebSocket、Kafka、gRPC。日常Web项目基本只需要HTTP接口,原生的就够了。
- 上手曲线相对友好:虽然它是个“重量级”工具,启动慢、内存吃得多,但只要理解了几个核心组件,写脚本就像拼积木。
- 分布式能力成熟:单机能力有限的时候,JMeter的Master/Slave模式可以扩展压测机,虽然配置有点繁琐,但胜在稳定。
当然,它也有明显的缺点,比如对资源消耗比较大、脚本是GUI编辑的XML结构、混合复杂场景时脚本结构容易混乱。但这些问题都可以通过合理的脚本组织、命令行非GUI模式、以及代码化脚本管理来规避,并不影响它成为最稳妥的选择。
2. JMeter环境准备与核心概念
2.1 安装与版本选型
安装JMeter其实没什么难度,但有几点容易被忽略。首先说版本,我个人建议优先选最新的稳定版,不要用太老的版本。因为老版本在Java版本兼容、协议支持上有不少历史包袱。JMeter是纯Java应用,所以第一步是安装JDK。JMeter 5.6.3及以上版本推荐JDK 11或更高,JMeter 5.4以前也可以用JDK8。实际项目中我遇到过用JDK8跑新版JMeter启动报错的情况,所以最简单的方式是:装JDK 17 LTS,然后去官网下载最新zip包,解压就能用。
注意:JMeter解压后,不要直接双击JMeter.bat启动GUI去干活。GUI模式是用来写脚本和调试脚本的,跑压测一定要用命令行非GUI模式。原因后面细讲。
另外一个很容易踩的坑是:JMeter是32位还是64位的问题。现代环境基本都64位,但如果遇到以下情况——压测时线程数很高,或者脚本里用了大量正则表达式,JMeter可能报OutOfMemoryError。这时候需要修改jmeter.bat(Windows)或jmeter.sh(Linux)里的JVM参数,比如:
set HEAP=-Xms2g -Xmx4g -XX:MaxMetaspaceSize=512m推荐最小堆和最大堆设置一样,避免JVM动态扩容带来的性能抖动。压测机如果内存不足,设置再大也没用,所以压测机的配置尽量和被测服务的规格匹配或者更高。
2.2 三个不得不懂的底层机制:线程组、采样器、监听器
第一次打开JMeter,很多人会被左边那一堆树形节点搞懵。但你抓住三个核心,整棵树的逻辑就清晰了。
线程组(Thread Group)是压测的“发令枪”,它定义了模拟多少用户、以什么速度启动、持续多久。线程数、Ramp-Up Period(启动时间)、循环次数这三个参数决定了压力模型。比如你要模拟100个并发用户在10秒内全部启动,就设置线程数100、Ramp-Up 10秒。如果模拟长时间稳定负载,就要设置调度器,比如持续时间600秒,而不是循环次数。
采样器(Sampler)是真正干活的地方,它定义“发什么请求”。最常见的是HTTP请求采样器,包含协议、服务器地址、端口、路径、请求方法、参数、请求体。采样器是JMeter里的最小执行单元,每个采样器的结果会被后续的断言、监听器处理。
监听器(Listener)是“仪表盘”,它负责收集和展示结果。比如查看结果树、聚合报告、用表格查看结果、图形结果。注意一条铁律:生产环境压测时不要用监听器。因为监听器记录每个请求的细节会消耗大量CPU和内存,严重影响压测机本身的发压能力。真正的压测运行,一定要通过-n命令行模式跑,并且用-l指定输出日志文件,跑完后用JTL日志去生成报告。
三者的关系可以这么理解:线程组决定“多少人”,采样器决定“干什么活”,监听器决定“怎么记账”。很多人解读线程的数就是并发数,这个说法不完全对。并发数准确说是同一时刻在途请求数,JMeter的线程数只是模拟用户数,如果每个用户循环跑,实际并发在途请求可以远高于线程数(因为一个线程里可以有多个串行采样器,但同一线程内的采样器是串行执行的,所以实际并发上限是线程数乘以每个采样器内部的并发连接数,但普通HTTP请求同步模式下一个线程同一时刻只有一个请求,所以基本等价于线程数)。所以设计脚本时,一个线程组里如果放了多个HTTP采样器,它们是串行执行的,线程数并不等于并发请求数。这种情况可以用同步定时器(Synchronizing Timer)来强制对齐,不过实际业务场景很少需要,默认按线程数理解并发用户数即可。
2.3 用生活类比理解JMeter的组件结构
如果觉得上面的概念太抽象,我用餐厅来做类比。线程组是餐厅的客流控制器,规定了大厅里最多坐多少人、每批客人隔多久进场。HTTP请求采样器是厨师手里的菜单,一份菜单决定做一道什么菜(请求什么接口、传什么参数)。断言是品控员,菜出了厨房都要检查一下,颜色对不对、味道对不对,不对就标记为“失败”。监听器是店里的收银和报表系统,记录每天卖了多少桌、平均每桌吃了多久。而定时器是服务员控制上菜节奏的,防止一下子全部涌进后厨。
这个类比能帮新手快速建立框架感,但真正做项目的时候,你会发现组件之间的关系其实更像管道:线程组不断产生“用户请求流”,经过各种前置处理器、定时器、采样器、断言、后置处理器,最后流到监听器里变成数据。你掌握得越熟练,越能在管道中间灵活插入需要的组件,而不是死记硬背每个组件的位置。
3. 脚本设计:从录制到手写
3.1 录制脚本的便利与陷阱
JMeter自带的录制能力是通过HTTP代理服务器实现的,配置好代理、打开浏览器操作页面,请求就能被录制成脚本。对新手来说,录脚本确实很直观,但我实际项目里几乎不用它,原因有三个:
第一,录制脚本会包含大量静态资源请求,比如图片、CSS、JS,这些请求对后端接口性能测试来说基本是噪音。当然可以过滤掉,但过滤规则本身就是额外成本。第二,录制结果的可读性很差,请求参数、路径都带一些额外信息,变量没有参数化,维护成本高。第三,JMeter代理录制对HTTPS证书的处理比较麻烦,需要导入证书,而且遇到比较复杂的登录鉴权场景,录出来的脚本经常要手动改。
所以我的建议是:用录制功能快速了解业务流程,然后立刻转为手写脚本。手写HTTP请求采样器在JMeter里非常灵活,每个字符都是自己控制的,可维护性好很多。真正的性能测试脚本应该是逻辑清晰的“代码”,而不是浏览器操作的“磁带记录”。
3.2 手写HTTP请求脚本的推荐姿势
手写HTTP请求,核心是构造请求到“请求”、“路径”、“参数”和“请求体”。这里有个细节:JMeter的HTTP请求里面,Parameters页和Body Data页看起来都能传参,但要分清用途。对于Get请求,参数放在Parameters页,JMeter会自动拼接到URL后面;对于Post请求,如果Content-Type是application/x-www-form-urlencoded,参数也可以放在Parameters页;但如果是application/json或者text/xml,就必须放在Body Data页,而且Params页不能再有内容,否则会把请求体搞混。
实际接口测试中,JSON格式最常用。举个例子,一个创建订单的接口,请求体长这样:
{ "orderId": "${orderId}", "userId": "${userId}", "amount": 100.0 }这里的${orderId}就是JMeter的变量引用语法,通过参数化方式在运行时动态赋值。构造请求时还要注意Headers,比如Content-Type: application/json、Authorization: Bearer ${token},这些Header可以在HTTP请求节点下的“HTTP Header Manager”里统一管理。
实操心得:HTTP Header Manager的作用域是它所在作用域的所有子级采样器。如果你在测试计划根节点添加一个Header Manager,那么所有线程组的请求都会带上这个头。如果你希望不同请求带不同Header,就把Header Manager放到具体采样器的子级。这种作用域规则在JMeter里叫“作用域覆盖”,是很多脚本问题的根源,值得单独花时间理解。
3.3 参数化的三种方式与实战选择
参数化是性能测试脚本绕不开的一环。真实场景里,不可能每个用户都用同一个账号登录,也不可能每个订单都用同一个ID,那样会引入严重的缓存命中,导致压测结果失真。JMeter里常用的参数化方式有三种。
第一种是使用CSV数据文件。把测试数据放到一个文本文件里,每一行是一条记录,通过CSV Data Set Config组件读取。这是我最推荐的方式,因为数据可控、可重复、适合大数据量。重点要配置好两个字段:共享方式(All threads,或者Current thread group)和EOF时的设置。如果数据不够用,可以选“循环”,或者让线程在EOF时停止。真实项目中,如果压测500并发,最好准备不少于500条数据,避免大量线程读写同一行导致数据争用。
第二种是使用函数。JMeter自带一些函数,比如${__Random(1,9999)}生成随机数,${__time(YYYY-MM-dd)}生成日期。适合生成少量、临时、不需要校验的数据。但要注意,纯随机可能导致冲突,如果接口有唯一性校验,随机数不保证唯一。
第三种是用户自定义变量(User Defined Variables)。适合全局固定值,比如服务器地址、端口、固定测试环境的账号前缀。它不属于数据驱动,但可以让脚本结构更清晰。
实际项目中最常用的组合是:CSV提供业务数据,函数提供随机参数,用户自定义变量提供环境配置。区分清楚这三者的使用场景,脚本的数据部分就基本不会出大问题。
3.4 断言:怎么判断请求真的成功了
很多新手做了压测脚本就急着跑,完全不设断言,结果跑到后面发现一堆200状态码,但系统其实一直返回的是在线文档提示或者统一异常报文,白白浪费了一整个压测周期。所以要学会用断言。
JMete里我自己最常用的是响应断言(Response Assertion)。它可以匹配响应文本、响应代码、响应消息等。比如登录接口成功后返回的JSON里有个"status":0字段,我在断言的“测试模式”里选择“匹配字符串”,填"status":0,然后要勾选“跳过多余的可变部分”,防止响应里不同请求之间只有该字段稳定,其余变化导致断言失败。
注意断言必须加在需要校验的HTTP请求节点下面,这样只对当前请求生效。如果放到线程组下,就会对该线程组所有请求生效,容易误判。
重要提示:断言不要滥用。每个请求都加复杂的JSON断言会拖慢压测性能,因为JMeter的断言默认是在请求线程里同步执行的。我的习惯是:只对关键业务请求加断言,而且断言的阈值严格控制,不检查完整JSON,只看状态字段或业务码。
4. 实战:一个接口压测的完整过程
4.1 场景定义:并发模型与压力梯度
这一节用模拟项目X中的一个实际场景来演示。需求是这样的:某公司的一个订单查询接口,预期峰值并发用户数达到300,目标平均响应时间小于800毫秒,错误率小于0.1%。我需要在压测前先定义场景,而不是直接填300了事。
场景定义分三步。第一步,确定“并发模型”。订单查询是读接口,通常是无状态只读操作,那么并发模型就用普通线程组,线性增加用户数。如果是有状态的混合场景(比如登录-查单-支付-退出),就要使用“通过控制器+随机控制器”组织业务比例,但这就复杂得多,这里先不展开。
第二步,确定“压力梯度”。不要一开始就直接300并发怼上去,那样一旦挂了,你都不知道是300才挂还是100就挂了。我通常的做法是:20并发跑3分钟热身,然后50并发跑3分钟,最后逐步升到300并发跑10分钟。这种阶梯式的压力爬升,可以直接观察TPS和响应时间的拐点,定位系统在哪个并发数附近开始不稳定。使用JMeter的“Stepping Thread Group”插件(需要额外安装扩展)或者用多个线程组分别跑就够了。
第三步,确定“持续时间”。负载测试中持续时间一般至少5-10分钟,太短无法覆盖慢SQL、缓存淘汰等延迟问题。对于稳定性测试,设置为1-2小时起,具体看业务需求。
4.2 构建线程组与设置持续时长
以压力测试为例,我在JMeter里这样配置线程组:
- 线程数:通过变量
${threads}控制,比如先设20。 - Ramp-Up Period:设置为
${threads},也就是20秒启动全部20个线程,相当于每秒增加1个用户。 - 循环次数:勾选“永远”,然后用调度器设置持续时间600秒。
注意:勾选“永远”时,循环次数会变成默认的空状态,但如果你勾选了“调度器”,就必须填写持续时间。持续时间的单位是秒,比如10分钟填600。
这样设计的好处是:Ramp-Up时间等于线程数,意味着每秒递增一个用户,压力是平滑爬坡的,避免瞬时冲击。如果你发现系统在启动阶段就开始报错,那说明系统连平滑上升都扛不住,这是很重要的预警信号。
执行的时候,不需要打开GUI,直接命令行:
jmeter -n -t order_query.jmx -l result.jtl -e -o report_dir-n代表非GUI模式,-t指定测试计划脚本,-l输出结果日志文件(JTL格式),-e -o自动生成HTML报告到指定目录。这个命令是JMeter压测的标准姿势,务必背下来。
4.3 关联与Token处理
很多系统接口有登录鉴权,最典型的表现是:请求Header里带着一个Token,而Token是通过登录接口获取的,并且会在一定时间后过期。手工测试时你可以复制Token,但压测时每个线程都必须有独立的Token,而且不能手动写死,这就需要做“关联”——从上一个请求的响应中提取动态值,传给下一个请求。
最常见的关联手段是用JSON提取器(JSON Extractor)或正则表达式提取器(Regular Expression Extractor)。比如登录接口返回:
{ "code": 0, "data": { "token": "a1b2c3d4e5f6" } }在登录HTTP请求下添加“JSON提取器”,配置变量名token,JSON Path表达式为$.data.token,Match No填写1,这样后续请求就能用${token}引用。如果响应不是JSON而是HTML,那就用正则提取器,比如提取name="csrf_token" value="([^"]*)",注意正则的贪婪匹配,尽量用([^"]*)限定边界。
关联是脚本稳定性的核心,因为它确保了每个线程有自己的会话。但也要注意Token时效性问题,如果压测时长超过Token有效期,后续请求就会401。解决方式有两种:一是让脚本循环登录,二是单独做一个“登录-拿Token-执行业务请求”的组合逻辑。一般建议用第二种,在同一个线程组里放登录请求和业务请求,逻辑上最清晰。
4.4 监听器与结果分析指标
跑完压测之后,数据怎么看?结果文件result.jtl是原始记录,每一行是一次请求的响应时间、状态码、字节数。通过JMeter自带的聚合报告或HTML报告,可以快速得到几个关键指标:
- TPS(Throughput):每秒事务/请求数,单位是
/sec。这个指标反映系统的处理能力。 - Avg响应时间:所有事务的平均时间,单位毫秒。
- 90th/95th/99th响应时间:分别表示90%、95%、99%的请求在多少毫秒内完成。平均响应时间容易掩盖长尾问题,比如平均300ms,但99%是5秒,说明系统有严重的抖动,大量请求超时率不高但体验极差。
- Error %:错误请求占比,超过业务容忍阈值就要终止压测排查原因。
- Received KB/sec / Sent KB/sec:网络吞吐量,有时候会发现瓶颈在带宽。
推荐重点看聚合报告(Aggregate Report),因为它同一行包含了Avg、Min、Max、Std.Dev、Error%、Throughput等所有关键信息。但聚合报告默认给出的是累计值,如果你想看过程中的趋势,还是依赖HTML报告里的图表更直观。JTL文件配合-e -o生成的HTML报告内置了响应时间百分比分布、TPS趋势、活跃线程数等图表,比手动看聚合报告强很多。
4.5 从TPS和响应时间曲线看系统瓶颈
拿到数据后,分析才是最有价值的部分。两种经典曲线能帮你判断瓶颈:
第一个是“TPS随并发上升但最终趋于平台”。这通常说明系统在某处已经达到处理上限,可能是应用服务线程池满了、数据库连接池耗尽,或者某个外部依赖成为瓶颈。这个平台期对应的并发数,就是系统当前架构下的最大能力。
第二个是“响应时间随并发上升出现明显拐点”。如果TPS还在涨,但响应时间已经急剧上升,说明系统开始排队,请求在等待某个资源。这时候可以去查被测系统的GC日志、线程Dump、慢SQL日志,找到排队源头。
我用过一个很笨但很有效的方法:压测的同时,在另一台机器上启动jvisualvm或者jstack,在并发上升时多次采集线程状态。如果发现大量java.lang.Thread.State: RUNNABLE的线程都卡在一个方法上,那基本就是代码热点;如果大量线程处于WAITING (parking)那大概率是线程池或锁竞争。JMeter只负责把压力打出来,真正的根因分析往往要靠系统侧的监控工具配合。
5. 常见问题与排查技巧实录
5.1 压测结果异常低:先查客户端资源
有次我压测一个很简单的接口,JMeter并发数200,但TPS只有几十,怎么看怎么不对劲。被测服务端CPU才百分之十几,数据库也没有慢SQL。最后一看压测机,CPU已经飙到接近满负荷。原因是我开了JMeter GUI,还挂了查看结果树监听器,每进来一个请求,GUI界面要刷新一次树形节点,消耗大量资源。
这类问题太常见了。压测结果异常低的时候,第一件事不是怀疑服务,而是检查压测客户端。正确做法是:
- 用命令行模式
jmeter -n跑脚本,不要开GUI。 - 不要添加“查看结果树”和“图形结果”这两个监听器在压测中展示,必须去掉或注释掉。
- 压测机资源监控要看CPU、内存、网络、文件描述符。如果CPU是瓶颈,降低并发数或者增加分布式压测节点。
- JVM堆设置不要过大,否则GC会带来周期性的停顿,干扰真实情况。一般压测机堆内存设置2-4G足够。
5.2 响应时间飙升但服务器CPU很低
这个现象很耐人寻味。服务端CPU很低意味着业务代码本身消耗不大,但响应时间却涨起来了,那八成是等待类瓶颈。常见的几个方向:
- 连接池耗尽,比如数据库连接池最大连接数100,并发一大,请求都阻塞在等待连接上。查Druid或HikariCP的监控。
- 线程池满了,比如Tomcat最大线程200,任务全排进队列,响应时间自然涨。看Tomcat线程池活跃数是否达到maxThreads。
- 锁竞争,比如并发访问同一个内存对象,遇到大量的
synchronized或者ReentrantLock。用jstack抓线程状态就能看到。 - 外部依赖变慢,比如调用第三方接口、下游服务响应变慢,客户端等待超时。需要通过链路追踪(比如在代码里做简单的计时输出)确认耗时分布。
记住一个排查口诀:先看客户端,再看服务端;先看资源,再看锁,最后看外部依赖。
5.3 内存溢出与报告乱码
JMeter压测时间长了,可能报java.lang.OutOfMemoryError: GC overhead limit exceeded。这通常是因为JTL日志文件太大,并且脚本里开启了太多监听器或保存了过多字段。解决方式除了调高JVM堆,还可以通过减少保存的数据量来控制日志体积。命令行加参数:
-j -l result.jtl -Jjmeter.save.saveservice.output_format=csv并且配置jmeter.properties里的saveservice相关选项,只保留需要的关键字段,比如时间戳、线程名、响应时间、成功状态,不要保存请求体、响应体。
至于报告乱码,大部分是编码问题。JTL文件和被测系统的响应可能是UTF-8,但如果脚本里用了本地编码过的参数,可能导致读取乱码。统一使用UTF-8编写CSV数据,在JMeter中通过-Dfile.encoding=UTF-8参数运行,或者修改bin目录下的jmeter.properties里的sampleresult.default.encoding=UTF-8,基本能解决。
5.4 分布式压测的正确打开方式
单机JMeter能模拟的并发有限,尤其是涉及大量HTTP连接和长连接时。JMeter官方推荐用Master-Slave分布式模式,原理是:Master负责调度和收集结果,Slave负责实际发压。
配置要点如下:
- 所有压测机安装相同版本的JMeter,并确保JDK版本一致。
- 在所有Slave机器上启动
jmeter-server.bat或jmeter-server.sh。 - 在Master机器的
jmeter.properties中配置remote_hosts=slave1_ip:1099,slave2_ip:1099。 - 运行时通过
-R slave1_ip,slave2_ip指定远程节点,不加-r直接跑。
分布式压测有个常见的坑:Master节点会汇总所有Slave的结果并在最终报告里显示总和,如果你在Master上也在发压,会重复计算。一般建议Master只做调度,不参与发压,所以不在Master机器上运行测试线程组。另外,所有Slave机器必须保证本地时区一致,否则时间戳会错位,导致响应时间的聚合结果混乱。
我还要强调一个观念:不要一上来就搞分布式。先优化脚本,确认单机跑300并发没问题之后,再评估是否需要分布式。很多时候用单机配合高配置机器就能满足需求,分布式带来的网络同步、结果归并、运维成本其实很高。
6. 关于JMeter的几点个人体会
最后分享几个实际工作中的习惯,不一定写在职级评审材料里,但确实让我少踩了很多坑。
第一,养成脚本版本管理的习惯。JMeter脚本虽然是XML,但把.jmx文件放到版本库里完全没问题,配合Jenkins之类的工具做接口回归性能测试时,能追踪每次变更对性能的影响。我自己的做法是:JMeter脚本里所有环境信息(域名、端口、账号密码)都用变量命名,不写死,这样不同环境可以复用同一份脚本。
第二,性能测试不是跑完就完,要沉淀基线数据。每次压测都记录下环境信息、被测代码版本、并发数、TPS、响应时间、错误率、服务端资源,放到同一个文档里。这样下次改动代码后再跑一次,就能快速看出性能是上升还是下降。没有基线的性能数据,参考价值会大打折扣。
第三,定期检查你自己的脚本是否真实有效。我会在压测前先跑一次单线程,手动验证脚本逻辑正确:断言是否通过、参数是否被正确替换、关联变量是否成功提取。如果单线程都跑不通,压测跑起来只会浪费一大堆机器资源,而且结果完全不可信。
第四,学会用关键指标做判断,而不是只看一个数字。平均响应时间好但99%响应时间很差,这个系统对一部分用户来说就是不可用的。TPS高也不代表一切,如果错误率也同步升高,那这个TPS再高也没有意义。性能测试的核心从来不是造一个漂亮的报告,而是让系统在上线前暴露那些只有高并发时才会出现的问题。JMeter只是帮你把这个过程重复一千遍一万遍的工具,真正值钱的是你从这些数据里读出来的结论,以及后续的调优动作。
这个东西我用了很多年,每次都会发现新版本更好使了,但底层的逻辑没有变过:把场景想清楚,把脚本写干净,把数据读准确。希望你也能在实际项目中用起来,跑出有价值的性能结论。