
第一次用Jmeter跑压测的时候我其实挺抗拒的。满屏英文菜单线程组、取样器、监听器这些概念绕得人头疼。但等我真正拿它做完一次100用户的并发测试把聚合报告里的吞吐量、错误率、响应时间解读清楚之后就再也没想过换回LoadRunner。Jmeter这个压测工具说白了就是一套完全免费、跨平台、插件生态特别丰富的性能测试框架既能做接口自动化回归也能压测Web应用、数据库、消息队列市面上能叫得上名字的中间件基本都能通过插件扩展覆盖到。这篇文章不是什么官方文档翻译是我这几年在实际项目里把Jmeter从安装到落地整个流程踩出来的经验汇总。无论你是刚接触压测的新人还是已经写过脚本但总被各种报错卡住的老手看完应该都能照着复现一套完整可用的压测方案。我会把安装配置、脚本编写、HTTPS录制、参数化、断言、JDBC查询、插件管理、结果分析、常见报错这些环节全部过一遍最后再聊一个真实的云上环境迁移压测案例说说我在项目里是怎么用它验证承载能力的。1. 为什么压测我总跟人推荐Jmeter1.1 我是怎么从LoadRunner转到Jmeter的早年在测一个老旧的MVC3项目时团队里的性能测试工具还是LoadRunnerLicense贵得吓人而且只能在Windows上跑每次配脚本都要折腾半天。后来有个小项目预算不够买License我抱着试试看的心态下载了Jmeter发现它居然能直接通过代理录制HTTP请求还能在Linux服务器上用命令行跑压测出报告也只需要看一眼聚合报告就够了。从那以后Jmeter就成了我压测的首选工具。Jmeter本质上是Apache开源项目用纯Java写的所以只要机器上有JDK就能跑Windows、Linux、macOS都能用这一点在实际项目里太有用了。尤其是压测阶段我经常直接在压测机上跑命令行模式省去图形界面的开销能榨出更多压测资源给到被测系统。1.2 Jmeter最打动我的几个点第一免费且没有并发数限制。LoadRunner社区版顶多支持几十个虚拟用户Jmeter只要你机器扛得住模拟几千并发也不是问题。第二插件机制非常成熟缺什么功能就去装什么插件MQTT、Kafka、Dubbo这些协议都能扩展。第三结果数据能直接落地成CSV或XML方便用其他工具二次分析。当然Jmeter也有坑。它的默认报告界面比较粗糙如果你指望开箱就有像Grafana那样华丽的Dashboard那是想多了。另外某些高级场景需要写Groovy、Beanshell脚本对没接触过代码的同学不太友好。但整体来说在开源压测工具里Jmeter的生态和稳定性依然是第一梯队。2. 环境与基础从安装到第一个压测脚本跑起来2.1 装对JDK、选对版本很多新手卡在安装这一步其实多半不是Jmeter本身的问题而是JDK没装对。Jmeter是纯Java应用直接双击JMeter.batWindows或者执行JMeter.shLinux能启动的前提是你已经装好了Java运行环境。我的建议是装JDK 8或者JDK 11。Jmeter 5.x系列这两个版本都支持得很好太新的JDK反而可能遇到权限相关的兼容问题。装完以后在命令行敲java -version确认一下版本确保JAVA_HOME环境变量已经指向正确路径。这里尤其提醒一句别只装JRE不装JDK虽然Jmeter运行不一定需要编译器但后面写Beanshell、Groovy脚本或做证书处理时缺了JDK里的keytool工具会特别麻烦。下载Jmeter时认准Apache官网的正规渠道不要从乱七八糟的下载站拿官网提供ZIP和TGZ两种压缩包Windows解压ZIPLinux用TGZ。解压后目录结构里重点记住这几个bin启动脚本和配置文件。JMeter.bat是Windows启动入口JMeter.sh是Linux启动入口jmeter.properties是核心配置文件。lib存放依赖Jar包比如MySQL驱动、JDBC驱动都往这里放。lib/ext存放扩展插件JAR插件管理器也装在这里。下载后建议先双击启动一下图形界面确认能正常弹出主界面然后再往下走。如果启动报错大概率是JDK版本不匹配或者JAVA_HOME配置有问题先解决环境再继续。2.2 第一个Hello World线程组 HTTP请求 察看结果树启动Jmeter后你会看到“测试计划”节点。一个最基础的压测脚本只需要三个组件线程组、HTTP请求取样器、监听器。右键“测试计划” - 添加 - 线程用户 - 线程组。线程组里最核心的参数有三个线程数模拟多少个并发用户。Ramp-Up时间多少秒内启动完这些线程。如果设置线程数100、Ramp-Up时间为10秒就表示10秒内均匀启动100个线程每秒启动10个这样能避免瞬间全部线程同时打过去造成假性冲击。循环次数每个线程执行多少次请求。如果勾选“永远”就会一直压到手动停止。接下来添加HTTP请求。在线程组上右键 - 添加 - 取样器 - HTTP请求。这里最关键是填协议、服务器名称或IP、端口、路径。比如要压测一个用户列表接口填法就是协议填http服务器名称填localhost端口填8080路径填/api/user/list。方法根据接口文档选GET、POST等。然后再加一个监听器右键线程组 - 添加 - 监听器 - 察看结果树。点击上方绿色启动按钮请求跑完后察看结果树里能看到每个请求的响应数据和响应时间。第一次成功看到绿色对勾和响应报文时基础脚本就算通了。这个“最小的闭环”跑通之后后面所有复杂功能都只是在这个基础上加“料”。2.3 界面字体、常用配置等琐事Jmeter默认界面字体在Windows下经常偏小被很多人吐槽过。解决问题的办法很简单修改bin目录下的jmeter.properties文件去掉下面几行的注释并调整参数jmeter.hidpi.modetrue jmeter.hidpi.scale.factor2.0 jsyntaxtextarea.font.size20其中hidpi相关的配置是针对高分屏的缩放jsyntaxtextarea.font.size是针对脚本编辑器字体。改完重启Jmeter就能生效。如果还觉得按钮和菜单字体太小也可以调整操作系统的缩放设置但我实测下来改这两个配置就够用了。另外默认的Jmeter内存配置比较保守图形界面跑大并发脚本时容易卡。可以在bin目录下找到jmeter.bat或jmeter.sh修改HEAP参数比如Windows下搜索“set HEAP-Xms1g -Xmx1g”改成set HEAP-Xms2g -Xmx4g内存分配不要太贪心压测机本身还要留资源给操作系统和被测系统4G堆内存对绝大多数场景足够了。3. 压测脚本编写的核心细节参数化、断言与关联3.1 参数化的几种姿势和适用场景只压一个固定请求参数或者所有用户都用同一份登录态那压出来的结果参考价值很低。真实压测必须让不同并发用户携带不同数据这就是参数化。Jmeter里最常用的参数化方式有三种我按优先级推荐。第一种是用CSV Data Set Config。在线程组上右键 - 添加 - 配置元件 - CSV Data Set Config。配置的时候填文件名、变量名列表、分隔符等。假设有个users.csv内容是user1,123456 user2,123456 user3,123456变量名列表填username,password然后在HTTP请求参数里直接引用${username}和${password}即可。这个文件会被所有线程读取默认是每个线程按顺序取一行数据数据取完后再从头循环。这样就能用同一套脚本模拟多个不同身份用户的操作。第二种是用“用户定义的变量”配置元件适合放全局只读的常量比如服务器IP、端口、路径方便脚本在不同环境间切换。它不适合大量测试数据的场景。第三种是直接用函数。比如${__Random(1,1000)}可以生成1到1000的随机数${__time(yyyy-MM-dd,)}可以生成当前日期在处理临时数据时特别灵活不需要额外维护文件。实际项目里我习惯把稳定的大数据集放CSV把随机变化的字段用函数生成把环境相关常量用用户定义变量管理三者配合基本覆盖所有场景。如果你需要的是数据库里的数据那就不是用CSV了而是走JDBC Request查询后面第五章我会单独讲。3.2 断言响应断言、Beanshell断言与脚本化校验没有断言的压测其实就是“发起了一堆请求但不知道成没成功”。请求返回200不代表业务成功也许返回体里塞了个errorCode。所以我每次写压测脚本都会加校验逻辑。最简单的校验方式是响应断言。在线程组上右键 - 添加 - 断言 - 响应断言。把“要测试的响应字段”选为“响应文本”然后在“模式匹配”里写上期望包含的字符串比如登录成功后返回的“token”。请求响应里只要包含这个字符串本次请求就被判定为成功。但有时候响应体里的内容是动态的比如订单号、随机错误码用静态文本断言就不够用。这时我会用Beanshell断言。右键 - 添加 - 断言 - BeanShell断言。在写脚本框里直接写String response prev.getResponseDataAsString(); if (response.contains(\code\:200)) { Failure false; } else { Failure true; FailureMessage 响应中未找到code200实际响应 response; }prev是Jmeter预置变量代表当前取样器的结果对象。通过这段脚本我能根据业务规则做更细粒度的判断比如提取某个字段做数学或逻辑比较。需要强调一下新版Jmeter里官方更推荐用JSR223 Groovy替代Beanshell因为Beanshell脚本引擎每次都编译比较耗性能。高并发压测时尽量用JSR223取样器里的Groovy脚本执行效率高很多。3.3 关联JSON提取器与正则提取器接口之间经常有依赖比如登录接口返回一个token后续所有业务接口请求头都要带这个token。这种“上一个接口的响应里取值传给下一个接口”的操作叫关联。如果响应是JSON格式优先用JSON提取器。右键HTTP请求 - 添加 - 后置处理器 - JSON提取器。比如登录接口返回{ data: { token: abc123456 }, code: 200 }JSONPath表达式就写$.data.token变量名填token后续请求里直接用${token}引用。如果响应是HTML或文本格式就用正则表达式提取器。比如HTML里有一个隐藏字段input typehidden name__RequestVerificationToken valuexxxxx /正则表达式可以写name__RequestVerificationToken value([^])模板填$1$表示取第一个分组的值。提取出来的值同样用变量名引用。这里有一个我在实际项目中踩过的坑JSON提取器默认只匹配第一个结果如果响应里有多个相同结构的对象要留意Match Numbers参数。需要取第几个结果就写几如果写0表示随机取一个千万别在只需要单个值时忘了这个细节否则后续数据全乱套。3.4 文件上传与RESTful参数写法用Jmeter模拟文件上传很多人会下意识想用POST Body Data塞文件内容其实完全不用这么痛苦。HTTP请求里切换到“Files Upload”选项卡把“文件路径”填成实际文件路径“参数名称”填接口约定好的字段名比如uploadFile“MIME类型”按文件类型填比如image/jpeg或application/octet-stream。如果是多文件上传就点“添加”再增加一条记录。上传图片前记得先在察看结果树里确认响应内容很多上传接口对文件大小有限制Jmeter直接把文件发过去不会像浏览器那样先做压缩容易暴露出接口的真实限制这其实也是压测的价值之一。RESTful接口的参数写法要看具体场景。如果接口是查询类参数通常放在URL上直接在HTTP请求的路径里拼查询串比如/api/users?page1size20。如果接口是POST操作参数放在消息体里常用JSON格式。在HTTP请求的“Body Data”标签页里直接写{ name: ${username}, age: 18, hobbies: [coding, reading] }默认情况下Jmeter会以application/json格式发送这个Body。有些服务端校验严格还要在HTTP请求里手动添加一个HTTP信息头管理器设置Content-Type为application/json。如果Content-Type不对Spring MVC的接口会直接报415错误。4. HTTPS脚本录制与证书问题4.1 代理录制的基本步骤有些老项目接口文档不全尤其是内部系统看代码又不现实。这时候最快的办法是用Jmeter自带的HTTP代理服务器录制脚本把浏览器操作过程变成Jmeter脚本。右键“测试计划” - 添加 - 非测试元件 - HTTP代理服务器。关键配置全局设置里的端口号默认8080如果被占用就换一个比如8888。目标控制器选择“测试计划 线程组”这样录制的请求会直接进到线程组下。分组设置为“每个组放入一个新控制器”方便按事务分组。点击“启动”后Jmeter会提示需要设置浏览器代理。我一般手动把浏览器或系统代理指向本机IP加端口比如127.0.0.1:8888。保持这个代理状态然后在浏览器里操作被测系统Jmeter就会把操作产生的HTTP请求实时记录下来。录制完成后先停掉代理服务器再打开线程组查看生成的取样器。这里要注意一件事录制的脚本通常会带很多静态资源请求比如JS、CSS、图片这些与业务无关的资源建议直接删掉或者用正则过滤器排除不然压测时会产生大量无效流量干扰真实接口的TPS统计。4.2 证书装不上怎么办HTTP协议录制很顺利但换成HTTPS就报证书错误这是最典型的入门问题。原因是Jmeter代理服务器用自己的根证书做SSL中间人解密浏览器如果不信任这个根证书HTTPS请求就会校验失败。解决办法是安装Jmeter的CA证书。在代理服务器启动时Jmeter会在bin目录下生成一个名为ApacheJMeterTemporaryRootCA.crt的证书文件。在Windows上直接双击这个文件选择“安装证书”把证书放到“受信任的根证书颁发机构”存储区。装完之后重启浏览器和JmeterHTTPS录制就正常了。如果你在Linux服务器上做命令行压测证书问题又不一样。这种情况下用默认的HTTP协议就好如果被测接口强制HTTPS需要让Jmeter信任服务端的SSL证书有两个办法用系统keytool工具把服务端证书导入Jmeter使用的JDK的cacerts证书库在jmeter.properties中设置相关SSL配置让Jmeter接受所有证书但这只建议在测试环境使用。这里特别提醒千万不要在浏览器里看到证书错误提示后直接点“继续访问”就完事了。那样虽然也能录到HTTPS请求但Jmeter代理在后续重放脚本时服务端如果不信任发起方的SSL握手压测依然会报错。老老实实把根证书装进受信任区域会省掉后面一堆麻烦。4.3 录制后脚本的清洗与优化录制脚本只是第一步录出来的东西基本不能直接拿去压测必须要清洗。我的清洗流程一般是这样第一删除所有静态资源请求包括CSS、JS、PNG、ICO这些。第二合并同一事务下的分散请求比如一次页面跳转可能有多个Ajax请求除非压测目标就是这个页面否则建议只保留核心业务接口。第三把录制时写死的参数值改成变量。录制时录到的是一个固定用户名比如admin123压测时就得改成${username}否则所有并发用户都用同一账号没法真正模拟并发场景。第四加上必要的断言和关联。录制时Jmeter记录了响应数据但这些数据在压测时会动态变化必须手动加JSON提取器提取token加响应断言校验业务状态这样脚本才算真正可用。录制功能适合快速摸清接口调用链但真正要交付一份高质量压测脚本还是得手工打磨。我见过不少同学拿录制结果直接跑结果压出来的数据全是静态资源请求的指标这个“坑”尤其要规避。5. JDBC与数据库参数化实战在很多压测场景里光靠CSV文件带数据根本不够测试数据本身必须从数据库里实时查出来尤其是账号状态、库存数量这些业务强相关的字段。这种情况就需要用JDBC Request。5.1 数据库驱动与连接配置用Jmeter压数据库或做数据库参数化的前置条件是把对应的数据库驱动Jar包放进Jmeter的lib目录。以MySQL为例需要下载mysql-connector-java的Jar包放到lib目录后重启Jmeter。接着添加“JDBC Connection Configuration”配置元件这里要填四样关键信息Variable Name连接池名字比如mysql_pool。这个名称决定了后续JDBC Request引用哪个连接池建议全局唯一。Database URL连接字符串比如jdbc:mysql://localhost:3306/test?useSSLfalse。JDBC Driver Class选com.mysql.jdbc.Driver新版驱动也可以选com.mysql.cj.jdbc.Driver。Username和Password数据库账号密码。如果密码里有特殊字符要注意URL或参数里是否要做转义。添加完成后再添加JDBC Request取样器查询类型选“Select Statement”查询语句填SQL比如SELECT id, username, password FROM tb_user WHERE status 1 LIMIT 100这样就相当于Jmeter直接对数据库发起了一次查询查询结果可以被后续逻辑复用。5.2 JDBC Request参数化取值与结果复用JDBC Request不是只在压测数据库时才用更多时候是拿它来“取数”。比如登录压测需要100个已注册用户这些用户不是写在CSV里的而是从数据库里查出来的。查询完成后要把结果存成变量继续给下一个接口用。在JDBC Request配置里有一个“Variable Names”参数这里设置的变量名会和查询结果的列名一一对应。假设我查询的SQL是SELECT username, password FROM tb_user LIMIT 10然后在Variable Names里填username,password结果集里的第一条记录就会被赋值给变量username_1、password_1第二条是username_2、password_2以此类推。如果我只需要第一行后续请求直接引用${username_1}和${password_1}如果想循环取每一行就需要配合循环控制器或者用${username_${__counter(FALSE,)}}这样动态拼接变量名。可能有人会问查询出来的数据能不能随机取一条可以的。用一个JSR223取样器或Beanshell后置处理器把结果集转成List再用随机函数取下标这样每个线程拿到的数据都不重复更贴近真实用户场景。不过脚本复杂度会上升非必要先用第一行就够了。5.3 同一个CSV文件里每个线程分块取值的实现还有一个很典型的场景特别容易被搜索引擎上到“jmeter在同一个csv参数化文件中每个线程分块取值”。比如我有一个10000行的CSV希望线程1只取第1-100行线程2只取第101-200行每个线程独占一个“数据块”互不干扰。这里最直接的办法是拆文件。用脚本把大CSV按行数切成N份每个线程组各引用自己的子文件。这个方案简单可控缺点是文件数量会比较多。另一个方案是在JSR223预处理脚本里用Groovy读取CSV按当前线程号和迭代序号计算行号手动设置变量。核心思路类似这样def threadNum ctx.getThreadNum() def iteration ctx.getIteration() // 当前迭代次数 def lineNumber threadNum * 100 iteration // 按线程和迭代定位行 def lines new File(/path/to/data.csv).readLines() def data lines[lineNumber - 1].split(,) vars.put(username, data[0]) vars.put(password, data[1])这个做法的优点是可以灵活控制读取逻辑但每轮迭代都重新读整个文件文件很大时效率不高。这属于“能解但不要贪”的方案真实项目里如果数据量大、分块需求固定我还是首选拆文件运维简单也方便单独替换某个线程组的数据源。其实Jmeter的CSV Data Set Config里有一个“SharingMode”参数可以选择All threads、Current thread group、Current thread。选择Current thread时每个虚拟用户会有自己独立的CSV游标但它不是按“块”分配而是每个线程顺序取一行循环自身。如果你要求的“分块”只是每个线程不重复数据这个模式就够了如果严格要求线程1取1-100行、线程2取101-200行还是用拆文件或Groovy脚本方案。6. 插件管理与MQTT压测扩展6.1 用Plugin Manager装插件Jmeter自带功能虽然不少但遇到MQTT、WebSocket、Redis这些协议时默认组件就力不从心了。好在Jmeter有一个社区维护的插件管理器能像手机应用商店一样一键安装扩展。安装插件管理器的步骤非常简单去官网下载plugins-manager.jar放进Jmeter的lib/ext目录重启Jmeter在菜单栏的“选项”里就能看到“Plugins Manager”入口。打开后看到Available Plugins列表勾选需要的插件点右下角“Apply Changes and Restart Jmeter”就可以自动下载并安装。这里有一个注意点插件管理器需要联网下载。如果压测环境是内网隔离的得提前在有网环境下把插件装号再把整个Jmeter目录拷进去或者手动下载JAR包放进lib/ext。别到了现场才发现插件装不上那就尴尬了。6.2 MQTT插件能解决什么场景随着物联网项目越来越多MQTT协议的性能测试需求也在增加。Jmeter官方没有内置MQTT取样器但通过插件管理器可以安装“MQTT Protocol Support”插件装完后在线程组里添加取样器时就能看到MQTT Connect、MQTT Pub Sampler、MQTT Sub Sampler。MQTT Connect用于建立客户端与Broker之间的连接MQTT Pub Sampler用于发布消息MQTT Sub Sampler用于订阅Topic。做物联网压测时我最常用的是先建立一批连接然后以固定频率往某个Topic发消息同时订阅另一个Topic压测消息中转和Broker的吞吐能力。这个插件用起来比很多商业工具还顺手能设置QoS级别、Retain标志、KeepAlive时间基本覆盖了真实业务场景的核心参数。配置完成后聚合报告里同样能看到发布/订阅消息的TPS、平均延迟和错误率对验证物联网平台的承载能力很有说服力。如果你在做类似“设备并发上报”的需求强烈建议试试这个插件。6.3 常用插件清单除了MQTT插件我再推荐几个装了不亏的常用插件按使用频率排个序JSON Path Extractor官方其实已经内置了JSON提取器但插件版提供更多JSONPath函数支持老版本Jmeter用户推荐安装。Custom Thread Groups提供步进线程组Stepping Thread Group、自由格式线程组能模拟“先加10用户跑到稳定再加10用户”这种渐进式压测比默认线程组灵活很多。PerfMon Metrics Collector配合ServerAgent服务端监控能看到压测期间被测服务器的CPU、内存、网络、磁盘I/O是定位系统瓶颈的神器。WebSocket Samplers做WebSocket长连接压测时使用我们项目里有个在线聊天系统就是用这个插件做的连接数测试。插件也不是装得越多越好。插件本质是一堆额外JAR包装太多会拖慢Jmeter启动速度和采样效率容易把压测机自身的性能问题误判成被测系统的问题。我一般只装项目确实需要的插件跑完压测后如果长期不用还会考虑卸载。7. 结果分析从聚合报告到100用户并发报告压测脚本跑完之后重头戏才刚开始——读报告。很多新人看着聚合报告密密麻麻的表格一脸懵其实核心指标就那么几个掌握了就能把系统的性能状况说清楚。7.1 聚合报告关键指标怎么读在线程组上右键 - 添加 - 监听器 - 聚合报告跑完后能看到一张包含大量列的表。我最常用的是这几列Samples总请求数等于线程数乘以循环次数。比如100用户循环100次就是10000个请求。Average所有请求的平均响应时间单位毫秒。这个值越高说明系统响应越慢。Error%错误率。压测合格线一般要求0%金融类项目甚至要求压测过程中不允许有业务失败。Throughput吞吐量如果以“/sec”为单位就是每秒能处理的请求数也就是我们常说的TPS。90% Line90%的请求在多少毫秒内完成。这个指标比平均响应时间更能反映真实体验因为平均值容易被极端值拉偏。读报告时的正确姿势是“先看错误率再看TPS最后看延迟”。不管TPS多高只要有业务错误压测结果就得打折扣。错误率没问题时TPS和平均响应时间就成衡量系统好坏的核心指标。举个例子100用户循环100次总共10000个请求如果聚合报告显示Error%0Throughput2000/secAverage45ms那说明系统在2000 QPS左右的并发压力下依然稳定。7.2 模拟100用户并发实测案例我之前做过一次典型的登录接口并发验证配置是这样的线程数100Ramp-Up时间10秒循环次数30次总共3000个请求。压测目标接口是某内部系统的登录接口服务端使用的是Spring Boot MySQL的经典组合。压测过程中聚合报告是动态变化的。启动后前三秒是线程逐渐启动的阶段TPS会从0逐步爬升等到全部100个线程跑起来后TPS稳定在1500/sec左右平均响应时间从启动初期的210ms逐渐下降到90ms左右这是因为服务端连接池和线程池热起来了。最终报告如下指标数值Samples3000平均响应时间92ms90% Line145ms最小/最大35ms / 800ms错误率0%Throughput1502/sec拿到这个结果我要做的第一件事不是看数字漂不漂亮而是问自己这个结果符合预期吗如果之前做过压力评估预期支持2000 QPS那么当前TPS只有1500说明还有优化空间如果业务预估峰值只有1000 QPS那这个结果已经足够了没必要为了指标好看盲目加并发。7.3 报告导出和保存聚合报告里的数据关掉窗口就没了所以压测结束后一定要及时导出。最稳妥的方式是在压测执行前就在聚合报告里配置好输出文件这样压测过程中数据会实时落盘避免中途崩溃导致结果丢失。配置方法是在聚合报告界面的“文件名”输入框里填输出路径比如/result/100user_aggregate.csv。同时也可以勾选“保存表格数据”这样就会同时保存CSV格式。CSV文件可以直接用Excel打开也可以再导入Jmeter的聚合报告中回看。察看结果树同样支持导出。右键 - 选择“全部保存”或“保存为”可以把本次请求的完整响应报文导出成文件。这个功能在排查问题时特别有用可以快速定位到底是哪个请求的响应内容不符合预期。但要注意结果树会保存大量响应数据压测结束后如果不及时清理磁盘上的输出文件几百上千MB的数据都算平常所以建议只在调试阶段全量导出正式压测时关闭察看结果树的保存功能。如果想生成HTML可视化报告推荐用命令行模式跑压测并生成报告这是我在压测任务中推荐的做法。命令行模式生成的报告会在output目录下生成完整的HTML页面包括TPS曲线、响应时间散点图、错误分布等给自己看或给项目组汇报都够用了。8. 压测过程中的高频问题排查清单Jmeter虽然好用但跑压测时各种报错也是层出不穷。我把自己踩过和帮别人排查过的高频问题整理成一张“避坑清单”大概覆盖了搜索引擎上大多数搜索需求的场景。8.1 java.io.IOException: error writing to server这个报错在压测日志里很常见看到它先别慌它通常不是Jmeter本身出问题而是请求发出后服务端在响应过程中主动断开了连接。可能原因有三种第一服务端处理不过来触发超时或主动关闭连接。这种情况要去查服务端的线程池、连接池和超时设置压测时服务端日志里一般会有对应的Connection reset相关记录。第二请求本身有问题比如发送的数据格式不对导致服务端解析失败服务端拒收后断开连接。第三Jmeter到服务端之间有网络设备或防火墙做连接限制长连接被切断就会报这个错。排查思路也比较固定先用单个线程循环跑一遍看还报不报错如果单线程不报错那就是并发场景下的连接数或服务端处理能力问题如果单线程也报错重点抓包看服务端返回内容。另外可以调整HTTP请求里的超时时间在HTTP请求的“超时”设置里配置连接超时和响应超时时间这样至少能确认是连接阶段失败还是响应阶段失败。8.2 __RequestVerificationToken 未提供必要的防伪标记如果你压测的是基于ASP.NET MVC的老系统POST请求时很容易收到这个错误。这是服务端为防止CSRF攻击加的一道安全机制Jmeter发起的POST请求没有携带服务端生成的防伪标记所以被拒绝。我当年处理这个问题的步骤是先用HTTP请求访问页面拿到页面HTML里隐藏的__RequestVerificationToken字段的值然后用正则表达式提取器或XPath提取器把值取出来最后把这个值放进POST请求的请求体或请求头里再发送。这样就模拟了浏览器“先访问页面、再提交表单”的完整行为。需要注意这个token通常是一次性的或者说在短时间内有效。压测时如果每个用户都从同一个初始页面拿token可能因为token复用导致校验失败。更好的做法是让每个线程在执行提交操作前先请求一次页面提取出属于自己会话的token再执行业务提交。简单说就是“先访问再提交”不要省略访问页面那一步。8.3 证书、文件已存在、乱码等小坑另外几个小问题也值得一说。录制HTTPS脚本报证书错误通常就是Jmeter根证书没被浏览器信任处理方案我在第4.2节讲过这里不重复。保存结果文件时报“文件已经存在”是因为Jmeter默认不允许直接覆盖已有文件。解决方式有两种一是把文件名路径改成一个新文件二是配置允许覆盖在jmeter.properties里找结果文件相关配置设置成自动覆盖。但我更推荐用带时间戳的文件名比如result_20250101_1200.csv这样历史结果不会丢方便对比不同时段的压测数据。中文乱码问题多数出在响应数据处理上。察看结果树里中文显示乱码可以在jmeter.properties中设置默认编码为UTF-8比如sampleresult.default.encodingUTF-8。如果响应本身是GBK编码就用后置处理器或BeanShell脚本转码。实战中绝大多数接口已经是UTF-8遇到乱码先确认接口编码再改Jmeter配置。9. 把压测放到真实项目里一个云上迁移场景的经验9.1 场景背景与压测目标上一段工作经历里我参与过一个“单节点K8s上的微服务整套环境迁移到云上ECS”的项目。整套环境是基于若依微服务框架搭建的有网关、认证服务、业务服务、数据库等多个组件。迁移完成后团队需要验证云上环境能否承载原先运行环境下的高并发访问量于是压测人员我们内部叫他peseman拿出配套的Jmeter脚本开始做迁移后的容量验证。这类迁移项目的压测目标非常明确不需要测出系统极限性能有多高而是要用同一套脚本、同一套并发模型在迁移前后的环境上各跑一遍对比TPS、响应时间、错误率有没有明显劣化。本质上是一种回归式的压测衡量的是迁移是否造成了性能回退。9.2 压测脚本如何配合项目里使用的Jmeter脚本分成几个测试片段分别覆盖登录、业务查询、核心流程写入等场景。其中比较有代表性的就是登录接口以及其他依赖Token的业务接口整套脚本通过第3.3节讲的关联方式把登录返回的Token传递下去每个并发用户保持自己的会话状态。为了不让测试数据互相干扰脚本里还用到了第5章提到的JDBC Request从数据库读取测试账号。压测前peseman会先跑一遍“数据准备”脚本把对应数量的测试用户写入数据库然后再跑正式压测脚本保证每个线程用的是独立账号不会出现多个线程同时操作同一账户导致的业务冲突。这类脚本的价值在于可重复执行。迁移前跑一遍拿到基线数据迁移后再跑一遍做对比。如果TPS从2000降到1200那不管页面打开多流畅底层性能肯定是退化的得继续排查云上ECS的规格配置、数据库连接池、K8s服务网格等环节。9.3 压测之后我给的新手建议给准备把Jmeter当成正式压测工具的朋友一句真心话Jmeter只是工具压测的核心是你要清楚自己的测试目标和业务模型。脚本编写只是手段更重要的是要有“先看错误率、再看TPS、最后看响应时间”的分析逻辑还要有“控制变量”的意识。每次跑压测前先记录被压系统的基础状态比如服务器的CPU、内存、数据库连接数。压测过程中用PerfMon这类监控插件同步采集服务端指标。压测结束后把聚合报告、系统监控截图、问题日志整理归档。这样你会发现压测报告不是做出来应付验收的而是真的能指导容量评估和系统调优的。最后分享一个小技巧压测脚本一定要做成可配置的。线程数、循环次数、服务器地址、参数化文件路径这些信息尽量用Jmeter属性或用户变量抽离出来不要硬编码在取样器里。这样等环境切换或者需要调整并发量时只需要改一个配置就能重新跑不用在脚本里翻来翻去找参数。这个习惯帮我节省了大量重复时间希望你也能养成。