ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

JMeter性能测试全链路:造数据、监控调优与报告落地

JMeter性能测试全链路:造数据、监控调优与报告落地

简介:这份《性能测试-Jmeter.pptx》面向性能测试入门到进阶的测试工程师及培训学员,围绕JMeter实战梳理从数据准备到测试报告输出的完整流程。课件结合云影像等业务场景,讲解初始化、铺底与参数化数据的制造方法,比较JMeter参数化脚本和存储过程、Java直插的优缺点,并以百万级登记数据为例演示抓包、参数化、调试、批量造数及20线程循环50000次的执行思路,同时覆盖性能测试需求与软硬件环境设计、脚本组织与Jenkins持续运行、ServerAgent与AWR等监控调优手段,最后落到吞吐量、响应时间、事务成功率、瓶颈分析和报告撰写。资源包共1个pptx文件,约12.48MB,按培训目录分模块展开,便于课堂讲授或自学对照。已有1150人学习,适合希望系统掌握JMeter性能测试方法、补齐方案设计与调优思路的读者参考。

1. 性能测试培训课件拆解:从造数据到调优的一条完整链路

拿到一份性能测试 PPT,多数人第一反应是翻到「聚合报告」那一页看看图,然后关掉。这份《性能测试-Jmeter.pptx》的价值不在这——它把一次完整压测拆成五段:造数据、写方案、执行脚本、监控调优、出报告,而且每一段都给的是具体动作,不是概念图。比如造数据这一节,直接给出了「JMeter 走接口造」和「存储过程直插」两条路线,还把各自的吞吐量摆出来了:接口造一小时 2 到 5 万条,存储过程一小时 10 到 20 万条。这种带数字的取舍,才是这类培训材料里真正值钱的部分。适合谁?正在被要求独立扛一次压测、但不确定从哪下手的中初级测试同学,以及想把流程规范化的小团队。云影像这类业务只是案例载体,方法可以平移到别的系统。

2. 造数据的三条路:为什么我最后选了接口造数

压测跑不起来,十次有八次卡在数据上。线程数设好了,脚本调通了,结果一跑就查不到记录,或者查出来的列表全是空的。数据准备不到位,后面所有指标都是假的。

2.1 初始化数据、铺底数据、参数化数据的区别

课件里把压测数据分成三类,这个划分很实用,因为三类数据的量级和造法完全不同。

初始化数据是系统能跑起来的最小集合,比如字典表、机构表、权限配置。这类数据量小,一般手工或从生产脱敏导一份就够,重点是别缺项。

铺底数据是历史数据,它的作用是让查询类接口有真实的压力。举个具体场景:登记列表页默认按时间倒序分页,如果表里只有几百条,数据库走全表扫描也毫秒级返回,你压出来的响应时间毫无意义;表里有 100 万条,索引是否命中、分页写法是否合理才会暴露出来。课件里那个云影像项目要求登记模块和保存模块各 100 万条,涉及 EXAM_REQUEST、EXAM_PATIENTVISIT 等表,说的就是这个。

参数化数据是跟着脚本走的,每个线程每次请求要用的唯一值,比如患者姓名、证件号、流水号。这类数据不是提前插进库的,而是脚本运行时从 CSV 里读,保证并发时不撞车。

理解这三类的差别,才能判断一个压测场景到底缺什么。我见过有人把参数化数据也提前插库,结果 20 个线程全去查同一条记录,数据库缓存命中率 100%,压出来一片绿,上线就崩。

2.2 接口造数与存储过程造数的取舍

课件给的两条路线,本质是「真实性」和「速度」的取舍。

方式速度优点代价
JMeter 参数化脚本走 HTTP 接口约 2–5 万条/小时不依赖表关系,走完整业务校验,数据真实慢,受接口性能限制
存储过程 / Java 代码直插约 10–20 万条/小时快,可批量循环必须吃透表关联,SQL 写错就产脏数据

课件作者的选择理由写得很实在:不熟悉云影像系统的表关系,时间又相对充裕,所以走接口造数。这个判断我认同。表关系复杂的系统,硬写 INSERT 的隐性成本极高——主表插了,子表的外键没对上,或者某个状态字段没按业务流转赋值,造出来的数据在业务上根本查不出来,等于白干。而且脏数据一旦进了库,清理比重造还麻烦,属于典型的后悔药没处买。

