JMeter压测实战:从脚本设计到性能瓶颈定位

发布时间:2026/9/20 14:57:36
JMeter压测实战:从脚本设计到性能瓶颈定位 1. 压测前必须想清楚的几件事做JMeter压测最大的坑不是工具不会用而是不知道为什么压、压什么、压多久。我见过太多团队拿到JMeter就咔咔加线程数结果压出来的报告除了给自己壮胆没有任何参考价值。如果手里正好有一个“迁移到云上ECS后需要验证承载能力”的项目那思路更得捋清楚。压测这件事本质上是回答三个问题系统在什么负载下开始变慢系统在什么负载下彻底不可用系统的瓶颈到底在哪个环节所以开跑之前先别急着打开JMeter先想清楚以下四件事。第一业务模型。你压的不是“登录接口”或“查询接口”你压的是用户真实操作路径。比如一个电商系统浏览商品、加购物车、下单、支付这几个动作的比例大约是6:3:1那脚本里的请求占比就应该按这个来而不是平均分配。没有业务模型的压测脚本压出来的数字没有任何意义。第二性能指标。你能接受的响应时间是多少错误率阈值是多少CPU、内存、磁盘IO、网络带宽哪个指标优先看这些都必须在压测开始前和业务方、开发方对齐否则压测过程中大家会因为“到底算不算达标”吵起来。第三施压策略。是瞬时压满峰值还是逐步加压寻找拐点是用恒定QPS还是有节奏地波峰波谷不同的施压方式对应的场景完全不同。瞬时压满适合验证容灾能力而逐步加压适合定位性能拐点也就是所谓的“容量探测”。第四数据准备。压测数据不能全用同一份。账号要大量、并发、独立商品库存要够数据库里的数据量要接近生产环境。很多压测得出错误结论就是因为用了几条测试数据去模拟几十万用户。这些问题想明白了再打开JMeter你会发现每一步操作都有明确的目的而不是为了跑一个“看起来很大”的并发数。2. 环境准备与脚本基础2.1 安装与版本避坑JMeter安装本身很简单但版本坑不少。首先JMeter是一个纯Java应用所以第一件事是确认JDK版本。JMeter 5.4以前可以用JDK 8JMeter 5.5开始最低要求JDK 8JMeter 5.6建议JDK 11或17。如果你用的是最新版JMeter比如5.6.3再配一个JDK 7那是肯定起不来的。官网下载是首选路径记住不要从乱七八糟的下载站拿安装包。下载到的压缩包解压后进入bin目录Windows用户双击jmeter.batMac或Linux用户执行jmeter.sh。注意执行命令时如果提示“Permission denied”记得先chmod x。这里有第一个实操经验生产或测试机上启动JMeter一定要用命令行模式jmeter -n -t xxx.jmx -l result.jtl不要开GUI。GUI模式下JMeter本身就要吃掉不少内存压在JMeter自己的头上测出来的数据根本不准。而且GUI模式跑得越久内存碎片越多数据偏差越大。bin目录下有两个文件建议提前改jmeter.bat或jmeter.sh里的HEAP参数还有jmeter.properties里的配置项。默认堆内存是-Xms1g -Xmx1g -XX:MaxMetaspaceSize256m如果压测脚本比较大或者聚合报告开得比较多建议改成-Xms2g -Xmx4g。别觉得内存给多了浪费JMeter是Java进程内存不够时频繁GC直接导致线程停顿压测结果会失真。2.2 线程组设计是压测的灵魂进入JMeter后第一个要认识的就是线程组Thread Group。简单理解线程组就是“模拟用户池”里面每一个线程代表一个模拟用户。但线程数不等于并发用户数这是一个需要专门说清楚的概念。线程数只是你开了多少个线程真正意义上的并发是“同一时刻发出去的请求数量”。如果脚本里带了思考时间Think Time比如用户看完页面再点查询那么100个线程实际同时打在服务器上的请求可能只有30个。所以做容量评估时要区分开了多少线程和实际RPS每秒请求数是多少。线程组的三个核心参数分别是线程数、Ramp-Up Period和循环次数。Ramp-Up Period的意思是“在多少秒内启动所有线程”。假设线程数是100Ramp-Up是10秒那么每秒启动10个线程首次并发峰出现在第10秒。很多新手喜欢把Ramp-Up设为0这意味着所有线程瞬间全部启动服务器会收到一个非常陡峭的流量冲击。这在某些故障注入场景下是有意的但常规压测不建议这么干真实用户不可能在同一毫秒内全部涌进来。循环次数控制的是每个线程跑多少轮脚本。做容量测试时建议勾选“永远”然后用调度器Scheduler来控制压测时长例如持续运行15分钟、30分钟。这比设置一个巨大的循环次数更科学因为你可以精确控制“持续施压的时间窗口”。2.3 HTTP请求配置的三个关键细节线程组下面就是Sampler采样器最常用的就是HTTP Request采样器。这里有几个必须养成的习惯。第一个是协议、域名、端口分开填不要直接拼成一个URL。这样做的好处是便于用变量替换。比如说要切换压测环境从测试环境切到预发环境只改一个变量就行而不是在几十个请求里挨个找。我见过有人把整个URL都写在“路径”里切换环境时改到怀疑人生。第二个是路径不要写死有查询参数的优先写在“Parameters”里不要拼在路径后头。因为JMeter里用CSV参数化时写在Parameters里的东西可以配合变量直接替换而拼在URL里的字符串处理起来各种别扭。第三个是随请求发送的Headers一定要加齐。比如Content-Type、Accept、Authorization这些少了任何一个接口都可能返回错误而新手往往以为是JMeter配置错了其实是被接口的鉴权或内容协商拦住了。3. 参数化与断言脚本能跑和脚本会说话是两回事3.1 数据库参数化取值很多接口的入参不是固定的尤其是业务主键。JMeter里有好几种参数化方式比如CSV文件、函数助手、数据库查询。这里我只重点讲数据库参数化因为它在真实项目里最实用也最容易出错。场景是这样的接口要求传入一个数据库中真实存在的数据ID而这个ID需要从数据库查出来、去重、再放到请求里。这时候就用到JDBC Request采样器和JDBC Connection Configuration组件。先在测试计划里配置JDBC Connection ConfigurationDataSource数据库类型选mysqlJDBC Driver Class选com.mysql.jdbc.DriverMySQL 8用com.mysql.cj.jdbc.Driver然后在“Database URL”里填jdbc:mysql://IP:3306/dbname?useUnicodetruecharacterEncodingutf8useSSLfalse用户名密码按实际填。这里有个关键点JDBC Connection Configuration里需要设置“Pool Max”公共的超时参数。多数人忽略这个导致数据库连接池满了之后JMeter等不到连接直接报“Cannot create PoolableConnectionFactory”。建议把Max Number of Connections设为和线程数一致或略高超时时间设180秒避免并发一上来就连接池崩溃。JDBC Request里写查询SQL比如SELECT id FROM orders WHERE status1 ORDER BY RAND() LIMIT 1。然后在下一处需要ID的HTTP请求中通过${orderId}引用查询结果。这里要注意的是JDBC Request的结果变量名Variable Names要填一个名字例如orderId然后在HTTP请求参数里写${orderId}即可。还有一个容易被忽略的点JDBC Request执行结果返回的是ResultSet对象如果你只取一行一列没问题。但如果要取多行数据作为后续请求的参数就要配合计数器Counter或ForEach控制器来循环取值。实际项目中我通常的做法是查出一批ID比如50个用计数器变量索引遍历这样能模拟出真实用户“每个人操作不同的订单”的效果。3.2 BeanShell断言不只校验HTTP状态码JMeter自带的响应断言Response Assertion只能做简单文本匹配比如判断响应里是否包含某个字符串。但真实的接口校验逻辑要复杂得多状态码200不代表业务成功可能是“返回码200、业务码500”的假成功响应JSON里的某个字段可能是动态的不能全文匹配有时需要校验多个字段组合逻辑。这时候就要用到BeanShell断言。在HTTP请求上右键添加“断言 - BeanShell断言”在Script区域写脚本逻辑。以下是我常用的一套模板String response prev.getResponseDataAsString(); // 解析JSON String code vars.get(code); // 如果用JSON提取器先提取了 if (response.contains(\success\:true) response.contains(\code\:200)) { Failure false; } else { Failure true; FailureMessage 业务校验失败响应内容: response; }但BeanShell有个性能隐患它走的是解释执行压测线程多的时候会拖累JMeter自身效率。所以我更推荐用JSR223断言 Groovy脚本执行效率远高于BeanShell。JSR223是编译执行的BeanShell是解释执行的同样一个断言脚本Groovy比BeanShell快好几倍。高并发压测时这会直接影响施压端的最大发压能力。JSR223断言脚本里最常用的就是解析JSON响应。配合groovy.jar内置的JsonSlurper可以非常优雅地处理import groovy.json.JsonSlurper def response prev.getResponseDataAsString() def json new JsonSlurper().parseText(response) if (json.code ! 200 || json.data.count 1) { assert false : 业务异常: ${response} }这里分享一个经验断言不是越多越好。每个断言都消耗JMeter的资源而且断言逻辑写得复杂了排错也更困难。我的原则是“核心字段必有断言次要字段不做校验”。拿支付接口为例只需要校验返回码和支付状态字段其它时间戳、签名这种动态字段不应该出现在断言里。3.3 动态调整QPS的两种方案有些压测场景要求QPS不是固定不变的而是按预设曲线变化。比如先跑100QPS过10分钟升到200QPS再观察系统表现。JMeter中实现动态QPS调整有两种典型方案。第一种是使用Constant Throughput Timer常量吞吐量定时器。这个定时器可以设定每分钟最大请求数按分钟设置所以计算时要把想要的QPS乘以60。它的工作方式是控制线程组整体发请求的节奏不追求精确到毫秒。缺点是精度有限而且如果线程数设少了吞吐量会达不到设定值线程数设多了吞吐量又会超出预期。第二种是用线程组配合Throughput Shaping Timer插件。这个插件允许你定义“阶段”比如第一个阶段0-10分钟、QPS 100第二个阶段10-20分钟、QPS 200。设置好之后JMeter会自动调配线程去跟上这个节奏。这个方案比较精确适合做阶梯加压测试。安装方式是在JMeter里通过Plugins Manager插件管理器安装Custom Thread Groups组件包。有一点要提前说明想用好Throughput Shaping TimerJMeter本身要安装插件管理器。插件管理器放在lib/ext目录下重启JMeter就能在“选项”菜单里看到。安装插件时选择Custom Thread Groups即可不需要额外配置。3.4 Cookie处理与文件上传HTTP是“无状态”协议但很多业务系统需要登录态才能继续操作。JMeter里处理Cookie有两个思路一是用HTTP Cookie管理器来自动管理Cookie它会把登录接口返回的Set-Cookie自动捕获并带上二是手动用正则提取或JSON提取器抓取登录返回的token然后通过HTTP Header Manager放到后续请求的Header里。Cookie管理器适合基于Session的认证系统Token方式适合基于JWT的微服务体系。如果是微服务架构建议直接用JSON提取器抓token。写法如下$.data.token在登录请求下添加JSON提取器Variable填写tokenVariableJSON Path表达式填上面的内容Match No填1。然后在后续请求的Header Manager里添加Authorization字段值写作Bearer ${tokenVariable}。文件上传也是高频场景尤其是涉及附件、头像、图片的业务。JMeter中在HTTP请求里把请求方式改成POST然后勾选“Use multipart/form-data”在“Files Upload”区域填写文件路径和参数名。这里的坑在于文件路径尽量用绝对路径相对路径在命令行模式运行时容易找不到文件另外参数名必须和接口文档一致否则服务端永远收不到文件。还有一种更常见的坑是文件上传时还要带业务参数比如备注、类型不能只传文件。4. 脚本录制与复杂协议扩展4.1 HTTPS脚本录制的正确姿势刚开始做JMeter压测的新手最容易卡住的是不会写脚本。一个一个接口手写虽然可行但业务复杂时效率太低。这时可以利用JMeter的HTTP(S) Test Script Recorder录制脚本。原理很简单JMeter启动一个本地代理服务器把你的浏览器或系统HTTP请求转发到目标服务器同时这个代理服务器会把请求“记录”下来生成对应的Sampler。你只需要在真实浏览器里操作一遍业务流程脚本就自动生成了。实际上手时会遇到HTTPS证书问题。因为JMeter代理本质上拦截了加密流量浏览器会提示证书不受信任。解决办法是先把JMeter的CA证书装到浏览器里。在JMeter的bin目录下有个ApacheJMeterTemporaryRootCA.crt安装到系统“受信任的根证书颁发机构”即可。录制前还需要设置浏览器代理。把HTTP代理和HTTPS代理都指向127.0.0.1端口填在JMeter Recording Controller里配置的端口默认8080。然后开始录制操作完业务后点停止。特别提醒录制的脚本里带了大量静态资源请求图片、CSS、JS这些请求在压测时通常要排除掉。真实的用户访问会产生这些请求但压力测试的重点是业务接口静态资源应该由CDN或前端缓存来承担。我通常会在录制时启用“记录HTTP请求”下面的排除模式把这些静态资源URL过滤掉。4.2 MQTT插件安装与物联网场景压测现在很多系统不是纯互联网项目而是物联网后台消息通信走的是MQTT协议。JMeter原生不支持MQTT需要安装第三方插件。安装方法依然是通过Plugins Manager搜索“MQTT”安装mqtt-jmeter插件。安装后在“添加 - 取样器”里会多出MQTT Connect、MQTT Pub Sampler、MQTT Sub Sampler三个组件。MQTT压测和HTTP压测最大的不同在于通信模式HTTP是一问一答MQTT是发布/订阅。脚本设计时要考虑“订阅端”的比例比如1个订阅者配多少个发布者消息主题怎么分配QoS等级用多少这些都是影响服务端压力的关键因素。我碰到过一个物联网项目的压测需求要模拟十万设备并发上报数据。直接开10万线程是不现实的JMeter单机撑不住。正确做法是分布式压测多台施压机配合。但MQTT插件的分布式支持不够好更多时候是把设备数据建模后每条消息封装成一个发布请求用几百线程持续高吞吐地发布消息来模拟而不是真的开10万线程。4.3 RESTful接口参数写法RESTful接口的参数有几种情况路径参数、查询参数、请求体。JMeter里写路径参数就是在HTTP请求的“路径”里直接把变量拼进去。比如接口是GET /user/{id}那么路径填 /user/${userId}在路径上右键添加“用户参数”或使用CSV文件配置好userId即可。查询参数写在Parameters表里和路径用问号分隔后的内容对应。请求体如果是JSON格式就在Body Data里写JSON字符串同样可以用变量替换值。唯一要注意的是Content-Type要设置为application/json否则服务端可能解析不了。5. 报告输出与数据解读5.1 HTML报告汉化模板JMeter压测结束后官方推荐用命令行生成HTML报告jmeter -n -t test_plan.jmx -l result.jtl -e -o report_dir这个命令会生成一个统计聚合视图的HTML文件包含响应时间分布、吞吐量、错误率、活跃线程数等图表。但默认模板是英文的很多测试同学看着费劲尤其是要拿去跟领导汇报的时候。汉化模板的做法是准备一套中文的reportgenerator.properties和CSS资源。这里我不建议大家下载来路不明的汉化包更靠谱的做法是自己翻译模板里的configuration/目录下的properties文件。里面包含面板标题、图表名称、时间单位等文本翻译后放到模板目录覆盖原文件即可。我有一个习惯是拿到HTML报告后第一时间看两部分响应时间百分位尤其是TP90、TP99和吞吐量趋势图。TP90和TP99能反映整体用户感受的“长尾”而不是平均值掩盖掉的那部分慢请求。如果TP99高于业务要求的300ms即便平均响应时间才80ms这个系统也是不达标的。5.2 自己整理一份带趋势的图表JMeter原生报告够用但如果你想把压测现场的变化过程记录得更细致建议自己额外记录一个“压测过程指标表”每分钟记录一次TPS、平均RT、错误率、CPU使用率、内存使用率、磁盘IO。这样跑完一轮压测除了JMeter报告之外你手上还有一份“时间和系统资源”的对照表定位瓶颈时特别好用。熟练之后你甚至可以把这些都写入InfluxDB用Grafana做实时看板。这也是JTLJMeter结果日志配合实时监控的常见方案但那是进阶玩法了基础阶段先把Excel记录和JMeter报告用明白就够了。6. 常见问题与排查技巧实录6.1 “error writing to server”到底什么情况很多人在压测时会遇到一个报错java.io.IOException: Error writing to server。这个错误表面上看是网络异常实际原因不外乎以下几种。第一种是服务器主动断开了连接。当服务器处理不过来时它会重置TCP连接客户端再往这个连接上写数据就会抛IOException。这种原因下压测结果里往往会伴随大量非200响应和超时。第二种是负载均衡或防火墙的空闲超时设置太短比如HTTP客户端的KeepAlive时长超过了LB的空闲超时连接被中间设备关闭再用就报错。第三种是客户端本地端口不够了。高并发下JMeter频繁建立连接系统可用临时端口会被耗尽新连接建立不成功。识别方式是在压测机上用netstat看TIME_WAIT状态的数量是否巨大。排查顺序建议是先看服务端监控CPU是否满负荷、连接数有没有到达上限再看网络设备LB是否抛异常日志最后看施压机资源。我之前遇到过类似问题折腾了半天发现是压测机上文件描述符上限设得太低高并发下socket打不开和服务器一点关系都没有。6.2 文件“已经存在”导致的采样暂停JMeter压测过程中如果结果文件.jtl被重新使用比如第二次运行时用了同一个文件名而当前目录下已经有这个文件JMeter会提示“result file already exists”然后中止执行。默认情况下它不会帮你覆盖。解决方法有两种一是每次启动前手动删掉旧的.jtl文件二是在命令行加参数 -f 强制覆盖。我平时更建议用带时间戳的文件名输出比如jmeter -n -t test_plan.jmx -l result_$(date %Y%m%d_%H%M%S).jtl -e -o report_$(date %Y%m%d_%H%M%S)这样既能避免文件冲突也方便后续整理两轮压测的对比数据。要知道做性能优化的时候最宝贵的就是多轮压测结果的对比它能直观告诉你“上一轮的优化到底有没有效”。6.3 安全证书问题压测HTTPS接口时如果JVM不信任服务端的证书会报SSLHandshakeException。解决方式有三种一是把域名证书导成JKS文件导入JMeter的JVM密钥库二是在HTTP请求里勾选“Use KeepAlive”下方的SSL配置关闭证书校验三是用系统属性方式在启动时加载信任库。最简单的方式是关闭证书校验但要注意这种方法只适用于测试环境的压测。生产环境的HTTPS压测还是建议正规导入证书否则你测的东西根本不是生产环境真实链路数据没有参考意义。7. 实战案例云上环境承载能力验证结合开头提到的场景我分享一次完整的云上压测流程这里不涉及具体的项目名称和敏感信息只讲方法和思路。项目背景是业务系统从一个单节点的自建环境迁移到阿里云ECS上需要验证新环境的承载能力。压测工具就是JMeter压测脚本是和业务方确认过的核心链路脚本脚本里包含了登录、查询列表、创建订单、上传附件等场景业务比例按生产流量分配。流程大致是先在测试环境用小并发比如50线程验证脚本正确性确保参数化和断言都没问题。确认脚本跑通后用阶梯加压模式逐级增加QPS100、200、400、800、1600每级跑10分钟观察响应时间、错误率和系统资源的变化。实测过程中QPS 800之前系统表现很稳TP99基本在300ms以内错误率接近0。QPS升到1600时应用服务器的CPU使用率快速攀升到85%响应时间开始出现明显上升但还没有大规模报错。等到QPS跑到2000错误率直接飙升到15%TP99涨到5秒以上数据库连接池出现获取超时这时可以认定系统的性能拐点在1600左右。这个数据拿回去后和业务方对齐的结论是ECS配置下系统能支撑每秒1600次请求如果业务峰值预期是1200QPS那当前配置是够用的如果后续业务增长到1500QPS以上需要提前扩容数据库或做缓存优化。个人来说这类验证最需要注意的是“压测完一定要看系统有没有残留问题”。高并发压测结束后连接池、线程池、日志文件都可能出现异常状态建议压测完成后观察10分钟确认系统能自动恢复才算验证结束。8. 几个压测老手才会注意的细节最后分享几个小的经验都是踩坑踩出来的。第一JMeter脚本里不要开“View Results Tree”察看结果树监听器跑压测。GUI模式跑有监听器会消耗资源命令行模式跑也会把结果写进日志大量情况下影响JMeter自身性能。需要调试脚本的时候可以临时开正式压测必须关掉。第二压测机和服务器之间的网络带宽要提前估算。假设单请求响应包体是20KBQPS是1000那么需要160Mbps的带宽。如果压测机是云上ECS注意一下带宽计费模式别把带宽跑满了导致结果全部超时。第三压测前检查系统文件描述符。Linux默认1024压测并发一高就不够用。临时调一下ulimit -n 65535如果是systemd管理的服务还要在service文件里加LimitNOFILE65535重启服务才生效。第四JMeter分布式压测时所有机器的时间要同步否则聚合报表里的时间戳对不上分析数据时非常痛苦。用NTP做时间同步是最简单可靠的方案。第五多轮压测之间要有“冷却时间”。服务器在高压状态下CPU、内存、连接池、JVM GC都需要时间恢复。连续两轮压测中间至少等5到10分钟否则第一轮的尾部效应会影响第二轮的基线数据。做压测这行越深入越会发现工具本身只占三分之一剩下的是你对业务的理解、对数据的敏感度和对系统底层的熟悉程度。JMeter只是一个玩具重点是你怎么用它把事情测明白。先把脚本跑通然后试着去回答那三个问题系统在什么负载下变慢在什么负载下挂掉瓶颈在哪里答案找到了压测才算真正做完。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询