ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

JMeter接口压测实战:从环境搭建到分布式性能测试

JMeter接口压测实战:从环境搭建到分布式性能测试 1. 为什么接口压测绕不开JMeter1.1 压测这件事本质上是在回答三个问题接口性能测试说复杂可以复杂到搭建整套压测平台、做容量规划、搞全链路监控说简单其实就是回答三个问题这个接口到底能扛多少并发响应时间能不能达到预期系统的瓶颈到底在数据库、网络还是业务代码我第一次真正理解这三个问题是在一次线上事故之后。当时某个查询接口在高峰期突然大面积超时运维重启了好几次应用才缓过来。事后复盘发现这个接口的单次查询要关联四五张表数据量一上来就慢但谁也没法说清楚到底能支持多少并发。后来用JMeter做了轮完整的压测结果很快浮出水面200个并发的时候平均响应时间已经超过5秒错误率肉眼可见地往上涨。就是这么个过程让我意识到JMeter在接口性能测试里的位置——它不是唯一的选择但却是最不容易绕开的选择。不管是做后端开发的、做测试的、还是负责运维的只要涉及到我这个接口到底行不行的问题JMeter几乎就是默认的起点。1.2 为什么不是Postman也不是写脚本有人会问我平时用Postman调试接口很顺手直接在Collection Runner里跑几十遍不行吗理论上能跑但性能测试和接口调试是两种完全不同的工作。Postman跑接口关注的是单个请求对不对性能测试关注的是大量请求同时上来的时候系统稳不稳。这两个关注点不同决定了工具的核心设计也不同。Postman的运行器本质上是把多个请求串行或简单地并行跑一遍它缺少线程组这样的并发模型也缺少TPS、90%响应时间这类性能指标统计。也有人倾向于直接用Python写压测脚本Requests加并发库照样能压。这条路能走但有一个很现实的问题每换一个项目就要重新写一套脚本压测的采样数据、报告格式、失败重试逻辑都要自己造轮子。JMeter把这些东西都内置好了加上有图形界面可以即改即跑团队协作时大家看一眼脚本结构就懂沟通成本低太多。在我看来JMeter真正的定位是一个性能测试工作台——它不要求你每次从零开始而是把最常见的压测场景都做成了开箱即用的组件。你不需要成为性能测试专家才能开始用但用得深了它也能支持你玩分布式、搞监控集成、做持续集成里的性能回归。1.3 新手最关心的上手成本高不高这个问题我答保守一点如果你只是想跑通一个最简单的HTTP接口压测从打开JMeter到看到聚合报告十分钟足够了。我后面会带你完整走一遍。但要跑到能实际用于生产决策的程度还需要理解线程组、采样器、监听器、逻辑控制器、参数化、断言这些组件的配合关系以及压测本身的方法论——比如怎么设计压测场景、怎么判断指标是否达到预期、怎么分析瓶颈在后端还是压力机自身。这篇文章会沿着这条线往下拆尽量用实际场景把原理和操作串起来。2. 环境搭建从下载到跑通第一个请求2.1 版本和JDK的对应关系踩坑从这里开始很多新手卡在第一步不是不会下载而是下载完启动报错或者打开界面闪退。绝大多数情况就是JDK版本不匹配。JMeter是Java应用它本身不区分32位还是64位但你的JDK版本得对得上。以常用的JMeter 5.x为例5.4及之前的版本在JDK 8下就能跑5.5开始推荐JDK 8及以上到了5.6.3之后官方明确要求JDK 11以上。我的建议是不要卡着最低版本用装JDK 11或者JDK 17比较稳妥日常压测没那么多兼容性焦虑。另外有一个细节经常被忽略JMeter启动时会去读JAVA_HOME环境变量。如果你的机器上装了多个JDK版本一定要确认JAVA_HOME指向的是你希望JMeter使用的那一个。查一下很简单命令行里执行java -version echo $JAVA_HOME如果java -version看到的版本和JAVA_HOME对应不上说明你的PATH优先级和JAVA_HOME设置不在同一套环境里先把这个理顺了再往下走。2.2 下载安装认准Apache官网JMeter的下载我建议直接认准Apache官网的下载页面不要图省事去一些第三方软件站。原因不只是安全和版本老旧的问题而是第三方站点经常会捆一些推广软件或者提供的是改过的安装包出了问题排查起来非常麻烦。JMeter本身就是绿色软件官方提供的Binaries压缩包下载后解压即用完全不需要安装向导。解压之后进入bin目录Windows下双击jmeter.batmacOS或Linux下执行jmeter脚本。注意不要直接双击打包目录里那个不用看的源码jar包。启动界面会有两个窗口一个是JMeter的图形界面一个是命令行日志窗口后者不用管它但不要关因为关了主程序也会跟着退出。启动之后你看到的默认界面就叫工作台左侧是测试计划树形结构。新手可能觉得这个界面有点朴素但它的逻辑其实很清楚一个测试计划下面挂线程组线程组里挂采样器也就是具体的请求采样器旁边挂配置元件、断言、监听器。后面所有操作都围绕这个树形结构展开。2.3 跑通第一个HTTP请求在测试计划上右键添加一个线程组。这里先不要管线程数和循环次数保持默认的1个线程1次循环就好目的是验证整条链路通不通。然后在线程组上右键添加一个HTTP请求采样器。最核心的配置只有几项服务器名称或IP比如你要压测的目标服务地址。端口号HTTP默认80不需要填HTTPS默认443也不需要填如果是自定义端口比如8080要写清楚。协议http或https。方法GET、POST等。路径接口路径。消息体数据如果是POST请求在这里写请求体。配置好之后在线程组上右键添加监听器先用查看结果树来调试。点击顶部绿色三角启动按钮跑完之后在查看结果树里能看到请求的响应数据。这里能明确区分一下查看结果树是给调试用的压测时不要挂它我会在讲监听器的时候详细说明原因。2.4 录制脚本为什么我建议新手先别用JMeter本身提供了HTTP(S)测试脚本录制器原理是在本地起一个代理端口让浏览器走这个代理访问被测系统JMeter自动把请求转成采样器。这个功能对于抓取APP接口、抓取复杂业务流程特别有用很多网上的教程也喜欢教这个。但我的建议是新手第一轮学习时先别依赖录制。因为录制生成的脚本往往非常乱包含大量静态资源请求而且不容易理解每个请求的上下游依赖关系。手动写脚本虽然慢但每一步你都清楚在干什么这才是性能测试的基础功底。等到你对JMeter的常用组件熟悉了再回过头用录制功能去补一些手工拼不出来的复杂请求效率会高很多。关于录制的具体操作后面会在讲APP接口抓取时展开。3. 线程组设计压测模型怎么搭才接近真实业务3.1 线程数、Ramp-Up、循环次数的真实关系线程组是JMeter压测模型的心脏三个参数决定了一个压测场景的基本形状线程数、Ramp-Up Period、循环次数。很多人把这几个参数理解为同时多少个请求这是不对的。线程数表示JMeter会创建多少个线程来发送请求每个线程独立地执行测试计划中的采样器。Ramp-Up Period表示这些线程启动完成需要花费的时间。循环次数表示每个线程执行完一遍之后是否重复执行。举个例子线程数100Ramp-Up 10秒循环次数10。实际运行的效果是10秒内均匀拉起100个线程也就是每秒启动10个启动完成后每个线程连续执行10次请求。所以总请求数就是100乘以10等于1000次。而这个压测对被测系统造成压力从第一秒就开始了不是等100个线程全部启动完才开始。这三个参数怎么组合不取决于你想压多少量而是取决于你想模拟什么场景。如果是验证系统在突发流量下的表现比如大促开始那一刻那应该用短时间、大并发、低循环如果是模拟日常持续的业务负载那应该用较小的并发配合较长的持续时长。大多数刚上手的人把线程数拉得很高循环次数也拉到999最后压出来的数据乱成一团因为那根本不是现实场景。3.2 三种常用压测模式基于上面的逻辑实际工作中常见的压测场景可以归纳成三种模式。第一种是基准测试。单线程、单循环先跑一次看看接口在无压力情况下的响应时间是多少。这个结果是你判断后面所有数据的参照物。如果基准测试就慢那压测就不用做了先解决接口本身的性能问题。第二种是负载测试。逐步增加线程数或循环次数让系统在持续的压力下运行一段时间观察TPS、响应时间、错误率的变化趋势。目的是找到系统在什么并发量下开始明显变慢什么并发量下开始报错。第三种是压力测试。直接把线程数调到很高比如系统预估能扛500并发你就开800、1000看系统在被打穿之前是什么表现崩溃之后能不能恢复。压力测试的目标往往不是通过而是找到系统的极限边界。在JMeter里实现这三种模式基本不需要换工具只需要改线程组的参数。但有一点很关键压测过程要记录系统端的资源使用情况CPU、内存、磁盘IO、数据库连接数这些数据比JMeter报告里的响应时间更能反映瓶颈在哪。没有资源监控配合的压测只能告诉你系统慢了不能告诉你为什么慢。3.3 调度器让压测时间可控线程组面板里还有一个调度器选项它解决的是持续时间控制的问题。比如你要做一轮持续10分钟的压测如果不开启调度器你怎么控制时长靠循环次数乘以每个请求的响应时间估算完全不可控。开启调度器后只需要设置持续时间600秒JMeter会持续施压到时间自动停止。这在回归测试和持续集成场景里尤其好用脚本稳定之后可以放在流水线里定时跑不需要人盯着。两个常用配置持续时间为压测总时长启动延迟表示延迟多少秒后开始压测用于给监控系统留出采集前置数据的时间。3.4 新手最容易犯的错循环次数设成无穷大循环次数那一栏默认是1很多教程会让你勾选永远配合调度器使用。这本身没错但如果没勾选调度器就点了启动JMeter会一直跑下去压测现场就会陷入怎么停不下来的尴尬。正确的做法是要么明确设置循环次数要么勾选永远并搭配调度器的持续时间两者必须同时存在。4. 参数化与Token链路压测时最常卡住的两个瓶颈4.1 为什么压测要做参数化如果接口压测都用同一份请求数据那测出来的结果说服力会打折扣。比如一个查询订单详情的接口如果100个线程同时查同一个订单号热点数据都在缓存里命中率非常高这不能代表真实情况。真实用户的订单号千差万别缓存命中率没那么理想。参数化的含义就是把请求中的固定值替换成可变的、来自文件或表达式生成的数据。最常见的做法是使用CSV Data Set Config。准备一个文本文件第一行是变量名后续每行是数据比如order_id,token 10001,abc123 10002,def456在HTTP请求中把路径里的订单号或者请求体里的token写成${order_id}、${token}这种占位符。在线程组下面添加CSV Data Set Config配置好文件路径、变量名和分隔符JMeter在每个线程执行请求时会自动从文件里读取不同行的数据。这里有一个重要参数共享模式。默认是所有线程共享也就是所有线程轮流读取文件里的数据每个线程拿到的数据不同。如果希望每个线程固定使用一行数据可以选择当前线程组。如果数据量很大文件读取本身成为瓶颈可以考虑把文件放到内存里或者用更轻量的方式生成数据后面会提到。4.2 登录态的Token怎么处理先有了登录才能压测需要鉴权的接口。这是接口性能测试最常见的卡点。如果你压测的接口不需要登录那很简单跳过这一步。但如果需要Token最标准且高效的方案是先在JMeter里发送一次登录请求从响应中提取Token再把它作为全局变量传给后续的请求。具体操作分三步。第一步在测试计划中添加一个线程组叫登录并获取Token里面放一个HTTP请求来调登录接口。这个线程组的循环次数设为1只在压测开始时执行一次不参与后面的压测。第二步在登录请求上添加正则表达式提取器或者JSON提取器。如果登录接口返回的是JSON格式数据比如{ code: 0, data: { access_token: eyJhbGciOiJIUzI1NiJ9... } }用JSON提取器更简单直接。变量名填tokenJSON表达式填$.data.access_token匹配编号填1。如果响应不是JSON那就用正则表达式提取器在响应字段里找到Token的位置写对应的正则。第三步把提取出来的变量变成一个全局可用的变量。网上很多教程到这里就默认它能用在其他线程组了但实际上提取器提取的变量默认是局部于当前线程或当前线程组的如果你想在另一个线程组里引用需要通过属性传递。比较常见的做法是添加一个BeanShell PostProcessor把变量写入JMeter属性${__setProperty(token, ${token},)}然后在需要用到Token的请求里通过${__property(token)}来引用。这样登录线程组执行一次所有压测线程都拿到了同一个Token。4.3 并发登录与大量用户数据的思路上面的方案适用于Token可以复用的场景。如果被测系统要求每个用户独立的Token比如同一个Token在多个设备上登录会被踢下线那就需要每个线程都走一遍登录流程并且每个线程的登录账号都不一样。这正是参数化配合循环的典型场景把几千个测试账号放在CSV文件里线程组配置足够的线程数每个线程读取一个账号去登录登录响应里提取Token后续请求携带各自的Token。这种方式在脚本上会稍微复杂一些但更贴近真实业务。需要注意的是大量并发登录本身会对被测系统的多个服务产生压力比如用户中心、认证服务这些压力在计算压测结果时要单独评估避免把登录请求的开销算进业务接口的压测结果里。4.4 文件上传接口怎么压热搜词里有一个jmeter上传文件这里单独说一句。在HTTP请求采样器里切换到Files Upload区域配置文件路径和参数名。需要注意三点一是文件路径应该填绝对路径因为JMeter的工作目录在不同启动方式下不固定相对路径容易踩空二是如果要压测的是多用户上传不同文件参数化文件路径即可三是大文件上传的压测要关注压力机本身的带宽和磁盘IO否则瓶颈可能在施压端而不是被测端。4.5 参数化的小技巧用函数生成随机数据CSV文件非常适合维护数据量不大、有明确含义的测试数据。但如果你的场景只需要简单的唯一标识比如订单号、手机号、时间戳完全可以用JMeter内置函数来生成省去维护文件的麻烦。比如${__time(yyyy-MM-dd HH:mm:ss)}可以生成当前时间${__Random(1000,9999)}可以生成随机数${__UUID()}可以生成唯一的UUID。把它们拼起来就能组合出大量互不相同的请求数据。我的习惯是有业务含义的数据用CSV纯技术性的唯一性数据用函数。两种方式混用既保证数据的真实性又减少文件的读写开销。5. 断言与监听器怎么证明接口真的扛得住5.1 响应断言先保证响应是对的再说快不快压测结果里经常出现一种情况TPS很高响应时间很短但接口返回的全是错误信息。如果你的压测脚本没有做断言那所有请求不管返回什么都算成功这样就很有问题了。所以断言是压测脚本里不可省略的组件。最常用的是响应断言配置方式非常简单在HTTP请求下添加响应断言把要测试的模式配置为包含然后填入接口正常返回时一定包含的关键字。例如{code:0}或者success。这样JMeter每发出一个请求都会校验响应中是否包含这个关键字。只有包含关键字的请求才被计为成功否则计入错误率。压测跑完看错误率的时候断言先帮你过滤掉那些业务上失败的请求。这里说一个具体场景有一次压测看聚合报告错误率是0%我觉得很顺利但后来翻日志发现接口其实一直在报某个逻辑异常只不过HTTP状态码还是200JMeter把它算成了成功。后来我在所有压测脚本里都加了断言就再也没有发生过这种假通过的情况。5.2 聚合报告字段理解对了数据才有意义压测结束之后大家最常看的就是聚合报告。里面每一个字段都有具体含义但很多人在分析时容易只看平均值。Samples总请求数。Average平均响应时间。这个值容易掩盖问题因为少量很慢的请求会被大量正常请求拉平。Median中位数响应时间。比平均值更能代表大部分用户的感受。90% Line / 95% Line / 99% Line分别表示90%、95%、99%的请求响应时间小于等于这个值。性能问题越严重这几个值和平均值的差距就会越大。Min / Max最小和最大响应时间。Error%错误率。Throughput每秒钟的请求数这就是我们常说的TPS。分析的时候我习惯先看Error%是不是0再看99% Line是否在业务要求范围内最后才看平均响应时间。如果一个接口的Average是200ms但99% Line到了2秒说明存在明显的长尾请求你的系统处理不过来单纯优化平均值是没有用的。5.3 压测时监听器怎么挂才不影响结果这是个容易被忽视但影响很大的细节。查看结果树会保存每个请求的完整报文数据量一大施压机本身的CPU和内存就会被日志记录拖垮最终压测的数据混合了施压机性能下降的影响测出来的结果就不准确。我的原则是调试阶段用查看结果树正式压测阶段只保留聚合报告或者用后端监听器把数据发到监控平台无必要不挂结果树。另外聚合报告本身是在压测过程结束后统一汇总的也不建议在压测过程中频繁点它查看界面渲染本身也有开销。5.4 同样的脚本为什么两次压测结果差很多这种情况我遇到太多次了。脚本没变但今天压TPS是2000明天压变成800第一反应是系统出问题了但排查一圈发现多数时候问题出在压测环境的一致性上。压测环境要尽量保持跟生产环境相同的配置和独立资源不能跟其他测试任务共用一个环境。同时固定压测时间比如都选在业务低峰期避免其他人也在跑测试或者使用同一套资源。还有一个容易忽略的点压测机的性能会直接影响结果尤其是压测机CPU满载的时候TPS数据就没有参考价值了可以切到分布式压测来分散压力。6. 分布式压测与监控从单机到集群6.1 什么时候需要分布式压测单机JMeter能发起的并发是有限的这个上限主要取决于施压机的CPU、内存、网络连接数以及被测系统的承载能力。当你需要用很多并发线程来模拟海量用户时一台机器往往撑不住。判断是否需要分布式的标准不是并发数大于XX就必须用而是看施压机本身的资源占用。如果压测过程中施压机CPU达到90%以上或者网络带宽被打满这时候增设施压机的线程数已经没有意义因为瓶颈在施压端。此时就应该拆成多台施压机每台分担一部分压力。JMeter的分布式模式结构其实很简单一台控制机master负责编排脚本多台施压机agent负责实际发送请求。控制机把测试计划分发到各施压机执行再汇总各施压机返回的测试数据。配置过程大致是每台施压机启动bin目录下的jmeter-server控制机在jmeter.properties里配置远程主机地址然后在图形界面上点击远程启动。6.2 分布式配置中容易踩的两个坑第一个坑是脚本文件路径。如果测试计划里用了CSV参数化文件每台施压机都需要有这个文件而且文件路径要保持一致因为JMeter不会自动把文件同步到所有施压机。更稳妥的做法是在每台施压机的固定目录下放同一份数据文件。第二个坑是时间同步。压测的起止时间、数据采集时间点如果各机器不一致最后汇总出来的TPS曲线就失真了。建议在压测开始前给所有参与压测的机器做一次时间同步避免部分机器的数据在时间轴上偏移。6.3 压测数据可视化InfluxDB加Grafana如果压测任务频繁只看聚合报告是远远不够的需要把压测数据实时地投递到监控面板上。JMeter提供了后端监听器Backend Listener其中InfluxDB是一个使用较多的后端方案。配置好InfluxDB的地址、数据库名和采样周期JMeter会把每秒的TPS、响应时间、错误率写入InfluxDB再用Grafana做成可视化的仪表盘。当压测过程中TPS突然掉下去了或者响应时间开始往上走你能在Grafana上实时看到曲线变化再配合应用服务器的监控面板很快就能定位是哪个环节出了问题。这套组合现在非常常用也是JMeter从手工压测工具走向性能测试平台的关键一步。6.4 压测现场最容易忽略的三件小事第一件压测前导入安全证书。如果你压测的是HTTPS接口JMeter需要把目标服务器的SSL证书导入自己的信任库否则握手阶段就会报错。网上教程很多核心操作就是通过安装JMeter安全证书完成具体方式在不同环境下操作不太一样遇到时可针对性搜索。第二件正式压测前先在低并发下做一轮完整冒烟。脚本有没有问题、断言配置对不对、Token提没提取成功这些用1到5个线程先跑一遍就会暴露出来。直接拉高并发跑一旦脚本出错排查成本会高很多。第三件压测结束之后保存好原始结果文件。jmeter命令行方式跑出来的.jtl文件保留了每个采样数据后续要做更深入的分析、对比多轮压测数据甚至出正式报告都靠这份原始数据。7. 面试高频问题性能测试和JMeter怎么答才不虚7.1 被问做过性能测试吗怎么组织回答现在后端开发和测试岗位的面试大概率会问到性能测试相关的问题。很多人简历里写了熟悉JMeter但面试官一深问就露馅了。其实只要照着一条主线去组织回答就不会乱发现问题、定位瓶颈、优化方案、验证效果。一个相对完整的回答模板是描述一个具体的接口或系统说清楚这个系统的业务背景和性能要求然后说明你是如何设计压测场景的比如并发规模、压测时长、参数化方案接着说明你用了哪些指标来判断系统表现——TPS、响应时间、错误率、CPU内存使用率再说压测过程中发现了什么问题你如何定位到瓶颈比如是数据库慢查询、连接池不够、还是代码逻辑里的锁竞争最后说明优化后再次压测指标提升到多少。这个回答的精髓在于它把性能测试从会操作JMeter提升到会做性能分析的层面面试官最看重的也是后者。7.2 几个高频考点JMeter的线程组和循环次数的区别线程组定义了并发模型循环次数定义了每个线程重复执行的次数。两者组合起来决定总请求量。响应时间指标怎么分析要看平均值、中位数、90%、95%、99%多个维度只看平均值会忽略长尾请求。QPS和TPS有什么区别严格来说QPS是每秒查询数TPS是每秒事务数。在接口压测里一个事务可以包含一个或多个请求。但在实际工作中两者经常混用面试时说清定义就行。分布式压测的原理是什么控制机分发脚本多台施压机并行发请求汇总数据。JMeter压测的时候瓶颈怎么判断分别看施压机资源和被测系统资源。施压机CPU打满说明压测端成为瓶颈被测端CPU、数据库连接池、慢查询等指标异常就是系统端的问题。7.3 JMeter在性能测试体系里的位置最后想分享一点个人感受。单会用JMeter其实只是性能测试的第一步真正的难点在于怎么设计有效的压测场景、怎么分析数据、怎么定位瓶颈。JMeter更像是你的手用熟练之后要把更多的精力放在脑上面——思考你的系统在什么场景下会出问题出了问题时怎么从数据中反推出原因。这就像开车JMeter是那辆车性能测试方法论是导航。车再好没有导航也会迷路导航再准确不熟悉车况也开不顺畅。先把JMeter练熟再慢慢建立自己的性能测试思路这条路走下来你会发现自己看系统的眼光都会不一样。我在实际项目中还有一个习惯每次压测完都会把脚本、原始数据、报告和结论整理成一个独立的压测文档放到项目知识库里。下次这个接口有性能回归需求直接翻出之前的脚本重新跑一遍对比数据变化效率高很多。这算是压测工作中很小的一个习惯但长期坚持下来省下的时间和踩过的坑都不少。
返回列表