反过来说,如果目标表关系清晰(比如就一两张独立的日志表),或者数据量要求到千万级、工期又紧,那存储过程是更现实的选择。

2.3 接口造数的五步操作与线程配置

课件给的步骤可以直接抄,我按自己的习惯补了参数说明。

第一步,抓接口。用抓包工具把登记保存接口的完整请求抓下来,重点看三样:URL、请求头(尤其是 Content-Type 和鉴权 token)、请求体的字段结构。这一步别偷懒,字段少一个,后面批量跑全是失败。

第二步,参数化。把请求体里需要变化的字段抽成变量。JMeter 里通常配 CSV Data Set Config,把唯一值放文件里。

# CSV 文件示例,一行一条,字段间用逗号分隔 # patient_name,id_card,request_no 张三,P440101199001011234,REG20240101000001 李四,P440101199001011235,REG20240101000002

CSV 里的唯一字段要真正唯一,尤其是证件号、业务流水号这类有唯一索引约束的字段。复用同一个值,第一轮成功,第二轮就报唯一键冲突。

第三步,调试。课件写的是「设 10 个线程跑一次」,这个量级很合理:既能暴露并发下的参数冲突,又不至于一上来就淹掉日志。看什么?看响应断言是否全绿、看数据库里是否真的落了 10 条、看有没有主键冲突或事务回滚。

第四步,批量造。确认单轮没问题再放开。

第五步,配置循环。100 万条的量,课件给的是 20 线程 × 循环 50000 次。

# 线程组关键参数(JMeter 5.1.1) 线程数 Threads: 20 Ramp-up 时间: 20 # 20 秒内起满 20 个线程,避免瞬间冲击 循环次数 Loop Count: 50000 # 合计请求数 = 20 × 50000 = 1,000,000

Ramp-up 别设成 0。20 个线程同一毫秒发起请求,服务端连接池瞬间打满,会出现一批莫名的连接超时,让你误以为是脚本问题。

工具版本上,课件用的是 JMeter 5.1.1,本地和 Jenkins 上保持一致很重要。版本不一致时,JMX 脚本里的某些组件属性可能解析不同,症状是本地能跑、Jenkins 上线程数莫名其妙变了。

2.4 长任务交给 Jenkins 而不是本地 PC

100 万条按 3 万条/小时算,要跑三十多小时。本地 PC 跑意味着两天不能关机、不能休眠、网络不能断。课件里明确建议放到 Jenkins 上持续运行,这是血泪经验——本地跑一夜,第二天早上发现电脑休眠脚本中断,进度归零。

Jenkins 上跑 JMeter 的常规做法是用命令行模式而非 GUI:

# 无界面模式执行,-n 非 GUI,-t 指定脚本,-l 指定结果文件 jmeter -n -t /data/jmx/create_data.jmx \ -l /data/result/create_$(date +%Y%m%d%H%M).jtl \ -j /data/log/jmeter.log

-l的结果文件按日期命名,避免多轮覆盖;-j单独指定日志,出问题时有据可查。另外建议在 Jenkins 任务里加超时和失败重试策略,造数这种长任务挂掉重来一次成本太高,脚本里做好断点续跑的设计(比如用已插入的最大流水号做起点)能省下大量时间。

3. 性能测试方案怎么写:环境表和指标口径是命门

方案写得好不好,直接决定压测结果有没有人信。见过太多方案,需求写一句「系统需支持 500 并发」,环境写一行「测试环境」,结果报告出来没人认。

3.1 性能需求必须落到可测量的指标

课件强调方案第一项是「系统处理性能要求」,一般由项目组提供。这份需求规格说明书里该有什么?我认为至少要落三项:并发用户数、事务响应时间的上限、事务成功率的下限。

举个可执行的写法:登记保存事务,在 50 并发下,90% 响应时间不超过 2 秒,事务成功率 100%。这样写的好处是,测试结果出来能直接对照判定通过与否,而不是拿一堆曲线让人自由心证。

如果项目组只给了一句「要快」,那就得自己补拆:找出核心业务链路,按用户操作频次列出 TOP 5 事务,再跟业务方确认每个事务能接受的最长等待时间。这个沟通过程别省,后期指标不达标时,它就是判定基准。

