尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

JMeter组件全解析:从线程组到监听器,理清作用域与常用搭配

JMeter组件全解析:从线程组到监听器,理清作用域与常用搭配 这阵子手头压测任务告一段落帮几个项目搭完JMeter压测环境踩了不少坑也把组件之间的逻辑重新捋了一遍。决定写个系列第一篇先把JMeter的组件家底盘清楚。性能测试工具里JMeter可能是国内用得最广的了免费、开源、生态大从接口调试到高并发压测都能包办。但很多新手拿到JMeter面对那一堆组件树会懵线程组、采样器、配置元件、逻辑控制器、监听器到底各管什么放哪一层才算对为什么照着教程配了脚本跑出来结果却不对。这篇就把JMeter工具组件的分类、作用域、常用搭配和几组容易出问题的组合讲明白。内容比较基础但很实用适合刚入门性能测试的测试工程师、开发同学也适合那些用JMeter做接口测试但一直没系统整理过组件关系的朋友。1. 先把整体框架立起来JMeter里的组件到底怎么分类1.1 组件七大类型一句话说清各自位置打开JMeter新建一个测试计划左侧面板能看到那一长串“添加”菜单。很多新手被这个菜单吓住其实JMeter组件翻来覆去就这么几类线程组、采样器、配置元件、逻辑控制器、前置处理器、后置处理器、断言、定时器、监听器。如果按职责来记可以分成三拨第一拨是“干活的”包括线程组和采样器。线程组负责定义并发压力模型——多少个用户、多长时间内启动、循环几次采样器负责真正发出请求比如HTTP请求、JDBC请求、FTP请求最常见的还是HTTP请求。没有采样器线程组就是个空转的壳子。第二拨是“辅助编排的”包括配置元件、逻辑控制器、前置处理器、后置处理器。配置元件用来定义公共数据比如请求头、Cookie、从CSV读变量逻辑控制器用来控制采样器的执行流程比如循环、条件分支、交替执行前置处理器在采样器发请求前干点准备工作后置处理器在响应回来后提取数据做关联。第三拨是“检查与记录的”包括断言和监听器。断言用来判断响应是否符合预期监听器用来展示和保存测试结果。这个分类看起来简单但组件的位置放错、作用域搞错是新手踩坑最多的地方。所以我建议你先记住这个框架接下来再逐类拆细节。1.2 作用域和顺序规则很多问题的根源JMeter组件不是随便拖到哪都能生效的它遵循两套规则作用域规则和执行顺序规则。作用域规则基于测试计划的树形结构。一个组件如果放在线程组下面那么它对线程组下所有采样器生效如果放在某个采样器下面那么它只对这个采样器生效。举例来说把HTTP Cookie Manager放在线程组层级登录请求、查询请求、下单请求都能共用同一份Cookie如果误放到某个请求下面那其他请求就拿不到Cookie压测一跑就全是401或302。执行顺序规则则是JMeter固定的配置元件最先执行接着是前置处理器再是定时器然后是采样器采样器跑完之后执行后置处理器再执行断言最后监听器记录结果。同一个层级的组件按从上到下的顺序执行。这个顺序我想单独强调因为很多脚本跑出来的结果和预期不符往往是顺序问题比如变量还没生成就被断言拿去用了。提示同一个父节点下父级组件的作用域会覆盖所有子节点兄弟节点之间的顺序是自上而下。遇到“变量找不到”的报错优先检查变量生成的提取器是不是放在了使用它的采样器之后。2. 入门的核心三件套线程组、采样器和配置元件2.1 线程组参数怎么填才算真正会填线程组是每个脚本的起点右键测试计划 → 添加 → 线程用户 → 线程组。新建之后会看到四个关键参数线程数、Ramp-Up时间、循环次数、调度器。线程数就是并发用户数注意它和TPS不是一个概念。100个线程不代表系统就有100 TPS实际TPS取决于每个请求的响应时间。Ramp-Up时间指的是启动全部线程需要的时间如果设5秒每秒启动20个线程公式就是“线程数 ÷ Ramp-Up时间 每秒启动线程数”。很多人把这个参数理解成预热时间其实不是它是控制并发用户“爬坡”速度的模拟真实用户逐渐进入系统的过程。循环次数控制每个线程执行多少次采样器勾选“永远”并配合调度器使用可以做出“持续压测20分钟”这类的场景这是做稳定性压测最常见的方式。调度器有五个字段持续时间、启动延迟、结束时间、启动时间和状态。实际用得最多的是“持续时间”单位是秒。假设你要给一个登录接口做5个用户并发登录测试线程数填5Ramp-Up填5循环次数勾选“永远”调度器持续时间填60脚本就会在60秒内保持5个并发用户持续打登录接口。2.2 采样器HTTP请求的常用配置采样器是真正把请求发出去的东西。以最常用的HTTP请求为例添加方式右键线程组 → 添加 → 采样器 → HTTP请求。面板上的关键字段是协议http或https、服务器名称或IP、端口号、方法GET/POST/PUT/DELETE、路径以及参数区。参数区有两个TabParameters和Body Data。Parameters适用于GET请求的query参数或者表单格式application/x-www-form-urlencoded的POST请求Body Data适用于传JSON、XML等原始报文。这里很多新手容易踩坑。做一个若依微服务这类前后端分离项目的登录接口请求体通常是JSON格式比如{username:admin,password:123456}。这时必须在HTTP请求的“消息体数据”里写JSON并且通过HTTP Header Manager加一行Content-Type: application/json。如果你把JSON拼在Parameters里服务端根本解析不出来接口直接返回“请求体缺失”之类的错误。HTTP请求面板还有几个高级选项建议压测HTTP/1.1接口时勾选“使用KeepAlive”减少TCP重复建连的开销对POST请求如果需要模拟文件上传需要勾选“对POST使用multipart/form-data”并在“文件上传”区域填写文件路径和参数名这个在测试上传接口时经常会用到。2.3 配置元件Header、Cookie和CSV是压测的好帮手配置元件里最常用的三个是HTTP Header Manager、HTTP Cookie Manager和CSV Data Set Config。它们的共同点是“为采样器准备数据”但生命周期和生效范围有区别。HTTP Header Manager用来统一管理请求头比如Content-Type、Authorization、User-Agent。如果放在线程组下线程组里所有采样器都会自动带上这些请求头省得每个请求单独配。HTTP Cookie Manager负责Cookie的存储与发送做需要登录态的压测几乎必用。它能自动保存Set-Cookie在本地并在后续请求中自动带上去不用你手动从响应里提取Cookie再拼到请求头。要注意Cookie Manager必须放在线程组层节点否则其他采样器共享不了登录成功的会话。CSV Data Set Config也就是“CSV参数化文件”批量造数据和模拟多用户登录时特别好用。它会按行读取一个CSV文件每次采样器执行时自动取下一行数据填充到变量里。比如测试5个用户并发登录CSV里放五行账号密码变量名写username,password那么在采样器里用${username}和${password}引用就行JMeter底层会自动按线程取不同行保证不会所有用户共用同一个账号。用户定义的变量User Defined Variables也值得一提它适合放一些全局的环境配置比如环境地址、公共请求路径。切换压测环境时只改这一处不用到处翻脚本。3. 逻辑控制器让脚本会“思考”3.1 循环、If和While控制器怎么配合使用逻辑控制器是JMeter里最灵活的一块它不直接发请求而是决定采样器“怎么跑”。新手用得最多的是循环控制器、If控制器和While控制器。循环控制器可以让子节点循环执行N次适合那种“同一个请求重复刷N遍”的场景。比如想单独对首页接口做100次压测把它放进循环控制器里循环次数填100线程组循环次数填1逻辑就非常清晰。If控制器是条件分支用来满足“满足某条件才执行下面的请求”。比如只在响应码等于200时才执行后面的下单请求条件可以写成${__jexl3(${code} 200,)}。注意JMeter 5.x里官方推荐用__jexl3函数做条件判断不建议直接用${code}做字符串比较因为会把字符串误判成布尔值导致判断永远为真。While控制器实现“直到条件满足才停止”的循环。一个典型场景是分页接口服务端返回数据里有个字段判断是否还有下一页可以用While控制器配合后置处理器提取返回值不断循环获取分页数据直到“hasNext”为false才跳出。这个组件在接口自动化里很有用但要注意一定要在循环体内提供改变循环条件的后置处理器否则会成为死循环。3.2 事务控制器吞吐量统计的正确姿势很多同学压测时会发现聚合报告里的TPS不好看其实不是系统性能弱而是统计口径的问题。接口级TPS算的是每个Sampler的请求数但在真实业务里一次“下单”可能要调三个接口登录、创建订单、支付。如果只统计其中一个接口的TPS很容易误判。这时候用事务控制器Transaction Controller把三个采样器包起来命名成“下单完整事务”。事务控制器下所有采样器执行完成的总耗时会作为一个“事务”计入结果聚合报告里的TPS就是我们真正关心的业务TPS。事务控制器还有一个作用它可以统计“不包含思考时间的真实响应时间”也可以配置“Generate parent sample”生成父级事务样本方便在监听器里单独看整个事务的表现。注意事务控制器不要包太重的循环或过多的请求否则样本里的平均响应时间会被拖长不利于分析瓶颈到底出在哪个环节。事务控制器只做“归组”具体每个接口的耗时还要靠单独的结果树和聚合报告去看。3.3 控制器与BeanShell结合实现动态控制逻辑控制器本身只是“开关”如果想让流程带点动态逻辑就得借助脚本能力。常见的是结合JSR223/Groovy或者BeanShell脚本写vars.get、vars.put来修改JMeter变量从而影响控制器的判断条件。比如做动态QPS调整可以在线程组外面加一个JSR223采样器循环里通过vars.get(currentQps)去读某个变量再根据当前运行时间或响应时间修改这个变量配合While控制器做到“在一定条件下加大/减小压力”。这种玩法比较高级解决的是“静态脚本无法动态调整压测强度”的问题但前提是脚本别写得过重。JMeter官方在JMeter 3.1之后明确建议优先用JSR223 Groovy而不是BeanShell性能和灵活性都不在一个量级。我自己的习惯是能用JSR223就不用BeanShell只有维护老脚本时才不得不处理BeanShell的兼容问题。逻辑控制器的设计原则是尽量让脚本可读“流控制”和“业务步骤”分离。控制器只负责跳转真正要执行的业务都放在采样器里不要为了炫技把一个采样器塞一堆脚本。这会让脚本特别难维护换个人接手基本就想重写。4. 断言、提取器和监听器验证结果、抓取数据、输出报告4.1 断言怎么选响应断言、JSON断言、JSR223断言断言负责判断请求成不成功。响应断言是最基础的一种直接在“测试模式”里勾选“响应文本包含”然后填一段你期望出现的关键词比如登录接口返回了“success”就认为通过。还可以检查响应代码、响应头甚至用正则表达式匹配响应内容。它上手快但只能做“粗糙”的校验比如服务端返回一个200但业务上却是失败这种场景响应断言往往发现不了。JSON断言是专门针对返回JSON格式的接口的可以配置JSON Path表达式例如$.code期望值填“200”JMeter会直接解析JSON并判断该路径下的值是否符合预期。这个比响应文本匹配可靠得多不会因为某个字段里恰好包含关键词而产生误判。如果需要更灵活的校验就用JSR223断言很多老教程里叫BeanShell断言。JSR223断言可以写一段Groovy脚本自由地解析响应体、做逻辑判断、输出日志还能用AssertionResult.setFailureMessage()自定义失败提示。比如登录成功后希望检查响应中的“userId”是否等于某个变量用Groovy写几行就行。这里再次强调脚本断言是双刃剑压测时脚本越重JMeter自身的CPU消耗越大可能反过来拉低压测结果。尽量用简单断言复杂逻辑放到调试阶段用正式压测尽可能关掉或少用。4.2 前后置处理器正则提取器和JSON提取器的用法后置处理器在采样器执行完成后运行最常用的场景是做“关联”——从上一个请求的响应里提取数据给下一个请求用。比如登录接口返回一个token后续所有请求的请求头都要带Authorization: Bearer ${token}这时候就需要用后置处理器把token提取出来。正则表达式提取器是老牌方案适合处理格式固定的响应比如提取token:(.*?)模板填$1$匹配数字填1缺省值填NOT_FOUND。优点是兼容老接口缺点是正则写不好很容易匹配错尤其是遇到JSON里有多层嵌套、字符串带转义的情况。JSON提取器是处理REST接口相对舒服的选择配置项是JSON Path表达式比如$.data.token再把变量名填成token后续请求直接引用${token}。它不用关心响应里的空格和换行解析结果也更精准。现在新项目接口基本都是JSON所以JSON提取器可以说是首选。前置处理器相对用得少常见的是在请求前修改参数或生成随机数据比如用JSR223前置处理器动态生成一个UUID放到变量里。实际做接口压测时抽奖接口、订单号这类“每次请求都需要不同参数”的场景非常依赖前置处理器动态生成参数。4.3 监听器的正确用法监听器负责把压测结果“呈现”出来。查看结果树最直观能看到每个请求的请求体、响应体、耗时、断言结果是调试脚本时最方便的帮手。但它只能在调试阶段开正式压测时如果在GUI模式开着结果树跑高并发JMeter的CPU和内存会很快被打满结果就失真了。聚合报告适合在调试完成后看整体指标重点看吞吐量、平均响应时间、90%或99%百分位响应时间、错误率。这些数据能直接反映系统的承载能力。真正做正式压测时建议不要开着GUI跑而是用命令行模式jmeter -n -t test.jmx -l result.jtl -e -o reportdir。这样可以避免JMeter GUI自身的开销干扰压测结果同时生成一个HTML报告比聚合报告更直观有响应时间趋势图、吞吐量曲线、错误分布等。关于HTML报告汉化的问题新版JMeter的报表模板已经自带多语言或可以通过替换模板文件来处理如果打开是英文检查一下你生成的report目录里是不是没有加载到模板语言包或者设置一下jmeter.reportgenerator.locale参数。提示压测结果里的“响应时间”默认不包含思考时间如果你想模拟真实用户“看一会再点下一页面”的场景需要用固定定时器或高斯随机定时器在两次请求之间加一个停顿这样更接近真实用户操作而不是纯CPU压测。5. 常见问题与排查技巧实录5.1 组件不起作用的几个常见原因很多JMeter脚本跑不通最常见的不是系统有问题而是组件配置不对。我整理了几个高频问题现象可能原因解决办法响应断言失败但浏览器访问正常缺少Cookie或请求头请求没带登录态在查看结果树里对比“请求体”“响应体”检查是否加了HTTP Cookie Manager和Header Manager变量显示为${token}没有被替换提取器的作用域不对或提取器放在使用点之后把正则/JSON提取器放到“生产token的请求”下面并确保它在使用该变量的请求的上级节点并发一高JMeter自身CPU飙升BeanShell脚本太重或者开着结果树压测改用JSR223 Groovy脚本正式压测用命令行跑并关闭监听器响应返回500且看不到具体错误服务端未打印堆栈或请求参数类型不对检查Content-Type是否匹配路径是否正确必要时在结果树里查看服务端返回的报错信息接口返回的数据在CSV参数化后没变变量引用方式错误或CSV文件路径问题确认CSV文件位置是相对还是绝对路径变量名是否和CSV表头完全一致5.2 HTTPS录制和证书问题JMeter录制HTTPS脚本时需要先设置HTTP代理服务器然后浏览器会提示证书不受信任。这个时候需要把JMeter生成的临时根证书ApacheJMeterTemporaryRootCA.crt安装到浏览器的受信任根证书颁发机构里重新打开浏览器就能正常录制。这个坑在于证书安装后浏览器更新或换机器会失效所以录制完脚本后还是要回到Request面板手工核对一遍请求参数不能完全依赖录制生成的内容。5.3 上传文件、动态验证码这类特殊场景文件上传接口用JMeter做压测重点是请求类型。上传文件时必须勾选“对POST使用multipart/form-data”并且在文件上传区域配置好文件路径和参数名。很多上传接口报错是因为用了普通POST表单服务端拿不到multipart的数据。动态验证码是所有压测场景里最让人头大的。生产环境的验证码功能如果没做“测试模式可绕过”的处理压测脚本很难直接处理真实验证码。常见的办法有三种一是找开发加个测试开关直接走验证码校验的白名单二是用后置处理器把验证码请求的响应提取出来模拟“前端调用生成验证码接口再提交”的链路三是用JSR223脚本对接验证码识别的内部服务。不管哪种核心原则都是压测重点是“系统承载能力”验证码识别不该成为压测瓶颈更不建议在正式环境用暴力识别这种方式去撞接口。5.4 从K8s迁移到云ECS后JMeter高并发验证的注意点如果你和我一样接手过把单节点Kubernetes上的一套微服务环境迁移到云上ECS然后用JMeter做高并发验证的活儿这里有几个容易被忽略的点可以提一下。迁移完成后压测前先确认云ECS的安全组策略放行了压测机到ECS的端口否则脚本一跑就超时。第二点如果服务端是微服务架构压测前最好先确认一下所有内部调用链路是否走通了迁移后服务注册发现配置容易漏掉导致接口能通但部分功能报错。第三点JMeter压测机要放在和被测系统同一个区域的VPC里尽量不要跨公网压测公网延迟会污染响应时间数据。最后一点一套标准的承载能力验证脚本最好包含登录、业务列表查询、核心操作等几种交易类型比例按线上实际情况分配不要只打一个接口。结尾写这么多我自己梳理组件时感触最深的一点是JMeter工具组件不多但组合起来非常灵活真正决定脚本质量的是“作用域、顺序、数据流”这三件事。很多新手花大量时间调线程数和监听器却忽略了组件摆放的位置和变量传递的逻辑导致脚本本身就不够准。最后再分享一个小技巧我现在写新脚本时尽量用JSR223 Groovy代替老式的BeanShellJMeter会自动编译Groovy脚本执行效率比BeanShell高一个量级而且脚本变量、日志输出都更顺手。压测过程中也要习惯用命令行模式跑正式测试GUI只用来调试。这套组合拳打下来JMeter这个性能测试工具用起来会顺手很多后续有机会我会再把JMeter动态QPS调整和HTML报告模板汉化这两个实操单独写成文章欢迎持续关注。
返回列表