☰
JMeter插件与服务器监控实战:PerfMon与性能瓶颈定位
2026/10/1 1:41:10 网站建设 项目流程

1. 先把插件和服务器监控这两件事的关系捋顺

1.1 原生 JMeter 在真实项目里到底卡在哪

JMeter 装完打开,新建线程组、加个 HTTP 请求、挂个聚合报告,跑起来看着挺像那么回事。可真接到一个上线前的性能验证任务,这套原生组合很快就不够用了。第一次让我意识到问题,是一次接口压测:客户端曲线显示平均响应时间从 80ms 爬到 300ms,我盯着这个数字盯了半小时,完全说不出原因,因为服务端对我而言就是个黑盒。

后来复盘,原生 JMeter 的短板其实很集中。一是负载模型太粗,线程组基本只有"固定线程数 + 循环次数 + ramp-up"这几个旋钮,想做阶梯加压、波浪加压、按到达率加压这些更贴近真实流量形态的模型,得靠逻辑控制器硬拼,拼到最后脚本自己都读不下去。二是服务端不可观测,TPS 掉一半,你不知道是 CPU 打满、内存开始换页、磁盘 IO 堵住还是连接数撞墙,光靠客户端一条响应时间曲线根本归不了因。三是数据处理偏弱,参数化、断言、结果清洗这些每天都要碰的活儿,原生组件能做,但做起来笨,尤其是从数据库取值、按随机顺序读 CSV 这种高频需求。

这三个短板,正好对应三类插件加一类监控体系:线程组类插件、结果图表类插件、数据处理类插件,以及服务器资源监控。jmeter 插件和服务器监控在我这里从来不是两个独立话题,它们必须串成一条链路才有意义——脚本负责把压力发出去,监控负责告诉我压力打到哪儿去了。把这条链路想清楚,后面挑插件就不会乱。

1.2 插件来源分三层,优先级别搞反

市面上的 JMeter 扩展来源很杂,我习惯按三层来分,装的时候也从第一层往第三层走。

第一层是Plugins Manager 能直接检索到的包,也就是 jmeter-plugins.org 维护的那批,包名大多带jpgc-前缀。这批的优势是版本有人管、依赖有人管、装上就能在菜单里看到,出问题概率最低。我 90% 的场景只在这一层里找。

第二层是官方一直在更新、但没进插件管理器索引的组件,典型代表就是 JSON / YAML 相关的处理包、MQTT 采样器这类。它们通常要自己下 jar 丢进lib/ext,版本冲突得自己盯。

第三层是第三方自研或公司内部封装的插件。这一层看着自由,实际维护成本最高——原作者不更了、JMeter 升级后二进制不兼容、内部有人改过代码没留说明,这种坑我踩过不止一次。所以我的原则是:能用第一层解决就绝不碰第三层,除非有非它不可的理由。

顺带说一句,网上搜"jmeter 下载"时经常会混进一堆和性能测试无关的东西,比如各类编辑器插件、下载器扩展、游戏画质补丁之类,那些跟本话题不在一个频道上,别被搜索结果带偏。JMeter 的扩展只从官方站点和可信仓库走,这是底线。

1.3 我筛插件时看的三个硬指标

装插件不是越多越好,装多了启动变慢、菜单变乱、还容易互相打架。我自己的筛选标准有三条,分享出来你可以直接套。

第一,这个插件补的是能力缺口,还是只是让我少点几下鼠标。前者装,后者不装。比如 Ultimate Thread Group 补的是负载模型能力,必装;而某些只是把三个组件打包成模板的插件,完全可以不加。

第二,它是否引入额外依赖或额外进程。PerfMon 要在被测机上跑一个 ServerAgent 进程,这就属于引入额外依赖,必须评估部署成本和端口开通成本,不能想当然。

第三,它的输出能不能被下游消费。插件产生的数据要么能进 JTL 文件,要么能在 HTML 报告里体现,要么能被外部监控平台接住。如果一个插件只在 GUI 里画个图、导不出来,那它能提供的价值就非常有限,只适合临时看一眼。

这三条不是拍脑袋定的,是我被"装了十几个插件最后用不上三个"坑过之后的结论。你如果刚开始搭环境,建议先只装后面第 2 章列的那几个核心包,跑顺了再按需加。