3.2 环境清单要把硬件和软件分开列

课件给了两张表,一张硬件一张软件,格式可以照搬。我把它合并成一张,实际交付时更清晰:

角色IP硬件配置软件配置
测试客户端10.1.1.18016 核 / 8G / 500G / Win10JMeter 5.1.1 及 agent
应用服务器10.8.3.19916 核 / 64G / 500G / WinServer2012JDK1.8、Tomcat8.0、nginx1.10
数据库服务器10.8.3.17316 核 / 64G / 500G / WinServer2012Oracle 19c

(表中 IP 为课件示例,实际项目按自己的环境填。)

这里有个容易被忽略的点:客户端配置。压测机自己也可能成为瓶颈。8G 内存的 PC 跑几百并发,JMeter 本身就可能 OOM,压出来的响应时间包含了你自己的 GC 停顿。所以并发量上到一定规模,要么加压测机做分布式,要么先把 JMeter 堆内存调大。

# Windows 下改 jmeter.bat,Linux 改 jmeter 脚本 # 默认堆通常偏小,压测机建议调到 2G 以上 set HEAP=-Xms2g -Xmx2g -XX:MaxMetaspaceSize=256m

参数说明:-Xms和-Xmx设成一样,避免运行期堆扩张带来的抖动;Metaspace 留给类加载,脚本里组件多时容易不够。改完重启 JMeter 生效。

3.3 监测指标的口径先对齐再开跑

课件列了一组指标:用户数、事务响应时间、事务成功率、CPU 使用率、内存使用率,还并列了数据库侧的 TOP SQL、AWR/ADDM。这张表的价值在于「口径」二字。

「事务响应时间」到底怎么算?是单个 HTTP 请求的耗时,还是包含多个请求的事务控制器耗时?两种口径差出几倍。我的做法是:脚本里用事务控制器把一条完整业务链路包起来,聚合报告里看事务级数据,同时保留子请求数据用于定位瓶颈。报告里必须注明用的是哪个口径,否则开发看了一个数、运维看了另一个数,会上先吵半小时。

「事务成功率」同理,错误判定规则要提前写进脚本断言。HTTP 返回 200 不代表业务成功——很多系统出错也返 200,错误信息藏在 body 的 code 字段里。断言必须校验业务码,否则成功率永远 100%,自欺欺人。

4. 脚本执行:从控制器编排到服务端监控挂载

脚本能跑通和脚本跑得对,是两件事。这一章的内容基本都是课件里踩出来的细节。

4.1 控制器选型与同步定时器的位置

课件给了一条很实用的判断规则:多个接口请求同步进行,用简单控制器;有先后顺序的,用事务控制器。

同步(无依赖)的典型场景是打开首页加载多个静态资源,或者并发查询多个互不相干的列表,它们之间没有数据依赖,谁先谁后无所谓。有顺序的典型场景是登录→下单→支付,后一步依赖前一步返回的 token 或订单号,必须串行。

同步定时器(Synchronizing Timer)的作用类似集合点:让指定数量的线程在同一时刻一起发出请求,模拟真实的瞬时并发。位置很关键——它要加在需要并发的那个请求之前,而不是线程组最前面。加错位置的症状是:你以为在压保存接口的并发,实际压的是登录接口的并发。

# 同步定时器参数 模拟用户组的数量: 50 # 攒够 50 个线程才一起放行 超时时间(毫秒): 10000 # 超过 10 秒没攒够也放行,防止死等

超时时间这个参数别漏。设了集合点但线程数没配够,或者前面有请求失败导致线程提前结束,不设超时会一直卡着不返回,看起来像脚本挂了。

4.2 PerfMon 插件与服务端 Agent 的连通性排查

课件第 3、4 条讲服务端监控:在服务器上跑 ServerAgent,客户端装 PerfMon Metrics Collector 插件,就能采到 CPU、内存。

部署顺序不能反:

  1. 服务端放 ServerAgent 包,Windows 双击startAgent.bat,Linux 执行startAgent.sh。
  2. 确认 4444 端口通。这一步最容易翻车——Windows 防火墙默认拦入站,Linux 有 iptables 或 firewalld。
