简介:《性能测试-Jmeter》培训课件面向性能测试初学者、中级测试工程师及需要承担压测任务的项目成员,围绕JMeter在真实项目中的完整使用链路展开,帮助读者理解造数据、方案设计、脚本执行、监控调优和测试报告编写各环节的衔接关系。包内共1个PPTX文件,压缩包约12.48MB,以图文步骤、配置清单、脚本截图和业务案例组织内容,便于培训授课或按章节自学。课件具体覆盖初始化数据、铺底数据与参数化数据的准备,JMeter模拟HTTP请求和存储过程直插数据库两种造数据方式的取舍,以及抓包、参数化、调试、多线程批量运行的实践思路;同时讲到测试环境软硬件说明、业务场景设计、聚合报告与PerfMon监控、AWR及慢日志分析、性能瓶颈定位和报告结构。已有1150人学习,适合希望把JMeter操作能力延伸到完整性能测试流程的读者参考。
1. 一个版本号,把 100 万条造数据的活拖了三天
JMeter 5.1.1 这个版本号,现在看确实有点年头。可当年那套压测环境就是围着它搭的:压测客户端在 Win10 上跑 5.1.1,Jenkins 服务器上也是 5.1.1,被测的应用服务器跑 Windows Server 2012 R2 + JDK 1.8 + Tomcat 8.0,数据库那台是 Oracle 19c。版本看似一致,坑全藏在“看似”里——本地随手升了个新版写脚本,扔到 Jenkins 上插件对不上号,PerfMon 采集器直接不认;造数据跑到一半Connection closed,二十万条脏数据躺在表里,还得先回滚再重来。
这篇笔记就是按那套环境原样复盘的:从 JDK 和机器清单怎么定,到 Fiddler 抓包、参数化、十线程试跑、二十线程五万循环的造数链路,再到 ServerAgent 4444、jconsole 远程、AWR 快照和最终验证。适合正在搭性能测试环境、或者被jmeter.bat一闪而过折磨过的人。
2. 装 JMeter 5.1.1 之前:JDK、机器和 4444 端口先定死
性能测试环境翻车的重灾区,几乎都不在 JMeter 本身,而在它脚下那三台机器。我见过太多人一上来就双击jmeter.bat,黑窗口闪一下就没影了,然后开始怀疑压缩包下坏了。其实真正常见的就两件事:JDK 版本对不上,或者服务端 4444 不通。这两件先定死,后面百分之八十的“玄学问题”都不会发生。
2.1 JDK 1.8 是与 JMeter 5.1.1 搭配使用的前提条件,并非可选项
JMeter 5.1.1 是基于 Java 8 编译的,在同版本环境里最稳。压测客户端、Jenkins 服务器、应用服务器这三台机器上的 JDK 大版本必须统一,小版本可以差一点,但别一台 1.8 一台 11。先用命令把现状摸清楚:
java -version # 期望输出(三行里第二行是关键) # java version "1.8.0_202" # Java(TM) SE Runtime Environment (build 1.8.0_202-b08) # Java HotSpot(TM) 64-Bit Server VM (build 25.202-b08, mixed mode) echo $JAVA_HOME # 期望输出 /usr/local/jdk1.8.0_202 ,Windows 下用 echo %JAVA_HOME%这里有两个参数要说明。java -version看的是 PATH 里第一个 java,而JAVA_HOME是 JMeter 启动脚本真正读取的变量,两者指向不同 JDK 是最常见的坑:命令行敲java -version显示 1.8,JMeter 启动脚本却去读了另一个目录下的 JDK 11。所以这两条命令必须都跑,输出指向同一个安装目录才算过关。
提示:Windows 上装了多个 JDK 时,除了改系统环境变量,还要检查
Path里 java 的条目顺序,排在前面的那个才生效。
2.2 三台机器的清单:IP、硬件、软件一次列全
性能测试方案里最不该省的就是这张表,压测结果一旦出问题,第一个要回答的就是“当时环境什么配置”。这是那套影像登记系统的实际清单,照着填自己项目的就行:
| 角色 | IP | 硬件配置 | 系统与软件 |
|---|---|---|---|
| 测试客户端 | 10.1.1.180 | 16 核 / 8G / 500G | Windows 10 旗舰版 SP1 64 位,JMeter 5.1.1 及 ServerAgent |
| 应用服务器 | 10.8.3.199 | 16 核 / 64G / 500G | Windows Server 2012 R2 Standard 64 位,JDK 1.8、Tomcat 8.0、nginx 1.10 |
| 数据库服务器 | 10.8.3.173 | 16 核 / 64G / 500G | Windows Server 2012 R2 Standard 64 位,Oracle 19c |
硬件配置看两个数:核数和内存。客户端只有 8G 内存,二十线程跑长循环的时候要留意 JMeter 自己的堆,别压测机和被测机一起卡。服务端 64G 是给 Tomcat 和 Oracle 留的余量,压测时内存曲线要看的是“压力撤除后能不能回落”,而不是绝对占用数字。
端口这一层,测试客户端到应用服务器、数据库服务器的 4444 必须先打通,这是 ServerAgent 的默认监听端口:
# Linux / macOS 客户端上验证到应用服务器的 4444 telnet 10.8.3.199 4444 # Windows 客户端上用 PowerShell 更直接 Test-NetConnection -ComputerName 10.8.3.199 -Port 4444 # 关注输出里的 TcpTestSucceeded : TrueTcpTestSucceeded为 False 时不要去改 JMeter 配置,先去看服务端防火墙入站规则。Windows Server 默认是拦的,加一条放行 4444 的入站规则,再重启 ServerAgent 才能生效。这条规则没加,后面 PerfMon 图表永远是 “No data”,排查半天还以为插件装错了。
2.3 解压即用:目录、环境变量和插件位置
JMeter 不用装,解压就完事,但目录和环境变量得规矩一点。我一般统一放/opt/apache-jmeter-5.1.1,Windows 下对应D:\tools\apache-jmeter-5.1.1,路径里不要有中文和空格:
# 解压到统一目录 unzip apache-jmeter-5.1.1.zip -d /opt/ # 配置环境变量,写进 ~/.bashrc 或 /etc/profile export JMETER_HOME=/opt/apache-jmeter-5.1.1 export PATH=$JMETER_HOME/bin:$PATH # 验证安装,能打印版本和 JVM 信息就算过 jmeter -vjmeter -v的输出里同时包含 JMeter 版本号和 JVM 版本号,两个都要对:JMeter 显示 5.1.1,JVM 显示 1.8.0_xxx。如果 JMeter 版本对而 JVM 是别的版本,说明上面JAVA_HOME那步没生效。
插件是另一套东西。PerfMon Metrics Collector 不是 JMeter 自带的,要先把 Plugins Manager 丢进lib/ext:
# 把插件管理器放到 lib/ext,重启 JMeter 后菜单里才会出现 Plugins Manager cp jmeter-plugins-manager-1.3.jar /opt/apache-jmeter-5.1.1/lib/ext/ # 重启后从 Options -> Plugins Manager -> Available Plugins # 搜索 PerfMon,安装 jpgc - PerfMon Metrics Collector 与 jpgc - Standard Set装完插件必须重启 JMeter,lib/ext下的 jar 是启动时一次性加载的,热装不认。ServerAgent 则是独立的压缩包,解压到被测服务器上,不是压测机上——这点新手特别容易搞反,结果在自己机器上起了个 Agent,当然采不到服务端的数据。
3. 造数据:从 Fiddler 抓包到 20 线程 50000 循环
压力测试之前没有数据,等于空跑。那套影像登记系统的需求是:新建登记模块 100 万条,登记保存模块 100 万条,涉及EXAM_REQUEST、EXAM_PATIENTVISIT等表。造数据这件事有两条路,选错了不是慢一点的问题,是数据准不准的问题。
3.1 两条造数路线的取舍:脚本模拟 vs 直插数据库
| 方式 | 速度 | 优点 | 缺点 |
|---|---|---|---|
| JMeter 参数化脚本模拟 HTTP 请求 | 约 2~5 万条/小时 | 不用关心表关联关系,数据经过完整业务链路,比较真实 | 速度慢 |
| 存储过程或 Java 代码直接插表 | 约 10~20 万条/小时 | 速度快 | 必须先理清表之间的关联,SQL 写错就是脏数据,得重写重跑 |
当时选了第一种。原因很实在:对这套影像系统的表关系不熟,硬写存储过程大概率写出关联错位的数据,而且时间上还算充裕。反过来说,如果表结构清楚、数据量要求到了千万级,第二种才是正解——一小时二十万和一万的差距,在千万级需求面前是几十个小时。
注意:直插数据库的脚本一旦写错,清理成本远高于重跑。用这条路之前,先在测试库小批量跑一百条,把关联表 join 出来人工核一遍。
3.2 第一步到第三步:抓包、参数化、十线程调试
第一步用 Fiddler 抓登记保存接口。打开 Fiddler,客户端走一遍完整的新建登记流程,在会话列表里找到提交那一条,看 Inspectors 里的 Raw 请求,把 URL、请求方法、Content-Type 和请求体里的字段全部抄下来。抓包的意义在于拿到真实的参数结构,而不是自己臆想一个 JSON。
第二步把参数里会变的部分抽出来。姓名、身份证号、检查类型、科室编码这类字段做成 CSV,其余固定值直接写死在 HTTP 请求里:
patientName,idCard,examType,deptCode 张某某,110101199001011234,CT,0201 李某某,110101199001015678,MR,0202 王某某,110101199001019012,DR,0203CSV 至少准备几千行,靠循环复用。然后在 JMeter 里加 CSV Data Set Config,参数这样填:
Filename: data/register.csv File encoding: UTF-8 Variable Names: patientName,idCard,examType,deptCode Ignore first line: True Delimiter: , Allow quoted data: False Recycle on EOF: False Stop thread on EOF: True Sharing mode: All threads几个参数值得单独说。Ignore first line设 True 是因为 CSV 第一行是表头,不跳过就会被当成数据插进去,产生一条姓名是“patientName”的脏数据。Recycle on EOF和Stop thread on EOF是一对,造数场景要把它们设成 False 和 True:数据读完就停,避免循环回开头重复插入同一批身份证号,撞唯一索引。Sharing mode选 All threads,二十个线程共用同一个文件游标,每条数据只被用一次。
第三步是调试,这一步千万别省。线程组先设 10 个线程、Ramp-Up 10 秒、循环 1 次,跑完看聚合报告和查看结果树,重点看 error 是不是 0、响应体里业务码是不是成功。十条都过不去,一百万条只会把错误放大十万倍。
3.3 第四步到第五步:20 线程 × 50000 循环,交给 Jenkins
调试通过后改线程组参数,这是造 100 万条的核心算式:
线程数(Number of Threads):20 Ramp-Up 时间:20 循环次数(Loop Count):5000020 × 50000 = 100 万,正好对上需求。Ramp-Up 设 20 秒是为了让线程在一秒一个的节奏里起来,不要瞬间二十个并发把应用打出一片 502。循环次数填的是单线程循环数,不是总数,这一点填错就会变成 20 条或者 100 万 × 20。
长时间跑不建议留在本地 PC 上。二十线程五万循环要跑十几个小时,本机不能关机、不能休眠、不能断网,中间一个系统更新重启就全废。所以第五步是丢到 Jenkins 上跑:
# Jenkins 构建步骤里执行,-n 表示非 GUI 模式 jmeter -n -t register_data.jmx -l result.jtl -e -o report/参数逐个说:-n是 CLI 无界面模式,GUI 模式跑长任务会吃内存还会拖慢压测;-t指定 jmx 脚本路径;-l指定结果文件,jtl 是原始采样数据,后面还能转成图表;-e -o是压测结束后自动生成 HTML 报告到指定目录。-o指向的目录必须是空的或者不存在的,目录里有旧报告会直接报错退出,这是 Jenkins 上最容易踩的一脚。Jenkins 服务器上的 JMeter 版本同样是 5.1.1,和本地保持一致,脚本直接复用不用改。
3.4 另一条路的样例:Oracle 存储过程长什么样
不选直插不代表不会写。真到了需要快的时候,存储过程的基本形态是这样的:
create or replace procedure T_ESB2_AUDIT_sp as begin -- 循环 300 次,每次插入一条审计记录 for i in 1 .. 300 loop insert into t_esb2_audit_new( FID, VERSION, BIZID, REQID, RESID, SOURCESYS, TARGETSYS, RESULTCODE, BODY, CREATETIME ) values ( sys_guid(), -- 主键用 GUID,避免序列冲突 '1.0', '4aa066b7-1660-4ae7-b84c-98eaea24a3ce', sys_guid(), sys_guid(), 'FOSS', 'UIP', 0, '{"code":"12345678","msgId":"12345678:abort"}', sysdate -- 创建时间取当前时间 ); end loop; commit; -- commit 放在循环外,避免逐条提交 end;几个关键点:sys_guid()生成主键,比序列更适合这种一次性造数;commit放在loop外面,每三百条提交一次事务,逐条提交会把速度拖到跟 HTTP 脚本差不多;循环上限 300 是单次执行的量,一百万条要靠外部再套一层循环或者多跑几次。执行就是exec T_ESB2_AUDIT_sp;,跑完记得核对目标表的 count 和关键字段分布,别只看条数对了就收工。
4. 执行脚本装配:控制器、集合点、聚合报告和 PerfMon
脚本能跑通和脚本能测出东西是两回事。同一个接口,控制器选错、集合点没加、监控没采,出来的报告就是一堆没有解释力的数字。这一章是按那套影像系统的脚本装配顺序来的,从结构到监控一步步加。
4.1 简单控制器和事务控制器怎么分工
命名按用例的方式来,一个用例里包含几个接口请求,名字里要能看出场景。控制器选哪个看请求之间有没有先后依赖:
- 各个请求同步进行、互不依赖,用简单控制器(Simple Controller)把它们包在一起,纯粹起分组作用,不影响执行顺序。
- 各个请求有先后顺序、要合并统计,用事务控制器(Transaction Controller),勾上
Generate parent sample,这样几个接口的总耗时会被算成一笔事务,聚合报告里看到的是整个业务流程的响应时间,而不是零散的单个接口。
举个具体例子:一个“登记提交”用例包含查询患者、校验、保存三步,这三步必须按顺序走,用事务控制器;一个“批量查询”用例里同时打三个不相关的查询接口,用简单控制器。分不清的结果是响应时间被拆成一堆碎片,报告里根本看不出业务视角的耗时。
4.2 同步定时器:集合点是压出真实并发的关键
请求执行前要加同步定时器(Synchronizing Timer),就是集合点的概念。不加的话,二十个线程因为有 Ramp-Up,实际上是陆续发出去的,压力是散的;加了之后,线程会在这里等,凑够指定数量再一起放行,瞬间并发才是真的并发。
配置两项:
Number of Simulated Users to Group by: 20 Timeout in milliseconds: 5000第一项填线程数,二十个线程就填 20,填大了永远凑不齐,脚本会卡死等;第二项是超时,五秒还没凑够就先放行,避免因为个别线程异常导致整个脚本挂起。这两个值要和线程组的线程数联动,改了线程数忘了改集合点,是最常见的“压测跑着跑着不动了”。
4.3 聚合报告和断言:先保证 error 为 0
脚本里必须加聚合报告(Aggregate Report),它是判断这轮压测有没有意义的第一道门。关注四列:Samples(采样数)、Average(平均响应时间)、90% Line、Error%。Error% 不为 0 的话,后面的 TPS 和响应时间都不用看了,先回去查错误。
配套加响应断言,别只靠 HTTP 200 判断成功。业务接口经常是 HTTP 200 但响应体里code是失败码,断言里加一条 JSON 断言或者响应文本包含,匹配规则选“包含”,模式填业务成功的标识字段。断言失败会进错误列表,聚合报告里的 Error% 才准。
4.4 ServerAgent 与 PerfMon:4444 通了才有曲线
服务端要先起 ServerAgent。Windows 服务器双击startAgent.bat,Linux 服务器执行startAgent.sh,启动日志里会打印监听的 4444 端口。起完之后回到 2.2 那步验证连通性,telnet 不通就先处理防火墙。
然后 JMeter 这边加监听器 PerfMon Metrics Collector,点 Add Row 配置采集目标:
Host/IP: 10.8.3.199 Port: 4444 Metric to collect: CPU / Memory / Disks I/O采集项按需勾,CPU 和 Memory 必选,磁盘 I/O 在怀疑有全表扫描或者大文件读写时再加。采集间隔默认就行,调太密会给被测机增加额外负担。网络不通的时候不要死磕 ServerAgent,换 nmon 之类的工具在服务端本地采集,效果差不多。
4.5 jconsole 远程连接:内存和线程两个页签
容器层面的监控靠 jconsole。前提是应用服务器先开远程 JMX 端口,在 Tomcat 的启动脚本里追加参数:
# 应用服务器上,把 JMX 远程参数加到 Tomcat 启动脚本 JAVA_OPTS="$JAVA_OPTS -Dcom.sun.management.jmxremote \ -Dcom.sun.management.jmxremote.port=9004 \ -Dcom.sun.management.jmxremote.ssl=false \ -Dcom.sun.management.jmxremote.authenticate=false \ -Djava.rmi.server.hostname=10.8.3.199"参数含义:port=9004是 JMX 监听端口,挑一个没被占用的;ssl=false和authenticate=false是测试内网图省事关掉认证,正式环境不要这么开;java.rmi.server.hostname必须显式写成服务端的真实 IP,不写的话 jconsole 拿到的是主机名或者容器内部地址,客户端就连不上,表现是一直转圈然后超时。
改完重启 Tomcat,本地开 jconsole,选“远程进程”,输入10.8.3.199:9004连接。主要看两个页签:
- 内存页签:相当于可视化的 jstat,看的是收集器管理的那块虚拟机内存。判断内存泄漏的标准不是占用高,而是每次 Full GC 之后占用能不能回落。如果压力撤除一段时间后内存依旧回不到原来的水位,十有八九有泄漏。
- 线程页签:相当于可视化的 jstack。线程长时间停顿的原因无非几类——等外部资源(数据库连接、网络、设备)、死循环、锁等待(活锁和死锁)。点开具体线程看堆栈,卡在哪一行一目了然。
5. 避坑:五个装完跑不通的现场
环境这东西,装成功的样子都差不多,装不成的花样各有各的不同。下面五条是那套环境里真实出现过的,按“现象 → 原因 → 解决”记,照着对号入座能省不少时间。
5.1 jmeter.bat 双击一闪而过
现象:Windows 客户端上双击jmeter.bat,黑窗口闪一下就没了,什么提示都没有。
原因:启动脚本找不到可用的 JDK。要么JAVA_HOME没配,要么系统里装了多个 JDK 而JAVA_HOME指向的那个目录被删了或者路径写错。
解决:开一个 cmd,先执行echo %JAVA_HOME%确认路径存在,再执行"%JAVA_HOME%\bin\java" -version看能不能正常输出。不能就去系统环境变量里重设。想看到真实报错,别双击,从 cmd 里进到 bin 目录执行jmeter.bat,错误信息会留在窗口里。
5.2 PerfMon 图表一直是 No data
现象:ServerAgent 在服务端明明起了,启动日志也正常,JMeter 这边 PerfMon Metrics Collector 加好了,跑起来图表一条线都没有,状态显示连接失败或者无数据。
原因:九成是 4444 端口不通,被服务端防火墙拦了。剩下一成是 Host 填错,或者把 ServerAgent 起在了压测机上而不是被测机上。
解决:先在压测客户端telnet 10.8.3.199 4444,不通就去服务端加防火墙入站规则放行 4444,重启 Agent 再试。通了还采不到,检查 PerfMon 里 Host/IP 填的是不是被测服务器的地址,端口是不是 4444。
5.3 Jenkins 上跑脚本,HTML 报告目录报错
现象:本地跑得好好的命令,放到 Jenkins 构建步骤里直接失败,日志提示输出目录已存在或者无法写入。
原因:-o指定的报告目录里已经有上一次的产物。JMeter 生成 HTML 报告要求目标目录为空,非空直接退出。
解决:构建步骤里先清目录再跑,两条命令连着写:
rm -rf report/ && jmeter -n -t register_data.jmx -l result.jtl -e -o report/顺手把result.jtl也一起清掉,否则新结果会追加到旧文件里,聚合报告的数字会翻倍。
5.4 造数据跑到一半报 Connection closed
现象:前几万条跑得挺顺,跑到中途开始大量报错,日志里出现Connection closed或connection refused,之后成功率一路往下掉。
原因:连接数被打满了。二十个线程持续压,应用服务器到数据库的连接池上限不够,或者数据库端的会话数到了限制,新连接直接被拒。
解决:先降线程数验证——把二十降到十,如果错误消失就说明是连接数瓶颈。然后两条路:调大应用侧连接池上限和数据库会话上限,或者接受这个上限、把造数任务拆成几个批次跑。造数阶段不建议硬顶,把数据库打挂了恢复起来更麻烦。压测阶段遇到这个报错就是另一回事了,那说明系统承载不了当前并发,是有效结论,别去调参数掩盖它。
5.5 AWR 报告里查不到压测时段的数据
现象:压测跑完了,去数据库执行 AWR 脚本生成报告,选时间段的时候发现压测那段时间没有快照,或者只有一个跨越很久的粗粒度快照。
原因:AWR 默认一小时自动打一次快照,短场景压测十几分钟就结束了,正好落在两次快照中间,采集不到。
解决:场景复杂、需要精确对应的时候,改成手工打快照——压测前打一次,压测后再打一次:
-- 压测开始前和结束后各执行一次,手工生成快照 SQL> exec DBMS_WORKLOAD_REPOSITORY.CREATE_SNAPSHOT(); -- 然后生成 AWR 报告 SQL> @?/rdbms/admin/awrrpt.sql;?代表 ORACLE_HOME。还有个版本差异要注意:Oracle 11g 想拿到优化建议,得再跑一遍addmrpt.sql;Oracle 19c 里两个脚本已经合并,跑awrrpt.sql就够了。
6. 跑通验证:一轮压测该留下哪几样证据
脚本跑完不报错,不等于这套环境装好了。我判断一轮压测“算跑通”的标准是四样证据齐全,少一样都不算数。
第一样,聚合报告的 Error% 为 0。打开report/index.html,先看错误率再看响应时间。Error% 大于 0 的时候,平均响应时间再好看也是假的,那些失败请求根本没参与计算。这时候要回去看断言和结果树,定位是参数问题还是服务端顶不住。
第二样,CPU 和内存曲线完整。PerfMon 采集到的曲线要覆盖整个压测周期,开头结尾都有数据。CPU 长期高于 80% 值得警惕,但更要看它是耗在 User、Sys 还是 Wait——如果是 Wait 占比高,就去查磁盘 I/O 和是不是有大表全表扫描没走索引。内存则看压力撤除后能不能回落,回不去的基本可以判定泄漏。
第三样,AWR 报告。压测结束后手工打快照再生成,重点看 Top SQL 和 DB 时间分布,找出耗时最高、逻辑读最多的那几条语句。这一步是数据库层调优的入口,光看应用层响应时间找不到根因。
第四样,日志关键字扫描。服务端日志里搜一遍高频异常,比翻几百兆日志快得多:
# 压测结束后扫服务端日志里的高频异常关键字 grep -E "OutOfMemoryError|Cannot allocate memory|SQLException|Connection closed|connection refused" \ /opt/tomcat/logs/catalina.out \ | sort | uniq -c | sort -rn | head -20这五类关键字分别对应不同方向:OutOfMemoryError和Cannot allocate memory是 JVM 内存溢出,SQLException是数据库语句执行异常(后面通常跟着数据库返回的具体信息),Connection closed和connection refused是连接数被限制、撑不住当前压力。按出现次数排序,排前面的就是瓶颈可能的方向。
从那以后我每次搭完一套压测环境,不管多赶时间,都强制先把这四样在小流量下走一遍:十条线程跑一轮,看错误率、看曲线有没有数、打一次快照、扫一遍日志。全部有数了再往上加并发,比跑到一半发现采不到监控数据再回头补要省事得多。希望帮到你。
本文还有配套的精品资源,点击获取