2. Plugins Manager 与常用插件安装实操

2.1 版本对应关系先对齐,再动手

插件装不上的问题,八成出在版本没对齐。JMeter 从 5.x 开始,插件管理器是独立 jar 包,不再随主程序发布,得自己放。同时它对 JDK 版本也有要求,JMeter 5.6 以上建议 JDK 8 或 11,跑 JDK 17 也能用但个别老插件会报类找不到。

落位很简单,两个 jar 各就各位:

# 插件管理器本体,放 lib/ext cp jmeter-plugins-manager-1.10.jar /opt/apache-jmeter-5.6.3/lib/ext/ # 命令行运行器,插件管理器用它执行一些后台动作,放 lib cp cmdrunner-2.2.jar /opt/apache-jmeter-5.6.3/lib/

cmdrunner这个包经常被漏掉,漏了之后表现是插件管理器能打开、能勾选,但点安装没反应或者直接抛异常。我第一次装就栽在这,折腾了四十分钟才反应过来少了个 jar。放完重启 JMeter,在Options菜单里看到Plugins Manager就说明挂载成功了。

注意:不同 JMeter 版本对应的插件管理器版本不同,5.4 以前用 1.4 左右的版本,5.5 以后建议 1.8 以上。jar 包名里的版本号和你主程序的版本对不上,先怀疑这里。

2.2 核心插件清单,每个说清楚解决什么

下面这几个是我每次重装环境都会勾上的,按用途分组说。

负载模型类,主要是Custom Thread Groups,勾选安装后会带出 Ultimate Thread Group、Stepping Thread Group、Arrivals Thread Group 三个。Ultimate 是最好用的一个,它用表格描述"每一批线程什么时候起、维持多久、什么时候停",做阶梯加压和持续压测直接填表就行,不用再拿逻辑控制器拼。Stepping 更适合看系统在逐级加压下的拐点。Arrivals 是按"每秒新增多少请求"来配,适合模拟真实用户的到达节奏,而不是简单的并发数。

结果图表类,3 Basic Graphs和5 Additional Graphs是一对。前者给响应时间、吞吐量、活跃线程三条基础曲线,后者给响应时间分布、每秒响应数、事务数、延迟和连接时间等更细的维度。这两个包最大的价值是把结果从"一堆数字"变成"能看出形状的线",找拐点特别直观。如果你还要做趋势对比,可以再加jpgc-graphs-dist。

服务端监控类,就是PerfMon Metrics Collector,包名jpgc-perfmon。它负责在压测过程中同步采集被测机的 CPU、内存、磁盘、网络。这个包本身只是 JMeter 侧的采样器,被采端还要部署一个 Agent,第 3 章细说。

数据处理类,Random CSV Data Set解决"CSV 顺序读导致所有线程拿同一批数据"的问题;jpgc-functions提供一批增强函数,比如生成 UUID、随机串、加密摘要这类;JSON/YAML Plugins让 JSON 断言和 JMESPath 提取更顺手。

调试类,Dummy Sampler是我用得最多的一个。它不发真实请求,可以自定义响应码、响应体、延迟时间,用来验证断言逻辑、后置处理器、正则表达式对不对,比拿真实接口试错快得多,也不会污染服务端日志。

埋一个经验点:装完插件后菜单会多出一大块,建议顺手把不用的快捷键和面板清理一下,尤其团队共用一套脚本时,插件差异会导致脚本在别人机器上打不开——JMeter 遇到未知组件会直接报错并忽略该元件,这个坑后面第 5 章会讲。

2.3 下载慢或者拉不下来怎么办

插件管理器的索引和包都在境外站点,网络条件不好的时候进度条会一直转。这种情况我有两个应对方式,按优先级排。

优先方式是在一台能正常访问的机器上装好,然后把整个 JMeter 目录(重点是lib/ext、lib、bin下的新增文件)打包拷过来。插件管理器装完后新增的 jar 基本都落在lib/ext里,少数辅助文件在lib,一起带上就行。跨机器迁移时记得确认目标机器的 JDK 版本一致,否则可能出现类版本不匹配。