# Linux 下验证端口连通性,从压测机发起 telnet 10.8.3.199 4444 # 或 nc -zv 10.8.3.199 4444

连不上就去看服务端防火墙规则和 agent 日志。Windows 上比较隐蔽的一种情况是:bat 双击后窗口一闪而过,其实是缺 JRE 或者端口被占,得在 cmd 里手动执行才能看到报错。

如果端口始终打不通,课件也给了退路:用 nmon 这类系统自带工具采集,事后分析。监控手段可以换,但「服务端资源曲线」这一项不能没有,否则响应时间变长了,你连是不是 CPU 打满都说不清。

PerfMon 采集间隔默认 60 秒,压测时长只有几分钟的场景,这个间隔太粗,会漏掉瞬时峰值。建议改成 5 到 10 秒。

4.3 jconsole 远程连接与内存、线程两页怎么看

JVM 层面的监控,课件选的是 jconsole,理由是它同时覆盖了 jstat 和 jstack 的常用能力,图形化更直观。

前提是服务端开 JMX 远程端口。Tomcat 下一般在 catalina 启动参数里加:

-Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=9004 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false

参数说明:port自定义,别和业务端口撞;authenticate=false和ssl=false仅限内网测试环境使用,生产环境必须开认证。这几项加完要重启 Tomcat。

启动 jconsole,输入IP:端口连接。看两页就够:

内存页用于判断泄漏。健康的形态是锯齿状——压力上来内存爬升,Full GC 后回落。可疑的形态是每次 Full GC 后谷底都比上一次高,压力撤除后也回不到初始水位。课件里说的「十有八九存在内存泄漏」就是这个特征。

线程页相当于可视化的 jstack。看到大量线程卡在同一状态,就点进去看堆栈。课件总结的三类停顿原因很准:等待外部资源(数据库连接、网络)、死循环、锁等待。区别方法是看堆栈停在哪个方法上——停在 socketRead 多半是等下游,停在业务方法的 synchronized 块里就是锁。

4.4 AWR 快照的手工触发与报告生成

Oracle 侧的现场取证靠 AWR。默认一小时一次快照,颗粒度对短场景来说太粗。课件给的做法是手工触发:

-- 测试完一个场景,手工打一个快照 exec DBMS_WORKLOAD_REPOSITORY.CREATE_SNAPSHOT(); -- 生成 AWR 报告,? 代表 ORACLE_HOME @?\RDBMS\ADMIN\awrrpt.sql;

操作要点:每个场景跑完立刻打快照,快照区间就对应这个场景。跑完所有场景再统一生成报告也可以,生成时选起止快照 ID 即可。

课件还提了一个版本差异:Oracle 11g 想拿到 SQL 优化建议,得额外跑ADDMrpt.sql;19c 两个脚本已合并。这个细节很实用,11g 上找不到建议别以为是没采集到。

另外注意 AWR 是有采集开销的,快照频率打得太密(比如几分钟一次)本身会干扰压测结果。手工触发、一个场景一次,是比较稳妥的节奏。

5. 排坑记录:压测现场最常见的五类翻车

课件第 4 章讲监控调优,方法论之外,我把实际会遇到的问题按「现象 → 原因 → 解决」整理了一遍。这些坑不提前知道,大概率要花整晚去查。

5.1 事务成功率 100% 但业务其实是失败的

现象:聚合报告全绿,Err 为 0,但数据库里没数据,或者数据状态不对。

原因:断言只校验了 HTTP 状态码。系统内部异常被全局异常处理器捕获后,仍然返回 200,错误码放在响应体的 code 字段里。

解决:加 JSON 断言,校验业务码字段等于成功值,同时校验关键业务字段非空。压测脚本的断言宁严勿松,漏判比误判危险得多。

5.2 CPU 占用率高但找不到热点

现象:服务端 CPU 长期 80% 以上,Top SQL 看了一圈都不慢。

原因:CPU 的消耗分 User、Sys、Wait 三块,不区分就找不到方向。Sys 高往往是频繁系统调用或上下文切换,Wait 高则可能是 IO 等待——课件里举的例子很典型:大表全表扫描没走索引,CPU 大量时间处于 wait。

解决:先用 top 看 CPU 时间分布,再用 pidstat 看具体进程和线程,Wait 高就去查磁盘 IO 和慢查询的执行计划,确认索引是否命中。