次选方式是手动下离线包再铺。以 PerfMon 为例,从官方仓库下JMeterPlugins-Standard-x.x.x.zip,解压后把lib/ext里的 jar 覆盖过去,重启即可。这种方式最麻烦的地方是依赖,PerfMon 依赖的基础包如果缺失,采出来的数据会不全,表现是只有 CPU 没有磁盘 IO,遇到这种先检查是不是只装了一个 jar。

提示:不管用哪种方式,装完都别急着跑正式脚本,用一个最简单的 HTTP 请求验证一下,确认插件真的在场。

2.4 装完怎么确认插件真的生效

确认方式分三层,逐层排查最省时间。第一层看菜单,重启后Options、Add里能不能找到对应元件,找不到就是 jar 没落位或者版本冲突。第二层看启动日志,JMeter 启动时bin/jmeter.log会记录加载了哪些扩展包,有异常会打堆栈,这是排查类找不到问题最快的地方。第三层做一次功能验证,比如挂一个 Dummy Sampler 配一个响应断言,看断言能不能正常判定,能判定说明扩展加载链路是通的。

我一般会把这三步写成一段笔记放在团队文档里,新人配环境照着走,基本不用问我。另外补一句,如果脚本要交给别人跑,最好在项目说明里列清楚用到的插件和版本号,这一步花两分钟,能省掉对方半天的排查时间。

3. 服务器监控:PerfMon 与 Prometheus + Grafana 两条路线

3.1 ServerAgent 部署,端口和权限是两个坎

PerfMon 的完整链路是:JMeter 侧挂PerfMon Metrics Collector采样器,被测机上跑ServerAgent,两者通过网络通信,JMeter 每 N 秒拉一次指标并写进 JTL。Agent 包在JMeterPlugins-Standard的压缩包里,解压后目录里带startAgent.sh和startAgent.bat,Linux 下先赋执行权限:

unzip ServerAgent-2.2.3.zip cd ServerAgent-2.2.3 chmod +x startAgent.sh ./startAgent.sh --tcp-port 4444 --udp-port 4445

默认控制通道走 4444,部分版本的数据回传还会用到 4445,如果你的压测机跨网段访问被测机,这两个端口都要放通,而且注意 UDP 也是要放的。这是最常见的第一个坎——JMeter 侧显示Waiting for sample转圈,八成就是端口没通。

第二个坎是权限。采集磁盘 IO、网络 IO、进程信息这些指标,在 Linux 下需要读/proc下的部分文件,普通用户有时拿不到完整数据,表现是 CPU 和内存有数、磁盘 IO 是空的。解决方式是用有足够权限的用户启动 Agent,或者只勾选确实能采到的指标,不要勾一堆空指标污染报告。

顺带说一个部署细节:Agent 是常驻进程,压测结束后记得关掉。我见过有同事忘了关,第二天做基线对比时发现机器上挂着一堆 Agent 进程,虽然不占多少资源,但对排查问题时的干扰是实打实的。

3.2 JMeter 侧采样器怎么配才不白采

PerfMon Metrics Collector的配置有几处容易配错。第一个是主机和端口,主机填被测机 IP,端口填 Agent 的 4444,多个被测机就加多行,每一行独立配指标。

第二个是指标选择。常用的有 CPU、Memory(区分 Physical 和 Swap)、Disks I/O(读写字节数、读写次数、队列长度)、Network I/O、TCP 连接数、Processes。我的建议是只勾和本次压测目标相关的指标,比如压的是 IO 密集型服务,磁盘队列长度和读写次数必勾;压的是纯计算型接口,CPU 和内存就够。指标勾多了,采样间隔内采集本身会带来额外开销,还可能让 JTL 文件膨胀得很快。

第三个是采样间隔。默认 1 秒,短时间高压测下这个粒度合适;如果是持续几小时的长时间压测,间隔调到 5 秒甚至 10 秒,数据量能降一个量级,趋势照样看得清。这一点很多人不注意,结果压测跑完 JTL 大得打不开。

配好之后跑一次短测,在"监听器"里看曲线是不是和客户端曲线同步出现。如果服务端曲线是一条平直的线,多半是 Agent 没采到值,别急着下"服务端没压力"的结论。

3.3 Prometheus + Grafana 这条路的优势在哪

PerfMon 胜在轻、快、和 JMeter 天然集成,缺点是它只在压测期间采,压测前后的机器状态看不到。如果你的团队本来就有Prometheus 平台监控服务器资源的基础设施,那我更推荐直接用现成的监控栈,压测时只要把时间窗对准就行。

做法很简单,被测机上跑一个node_exporter,它把 CPU、内存、磁盘、网络、文件系统、负载等指标暴露成 HTTP 接口:

nohup ./node_exporter --web.listen-address=:9100 > /dev/null 2>&1 &

Prometheus 侧加一条抓取任务指向被测机IP:9100,Grafana 侧导入 Node Exporter 全量看板,这一步网上资料很多,不再展开。做完之后,压测期间产生的所有资源曲线都会被持续记录,Grafana 的时间轴上你可以任意缩放,回看压测开始前 10 分钟、结束后 10 分钟的机器状态,这对判断"压测期间 TPS 掉是因为上一轮压力还没释放"这种问题特别有用。

进阶一点的做法是把 JMeter 自己的指标也推进监控体系。社区有把 JMeter 结果推到 Pushgateway 的方案,这样客户端和服务端的曲线能画在一张图上,找拐点的效率会高很多。代价是要多维护一套推送逻辑,脚本改动量不小,值不值得看你团队的压测频次。

3.4 时间对齐是最容易被忽略的坑

这一条我想单独拎出来讲,因为它几乎每次压测都会有人踩。JMeter 机器和被测机如果时钟不同步,两边曲线在图上错开几十秒甚至几分钟,你拿服务端 10:05 的 CPU 峰值去解释客户端 10:02 的响应时间抖动,结论就是错的。

解决办法很朴素:压测前用 NTP 把 JMeter 机器、被测机、监控服务器的时间都校准一次,误差控制在 1 秒以内。压测结束后先对一下三台机器的时间戳偏差,再开始分析。

第二个对齐点是 Grafana 的时间窗。默认时区如果和服务器时区不一致,曲线整体会平移。我第一次用 Grafana 做压测分析时,明明压测是下午两点开始,图上的压力段显示在早上六点,找了半天才反应过来是时区问题。分析前先把 Grafana 的时区设成和你日志一致的时区,这一步花十秒钟,能省掉一次误判。

注意:跨时区团队协作时,所有时间统一用 UTC 记录,展示层再转本地时区。日志和监控两边时区规则不一致,是最折磨人的问题。

4. 压测脚本里的高频场景:录制、参数化、断言、文件上传

4.1 HTTPS 脚本录制与证书导入

录制是搭脚本最快的方式,尤其是业务链路长、参数多的场景。JMeter 从 3.x 起就内置了HTTP(S) Test Script Recorder,位置在测试计划下添加非测试元件。

HTTPS 录制的前提是证书。第一次启动录制器时,JMeter 会在bin目录生成一个ApacheJMeterTemporaryRootCA.crt临时证书,你需要把这个证书导入到发起请求的客户端(浏览器或系统)的受信任根证书列表里,否则抓到的请求会直接失败。这一步在 Windows 上是双击证书导入,选"受信任的根证书颁发机构",在 Linux 上是往系统证书库里复制并更新信任链。

导入完成后,把客户端的网络出口指向127.0.0.1:8888,这个 8888 就是录制器的监听端口。然后在录制器界面上点启动,客户端上正常操作一遍业务流程,请求就会被记录下来。录完记得把网络出口改回去,不然客户端会一直走录制器,关了 JMeter 之后直接上不了网。

录出来的脚本不能直接用,一定要做三件事:一是清理无关的静态资源请求,用URL Patterns to Exclude把图片、CSS、JS 过滤掉,不然脚本里一半是垃圾请求;二是排查是否有敏感信息被明文写进请求参数,密码、令牌这类字段要换成变量或配置元件管理;三是核对每个请求之间的依赖关系,尤其是带会话、带前置单号的链路,录制器不会帮你处理关联,得自己加正则提取器把上一步的返回值传给下一步。

4.2 数据库参数化取值怎么写

用数据库里的真实数据做参数化,是让压测更接近真实场景的关键一步。整套配置由三个元件组成:JDBC Connection Configuration负责建连接池,JDBC Request负责执行查询并把结果存进变量,CSV Data Set Config或者循环控制器负责把变量按行分配出去。