5.3 并发上不去,加了线程响应时间反而涨

现象:50 线程时响应 1 秒,加到 100 线程响应变 3 秒,吞吐量几乎没涨。

原因:瓶颈不在应用层,可能在连接池、数据库或者压测机自身。也可能是同步定时器的超时设置导致线程被长时间挂起,实际有效并发远低于设定值。

解决:先确认压测机资源没打满(JMeter 自己 GC 频繁也会这样),再看应用连接池上限和数据库最大连接数。吞吐量不随并发线性增长是正常的,但响应时间陡增说明已经过了拐点,此时的并发数就是容量上限。

5.4 造数跑到一半中断,重跑就主键冲突

现象:长任务中断后重跑,前几千条全部失败,报唯一键冲突。

原因:脚本从头开始跑,CSV 也从第一行读,和已入库的数据撞车。

解决:造数脚本要设计成可续跑——用已插入数据的最大流水号作为 CSV 起始偏移,或者让流水号里带时间戳保证全局唯一。长任务放 Jenkins 上加失败重试,别指望一次跑完。

5.5 监控曲线和实际压测区间对不上

现象:报告里的 CPU 曲线大部分是平的,看不出压力峰值。

原因:PerfMon 采集间隔太长(默认 60 秒),或者压测开始后监控才启动,时间轴错位。

解决:采集间隔调到 5 到 10 秒,并且先起监控再起压测,结束顺序反过来。报告的图注里写明监控起止时间,方便和事务曲线对齐。

6. 报告落地与一套可复用的收尾习惯

课件把「如何写性能测试报告」放在第五节,篇幅不长,但这恰恰是整件事的交付物。前面造了多少数据、调了多少参数,最后都要收敛到几页纸上,让没参与压测的人能看懂结论。

报告的结构我按课件给的骨架来:测试目的、测试环境、测试方法、测试结果、瓶颈分析、调优建议。其中真正决定报告质量的,是后两项能不能把「现象」翻译成「原因」。

拿结果部分举例,不要只贴聚合报告的截图。截图里几十行数据,读报告的人抓不住重点。我的做法是抽出一张核心事务表:

事务并发数平均响应(ms)90% 响应(ms)成功率判定
登记保存508201450100%通过
列表查询502100380099.8%不通过

表下面紧跟一句结论:列表查询在 50 并发下 90% 响应 3.8 秒,超出 2 秒的指标要求,瓶颈定位为分页查询未命中索引导致的磁盘 IO 等待,建议对该查询的排序字段建联合索引。这样开发和 DBA 拿到报告就知道该动哪里,而不是再来问你一遍。

瓶颈分析的写法,建议按「证据链」组织,而不是按「怀疑对象」组织。比如不要写「怀疑数据库有问题」,而是写:压测期间数据库服务器 CPU 的 Wait 占比达 65%,AWR 报告中 TOP SQL 第一条为列表查询语句,执行计划显示全表扫描,逻辑读 120 万次。每一步都有数据支撑,结论才站得住。

调优建议要给出验证方式。改完之后用同样的脚本、同样的并发、同样的数据量再跑一轮,对比两轮的核心指标。这一步很多人省了,导致建议提了一堆,到底有没有效果没人知道。复测时注意数据量要保持可比,第一轮跑完表里多了 100 万条,第二轮的环境已经变了,这时候指标变好可能只是因为缓存热了,不能全记在调优头上。

有个习惯我一直在用,也推荐给做这块的同学:每次压测前,先把这一轮要验证的假设写在一张便签上——是验证 50 并发下的响应时间,还是验证某个索引优化是否生效,还是摸底系统容量上限。假设不同,脚本的线程模型、造数量级、监控项都不一样。最怕的是什么都不想清楚就开跑,跑完对着一堆曲线不知道要说明什么。

从那以后我每次启动压测任务前,都会强制走一遍这张便签:本轮假设是什么、对应哪个事务、数据量够不够、监控开没开、结果文件存哪里。这五个问题答不上来就不点开始。这套课件里的流程给了很好的骨架,造数和执行的细节可以直接照着做,剩下的就是每轮压测自己补上那点判断。希望帮到你。

本文还有配套的精品资源,点击获取

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进