先配连接池。填数据库地址、库名、账号密码,以及 JDBC 驱动类名,比如 MySQL 用com.mysql.cj.jdbc.Driver。驱动 jar 要放到lib目录下,不然启动就报找不到类。这里有个小坑:JDBC 密码在 JMX 文件里是明文存储的,脚本传出去等于把库密码传出去了,团队协作时要么脱敏,要么把密码放到外部属性文件里用${__P()}读。

再配查询。在JDBC Request的 Query 里写 SQL,Variable Names填一个名字,比如user_ids。JMeter 会把结果按user_ids_1、user_ids_2这样编号存变量,同时提供user_ids_#表示结果行数。取值的时候要自己算行数和随机索引,这是最容易出错的地方,很多人以为变量名写上去就能自动循环,其实不会。

我的做法是加一个 JSR223 前置处理器,用${user_ids_#}拿到总行数,随机生成一个索引,再拼出变量名去取值。这样每个线程每次都能拿到不同数据,避免所有线程都拿第一行的情况。用 CSV 文件做参数化时同理,顺序读会让所有线程开头都拿到同一批数据,Random CSV Data Set插件就是专门解决这个问题的。

提示:查询结果集别太大,几十万行全读进内存会直接把 JMeter 的堆撑爆。用LIMIT限制一下,采样样本够用就行。

4.3 BeanShell 断言和 JSON 断言怎么选

断言决定压测结果可信度,我一直觉得这是整个脚本里最不能被忽略的部分。原生 JMeter 提供了响应断言、大小断言、持续时间断言,配合BeanShell Assertion可以做复杂逻辑判断,比如解析响应体、校验业务码、比对字段值。

BeanShell 断言的基本写法是在脚本里判断条件,不满足就设置失败标志:

String resp = new String(ResponseData); if (!resp.contains("\"code\":\"0000\"")) { Failure = true; FailureMessage = "业务码非成功: " + resp; }

但我要说一个实际经验:BeanShell 解释执行性能很差,在高并发下它会成为瓶颈,尤其是每个请求都跑一段脚本时。我的替代方案是优先用JSON Assertion和JMESPath Assertion这类专项断言,它们底层是编译执行,效率高得多。只有在逻辑确实复杂、需要写多行分支判断时才用 JSR223 + Groovy,它同样支持Failure和FailureMessage变量,速度比 BeanShell 快一个数量级。

还有一个常见错误是断言写在错误的层级。断言加在采样器下只对那一个请求生效,加在线程组下会对组内所有采样器生效,加在测试计划下会全局生效。放在错的层级,表现为有的请求明明失败却没被标记,或者一个正常的静态请求把整个事务判失败。

4.4 RESTful 参数与文件上传的写法

现在接口大多是 RESTful 风格,JMeter 的 HTTP 请求元件直接支持 GET、POST、PUT、DELETE、PATCH。写这类脚本有几个固定套路。

路径参数直接拼在 URL 里,比如/api/orders/10086。查询参数用Parameters面板加,注意勾上编码。请求体参数切到Body Data面板写 JSON,同时必须加一个HTTP Header Manager,把Content-Type设成application/json,否则服务端很可能按表单解析,直接返回 400 或参数为空。这个错误我见过太多次,表现是 Postman 里好好的,到 JMeter 就是取不到参数。

文件上传要勾选Use multipart/form-data,然后在Files Upload里填文件路径、参数名、MIME 类型。三点提醒:路径尽量用相对路径配合CSV Data Set Config管理,方便换机器;被测服务通常对上传大小有限制,超过会直接断连,这种报错在客户端表现为error writing to server;测试用的文件不要放太大,几百 KB 到几 MB 就够,除非你专门要测大文件传输。

令牌、会话这类鉴权信息统一放到HTTP Header Manager里,值用变量引用,变量值放在User Defined Variables或者从上一个接口的响应里提取。这样脚本结构会清爽很多,换环境时只改变量,不用满脚本找硬编码。

5. 常见报错与排查速查

5.1 error writing to server 到底在说什么

java.io.IOException: error writing to server是 JMeter 里出现频率最高的报错之一,它的字面意思是"往服务端写请求数据时出错",也就是请求还没发完这个连接就废了。原因基本集中在四类。

第一类是请求体太大,尤其是上传文件场景,服务端设了请求体积上限,超过就直接掐断连接。第二类是服务端处理超时主动断连,客户端还在写就被 reset 了。第三类是长连接复用的问题,JMeter 默认开启 keep-alive,某些服务端或中间层在空闲一段时间后关闭连接,JMeter 复用这个已关闭的连接就会报这个错,解决办法是在 HTTP 请求的Advanced里取消勾选Use KeepAlive,或者给 HTTP 采样器加一个Connection: close的请求头。第四类是客户端资源不足,堆内存给小了,大响应体处理不过来,这个通常伴随 Full GC 日志。

排查顺序我的习惯是:先看压测并发量和报错比例的关系,只在高压下出现多半是连接复用或资源问题;固定比例出现多半是数据问题;单个采样器稳定报错就单独复现,把日志级别调成 debug 看完整堆栈。这份经验值钱的地方在于,光看这行报错本身是推不出原因的,得结合并发规模和复现规律一起判断。

5.2 插件装了却不生效的四种可能

这个问题我在不同团队里至少被问过十次,原因基本跑不出下面四种。

一是 jar 放错目录。JMeter 的扩展类要放在lib/ext,放在lib下只会被当普通依赖加载,元件不会出现在菜单里。二是缺依赖包,比如插件管理器少了 cmdrunner,PerfMon 少了基础包。三是同名的老版本 jar 还留在目录里,两个版本同时存在,某一个被优先加载导致功能异常,这种情况把旧版本删掉就行。四是脚本打开时报"未知元件",那是因为你本机没装对方用到的插件,JMeter 会忽略这个元件继续跑,但它对应的逻辑就丢了——这也是为什么我坚持在项目文档里写清楚插件清单。

排查最快的方式是看bin/jmeter.log,启动时每个扩展包的加载情况都有记录,异常也会打堆栈。别看界面,看日志,这是省时间的关键。

5.3 结果树导出和内存的那点事

GUI 下的"察看结果树"是调试利器,但它是内存杀手。默认它会把所有请求的完整响应体缓存在内存里,几千个请求就能把堆吃满,特别是响应体很大的接口。表现是界面越来越卡,最后 OOM。

正确做法是调试阶段才开结果树,而且只保留错误请求,把Log/Display Only设成Errors,跑正式压测时把结果树关掉。要留存数据就用Simple Data Writer或者非 GUI 模式下的-l参数直接写 JTL 文件,格式选 CSV,别选 XML——XML 格式的 JTL 体积能大出好几倍,解析也慢。

导出结果也有讲究。压测结束后用命令行把 JTL 转成 HTML 报告:

jmeter -g result.jtl -o ./report

生成的报告里有聚合表、响应时间曲线、吞吐量曲线、错误分布,比 GUI 上截图规范得多,直接能给到评审会。

5.4 一百个并发的报告怎么读才不误导人

模拟 100 用户并发跑完,报告里字段一大堆,我通常按下面的顺序看,你也可以照着来。

先看Error %,这是门槛指标,超过 1% 就别往下面看了,先解决报错。然后看90% Line和95% Line,这两个比平均值有参考价值得多,平均值会被极端样本拉偏,90 线更能反映大部分用户的真实体验。接着看Throughput,注意它和并发数的关系:并发涨了吞吐不涨,说明系统已经到瓶颈;并发涨了吞吐反降,说明资源在争抢,这时候一定要对照服务端监控曲线看是哪块资源先满。最后看Received KB/sec,估算一下带宽有没有成为瓶颈。

还有一个必须提醒的点:并发用户数不等于每秒请求数。100 个线程如果在跑一个响应时间 500ms 的接口,实际 TPS 大概在 200 上下,线程数除以响应时间才是粗略的请求速率。用线程数当 TPS 去写报告,是最常见的误读,评审会上被人问一句就露馅了。

我自己做性能测试这几年,越来越觉得工具本身不是门槛,门槛在于能不能把客户端数据和服务端数据对起来讲一个完整的故事。脚本写得再花哨,报告里只有一条曲线,那这个测试的价值就只剩一半。反过来,哪怕是几十行 Thread Group 加一个 PerfMon 曲线,只要你能指着 CPU 曲线和响应时间曲线的交点说清"拐点在这儿、原因是什么",这个结论就站得住。插件和监控工具都是为这句话服务的,别本末倒置。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询