JMeter接口测试工业化实践:从脚本编写到CI/CD全链路

JMeter接口测试工业化实践:从脚本编写到CI/CD全链路 1. 这不是“点点点”的接口测试而是用JMeter构建可复用、可追踪、可量化的HTTP验证体系很多人第一次打开JMeter看到那个复古的Swing界面下意识就把它当成一个“高级版Postman”——填URL、选方法、点执行、看响应。我带过的三届测试团队里有超过60%的新手在前三周都卡在这个认知层把JMeter当请求发送器用而不是当接口质量验证引擎来设计。结果就是脚本散落各处、参数硬编码、断言形同虚设、失败后根本不知道是环境问题、数据问题还是接口逻辑缺陷。真正的问题从来不在“能不能发出去”而在于“发得准不准、验得全不全、跑得稳不稳、查得快不快”。“使用Jmeter进行http接口测试全”这个标题里的“全”字恰恰是最容易被忽略的分水岭。它不是指功能菜单点全了而是指覆盖全生命周期从单接口功能验证到多接口业务链路串联从静态参数校验到动态Token与会话状态管理从单用户线性执行到千级并发下的稳定性压测基线从原始响应体比对到JSONPath精准提取JSR223深度断言从本地调试通过到CI/CD流水线中自动触发、失败归因、报告归档。这整套闭环才是工业级HTTP接口测试的“全”。你不需要是Java专家但必须理解JMeter不是黑盒工具——它的每个元件都有明确职责和执行时序你不需要写复杂代码但必须掌握Groovy脚本如何在关键节点注入逻辑你不需要部署K8s集群但必须清楚分布式压测时Master如何协调Slave、结果如何聚合、时钟偏差如何影响TPS统计。这篇文章就是基于我在电商中台、金融风控、IoT设备管理平台三个高并发、强一致性场景下累计运行超2700万次JMeter测试的真实经验写成。它不讲“怎么安装”只讲“为什么这样配置”不列菜单路径只拆解元件协作逻辑不堆砌截图只呈现真实踩坑后的修复策略。如果你正被“脚本一改就崩”“并发一上就乱”“报告看不懂”困扰那接下来的内容就是你该抄的作业。2. 理解JMeter的执行模型线程组不是“用户”取样器不是“请求”监听器不是“结果”很多人的JMeter脚本之所以脆弱根源在于对底层执行模型存在根本性误解。他们把“线程组”当成“虚拟用户数”把“HTTP请求取样器”当成“一次点击”把“查看结果树”当成“最终答案”。这种类比在简单场景下能蒙混过关一旦涉及登录态维持、令牌刷新、数据依赖或并发竞争立刻崩溃。我们必须回归JMeter的设计原点它是一个基于线程模型的事件驱动测试框架所有元件都在特定生命周期内按严格顺序执行。2.1 线程组的本质隔离的执行上下文容器而非用户模拟器线程组Thread Group常被误读为“启动N个用户”。这是危险的简化。实际上每个线程组创建的是一个独立的Java线程执行上下文它包含一组完全隔离的变量作用域vars独立的Cookie管理器实例除非显式共享独立的缓存控制如HTTP Cache Manager独立的计时器执行队列这意味着同一个线程组内的多个HTTP取样器天然共享会话状态如JSESSIONID而不同线程组间的取样器默认完全隔离即使访问同一域名也像两个无关联的浏览器窗口。我曾遇到一个典型故障某支付链路测试中登录接口放在“Setup Thread Group”支付接口放在“Main Thread Group”结果95%的支付请求返回401。排查三天才发现Setup组生成的Cookie从未传递给Main组——因为它们是两个独立线程CookieManager默认不跨组共享。解决方案不是“加个BeanShell”而是将登录逻辑移入Main组的前置逻辑控制器如setUp Thread Group或使用__setProperty函数全局传递Token。提示线程组的“循环次数”和“线程数”共同决定总请求数但实际并发压力由“线程数×每线程Ramp-Up时间内的请求数”动态决定。例如线程数100Ramp-Up 10秒循环1次意味着前10秒内均匀启动100个线程峰值并发≈100若循环10次则每个线程会连续发起10次请求实际并发可能远超100取决于接口响应时间。这是压测中TPS计算失真的常见根源。2.2 HTTP取样器的真相协议封装器其行为由上游元件严格定义HTTP请求取样器HTTP Request Sampler本身不处理任何业务逻辑它只是一个协议参数组装器。它的所有关键行为均由上游配置元件Config Elements预先定义HTTP Header Manager设置Content-Type、Authorization等头字段。注意它只影响后续的取样器对自身无效。HTTP Cookie Manager自动提取Set-Cookie并附加到后续请求。但若服务器返回SameSiteStrict且跨域需手动添加Cookie头。HTTP Cache Manager模拟浏览器缓存行为。开启后对相同URL的GET请求可能直接返回缓存导致“请求未发出”却显示成功——这在验证接口幂等性时是致命陷阱。最易被忽视的是重定向处理逻辑。JMeter默认跟随302重定向但不会自动携带原始请求的Body如POST数据。某次测试SSO单点登录登录接口返回302跳转至首页JMeter自动跳转后首页请求因缺少Session Token而失败。解决方法是在HTTP取样器中取消勾选“Follow Redirects”改用正则提取器捕获Location头再用第二个取样器手动发起跳转请求。2.3 监听器的定位调试探针非生产报告源监听器Listener如“查看结果树”、“聚合报告”本质是实时调试探针用于开发阶段快速定位问题。它们在压测时会严重拖慢性能每条请求都需序列化响应体、渲染UI、写入内存导致JVM频繁GCTPS虚高、响应时间失真。某次金融转账压测开启“查看结果树”后TPS从1200骤降至300GC耗时占总耗时78%。正确做法是压测时仅启用“Backend Listener”如InfluxDB/Graphite或“Simple Data Writer”写入CSV调试阶段再加载日志分析。注意“聚合报告”中的“90% Line”并非P90而是按响应时间排序后位于90%位置的值。当样本量不足如1000次该值波动极大不能代表真实长尾。生产压测必须保证单轮有效样本≥5000且排除预热期前10%数据。3. 构建健壮脚本的核心四件套参数化、关联、断言、逻辑控制一个能投入CI/CD的JMeter脚本必须跨越四个门槛数据不写死参数化、状态能传递关联、结果可判定断言、流程可分支逻辑控制。这四者缺一不可且存在严格的执行时序依赖——参数化在取样器前关联在响应后断言在关联后逻辑控制贯穿全程。3.1 参数化从CSV到JSR223数据供给的三种层级参数化不是“把URL里的ID换成变量”而是构建数据供给管道。JMeter提供三层能力第一层CSV Data Set Config静态批量供给适用于预置测试数据集如1000个用户账号。关键配置Recycle on EOF?文件读完后是否循环。压测时建议关闭避免重复使用旧数据引发脏读。Stop thread on EOF?读完即停线程。功能测试建议开启确保每个线程只跑一遍数据。Sharing modeAll threads全局共享 vsCurrent thread group组内共享。多线程组场景下若需各组独立数据流必须选后者。第二层__Random / __time 函数轻量动态生成适用于生成唯一ID、时间戳。例如${__Random(1000,9999,)}生成4位随机数。但注意__time函数在每次调用时重新计算若在同一个取样器中多次引用如URL和Body都用可能产生不一致时间戳。应改用vars.put(ts, ${__time(yyyy-MM-dd HH:mm:ss,)})在前置处理器中统一生成。第三层JSR223 PreProcessor动态逻辑供给这是真正的“数据工厂”。例如生成符合Luhn算法的银行卡号、计算HMAC签名、调用内部服务获取动态Token。核心代码模板import java.security.MessageDigest def cardNo 453201511283036 // 基础卡号 def digits cardNo.replaceAll(\\D, ).toList().collect{it.toInteger()} // Luhn算法校验位计算... def checkDigit calculateLuhn(digits) vars.put(validCard, cardNo checkDigit)实操心得JSR223脚本中vars线程变量和propsJVM全局属性必须严格区分。vars在线程间隔离适合存储用户私有数据props跨线程共享适合存储全局配置如API Base URL。曾因误用props.put(token, ...)导致100个线程共用同一Token引发并发修改冲突。3.2 关联从正则到JSON Extractor提取逻辑的精度演进关联Correlation是接口测试的生命线。早期用正则提取HTML现在90%以上是JSON API必须升级工具链。正则提取器Regular Expression Extractor适用场景遗留系统返回HTML片段、XML响应、或JSON中嵌套极深的字段。例如提取input typehidden namecsrf_token valueabc123中的value。关键参数Apply to选择“Main sample and sub-samples”以捕获重定向后的响应。Match No.0表示随机匹配-1表示全部匹配返回数组。生产脚本严禁用0必须用1指定首匹配避免随机性。JSON Extractor推荐首选语法简洁性能远超正则。路径表达式支持$.data.token提取根对象data下的token字段$..id递归搜索所有id字段慎用性能差$.[?(.status success)].orderIdJSONPath条件过滤某次测试订单查询接口响应结构为{code:0,msg:ok,data:{order_id:ORD123}}。新手常写$.data.order_id但若接口异常返回{code:1,msg:error}该表达式返回空导致后续断言失败。正确做法是先用$.code提取code再用If Controller判断${code} 0成立后再提取order_id。3.3 断言超越“响应码200”构建多维度验证矩阵断言不是“检查HTTP状态码”而是构建质量验证矩阵。单一断言等于没断言。响应断言Response Assertion基础但易错。Pattern Matching Rules选Contains时若填success会匹配{status:success}和{status:failed}因failed含success子串。必须选Equals或Substring并确保模式精确。JSON断言JSON Assertion验证JSON Schema合规性。例如要求$.data.items[*].price必须为数字类型{ schema: { type: array, items: { type: object, properties: { price: {type: number} } } } }JSR223断言终极武器当需要复杂逻辑时启用。例如验证返回的订单时间戳必须在当前时间±5分钟内import java.time.Instant def now Instant.now().toEpochMilli() def orderTime vars.get(orderTime).toLong() def diff Math.abs(now - orderTime) if (diff 300000) { Failure true FailureMessage Order time ${orderTime} is out of 5-min window (now: ${now}) }踩坑实录某次测试发现“断言全通过但业务实际失败”。排查发现接口返回{code:0,data:null}JSON断言只校验了code0未检查data是否为空。后续所有脚本强制增加“JSR223断言”if (vars.get(data) null) { Failuretrue }。3.4 逻辑控制If Controller与While Controller的业务语义落地逻辑控制器让脚本具备“思考能力”但必须赋予清晰的业务语义。If Controller不是“if ab”而是“if 登录成功 then 执行支付”。条件表达式必须用JMeter函数语法${JMeterThread.last_sample_ok}上一个取样器是否成功或${code} 0变量code值为0。注意比较字符串eq比较数值。While Controller实现“重试直到成功”或“轮询状态”。条件填${__javaScript(${status} ! SUCCESS)}。关键点循环体内必须有状态更新逻辑如再次调用查询接口否则陷入死循环。某次测试异步任务用While Controller轮询任务状态但忘记在循环内添加新的HTTP取样器导致线程卡死。模块控制器Module Controller解决脚本复用难题。将登录逻辑封装为独立线程组用Module Controller在任意位置调用。比复制粘贴更安全修改一处全局生效。4. 从功能验证到生产压测配置、监控、报告的工业化实践当脚本通过功能验证下一步是进入生产级压测。这不再是“跑起来就行”而是要建立可观测、可归因、可复现的压测体系。核心挑战在于如何让JMeter输出的数据真正反映后端服务的真实瓶颈4.1 压测配置的黄金参数Ramp-Up、持续时间与吞吐量控制压测不是“把线程数拉满”而是模拟真实流量曲线。错误配置会导致结论完全失真。Ramp-Up Period预热时间必须覆盖应用JVM的JIT编译、连接池填充、缓存预热周期。电商大促压测我们实测Tomcat应用需至少60秒预热才能达到稳定TPS。若Ramp-Up设为5秒前10秒TPS爬升缓慢后90秒才达峰值此时统计的“平均TPS”毫无意义。正确做法Ramp-Up ≥ 应用冷启动时间且在报告中剔除预热期数据。持续时间与调度使用“Scheduler”时Duration持续时间和Startup delay启动延迟需协同。例如Startup delay30s, Duration600s表示30秒后开始压测持续10分钟。但若线程组Ramp-Up100s则实际压测窗口只有500秒600-100而非600秒。必须手动计算有效压测时长 Duration - Ramp-Up。吞吐量限制Constant Throughput Timer当需精确控制QPS时使用。注意它作用于整个线程组且单位是“每分钟请求数”。若要限制为100 QPS需填6000100×60。更重要的是它通过动态调节线程休眠时间实现因此实际线程数必须≥目标QPS否则无法达到。例如目标100 QPS线程数仅设50则最大TPS50。4.2 监控体系不止看JMeter更要盯住被测系统JMeter报告只是表象真正的瓶颈在被测系统。必须建立双视角监控JMeter侧监控Active Threads Over Time确认并发是否按预期增长。若曲线平缓说明线程阻塞如DNS解析慢、连接池耗尽。Response Times Over Time观察响应时间拐点。当TPS提升RT陡增即为容量拐点。Errors %错误率突增是系统过载的最早信号。被测系统侧监控必须接入JVMGC overhead 5%、heap usage 85%、thread count 500均预示风险。数据库connection wait time 100ms、slow query count激增、buffer hit ratio 95%。中间件Redisused_memory_rss突增、Kafkaunder replicated partitions 0。某次压测中JMeter报告显示TPS稳定在2000RT200ms但业务方反馈下单失败率15%。我们接入Arthas发现Dubbo线程池activeCount达200/200所有请求在队列等待。根源是Dubbo线程池大小配置为200而JMeter并发2000远超承载能力。结论压测必须同步监控中间件线程池而非只看HTTP层。4.3 报告解读从Aggregate Report到PerfMon的深度归因“聚合报告”只能告诉你“哪里慢”“PerfMon”才能告诉你“为什么慢”。Aggregate Report核心指标释义90% Line非P90是响应时间排序后第90%位置的值。样本量1000时参考价值低。Throughput单位时间完成的请求数requests/sec非网络吞吐量。Received KB/secJMeter接收响应体的速率反映网络或客户端瓶颈。PerfMon Server Agent部署要点必须与被测应用部署在同一物理机避免网络延迟干扰。Agent端口如4444需在被测服务器防火墙放行。JMeter中添加Backend Listener选择influxdb配置influxdbUrlhttp://influxdb:8086/write?dbjmeter。关键归因路径当TPS下降时按此顺序排查JMeterActive Threads是否下降→ 若是检查JMeter机器CPU/内存是否打满。PerfMonCPU Usage是否90%→ 是应用代码或GC问题。PerfMonMemory Used是否持续增长→ 是内存泄漏。PerfMonDisk I/O Wait是否50%→ 是数据库或日志写入瓶颈。PerfMonNetwork In/Out是否饱和→ 是网卡或负载均衡器瓶颈。实操技巧在JMeter中添加JSR223 Timer在每次请求前记录系统时间戳请求后计算差值与JMeter内置RT对比。若自定义RT显著大于内置RT说明瓶颈在JMeter客户端如DNS解析、SSL握手若基本一致瓶颈在服务端。5. CI/CD集成与脚本治理让接口测试成为研发流水线的守门员脚本若不能融入CI/CD就只是玩具。真正的工业化是让JMeter测试像单元测试一样在每次代码提交后自动运行并在失败时精准定位到具体接口、具体断言、具体数据。5.1 Jenkins Pipeline集成从手动执行到自动门禁Jenkins是当前最成熟的CI/CD平台集成JMeter需解决三个问题环境隔离、结果归档、失败通知。Pipeline脚本核心段pipeline { agent any environment { JMETER_HOME /opt/jmeter TEST_PLAN api-test.jmx RESULTS_DIR results } stages { stage(Prepare) { steps { sh ${JMETER_HOME}/bin/jmeter -n -t ${TEST_PLAN} -l ${RESULTS_DIR}/result.jtl -e -o ${RESULTS_DIR}/report } } stage(Report) { steps { publishHTML([ allowMissing: false, alwaysLinkToLastBuild: true, keepAll: true, reportDir: ${RESULTS_DIR}/report, reportFiles: index.html, reportName: JMeter Test Report ]) } } stage(Quality Gate) { steps { script { def result sh(script: ${JMETER_HOME}/bin/jmeter -g ${RESULTS_DIR}/result.jtl -o ${RESULTS_DIR}/summary, returnStatus: true) if (result ! 0) { error JMeter summary generation failed } // 解析summary.csv检查错误率 def csv readFile(${RESULTS_DIR}/summary/Statistics.csv) if (csv.contains(Error %,10.0)) { // 错误率10% error Error rate exceeds threshold } } } } } }关键设计点-n非GUI模式必选。-l指定结果文件.jtl这是后续报告的基础。-e -o生成HTML报告但仅用于人工查看不可作为质量门禁依据因HTML报告是静态快照无法程序化解析。质量门禁必须基于.jtl或-g生成的Statistics.csv因其结构化、可编程。5.2 脚本治理版本化、模块化、文档化三位一体没有治理的脚本半年后无人敢动。我们推行“三化”原则版本化Git管理.jmx文件必须提交Git禁止二进制提交。JMeter 5.0已支持文本化保存XML格式可diff。分支策略main存稳定脚本feature/xxx存新接口测试hotfix/xxx存紧急修复。模块化Test Fragment Module Controller将通用逻辑登录、Token刷新、公共Header封装为Test Fragment。每个业务域如订单、支付、会员建立独立线程组用Module Controller按需调用。好处修改登录逻辑只需更新一个Fragment所有业务脚本自动生效。文档化README.md 内置注释每个.jmx文件同目录下必须有README.md包含接口清单URL、Method、关键参数数据准备说明如需预置用户ID范围断言规则如“code必须为0data不为空”在JMeter中使用Comment元件在关键取样器旁添加业务注释。经验教训某次大促前因未文档化“支付接口需传X-Channel-Id头”新同学调试时遗漏该头导致所有支付请求被风控拦截。此后强制要求所有Header、Query Param、Body字段必须在README中列出并标注是否必填。5.3 故障归因从JTL日志到根因定位的完整链路当CI流水线中JMeter测试失败工程师最需要的是30秒内知道是哪个接口、哪条断言、哪组数据出了问题。这需要日志、报告、脚本三者联动。JTL日志结构解析.jtl是CSV格式关键字段timeStamp毫秒时间戳elapsed响应时间mslabel取样器名称responseCodeHTTP状态码responseMessage响应消息successtrue/falsefailureMessage失败原因断言失败时填充自动化归因脚本Python示例import pandas as pd df pd.read_csv(result.jtl) failed df[df[success] false] if not failed.empty: # 按label分组统计失败次数 fail_summary failed.groupby(label).size().sort_values(ascendingFalse) print(Top failing endpoints:) print(fail_summary.head(3)) # 取第一个失败样本打印详细信息 first_fail failed.iloc[0] print(f\nFirst failure detail:) print(fEndpoint: {first_fail[label]}) print(fResponse Code: {first_fail[responseCode]}) print(fFailure Message: {first_fail[failureMessage]})与ELK集成进阶将JTL日志推送至Logstash索引到ElasticsearchKibana中创建Dashboard按label聚合失败率趋势图点击高失败率接口下钻查看failureMessage词云关联同一timeStamp的APM链路追踪如SkyWalking定位服务端慢SQL或远程调用超时这套体系上线后CI中JMeter失败的平均定位时间从原来的47分钟缩短至3.2分钟。6. 高级实战处理OAuth2、WebSocket、文件上传等特殊场景标准HTTP测试只是起点。真实项目中你会频繁遭遇OAuth2鉴权、实时消息推送WebSocket、大文件上传等“非标”场景。这些不是JMeter的短板而是考验你是否真正理解协议本质。6.1 OAuth2.0授权码模式绕过浏览器直取TokenOAuth2.0的授权码模式Authorization Code Flow本需浏览器重定向但JMeter可通过模拟授权服务器交互实现全自动化。核心步骤获取授权码Authorization Code发起GET请求https://auth-server/oauth/authorize?client_idxxxredirect_urihttps://callbackresponse_typecodescopeall服务器返回302Location头含codeabc123statexyz用正则提取器捕获code换取Access Token发起POST请求https://auth-server/oauth/tokenBodygrant_typeauthorization_codecode${code}redirect_urihttps://callbackclient_idxxxclient_secretyyy响应JSON中提取access_token在后续请求中使用Header添加Authorization: Bearer ${access_token}关键细节授权码模式中redirect_uri必须与注册时完全一致包括末尾斜杠否则授权服务器拒绝。某次测试因redirect_urihttps://callback与注册的https://callback/不匹配始终返回invalid_request。务必在OAuth管理后台确认注册URI。6.2 WebSocket测试用WebSocket Samplers插件突破HTTP局限JMeter原生不支持WebSocket需安装JMeter WebSocket Samplers插件由Peter Doornbosch开发。安装后新增WebSocket Open Connection、WebSocket Single Write Sampler、WebSocket Read Sampler等元件。典型测试流WebSocket Open Connection连接ws://echo.websocket.orgWebSocket Single Write Sampler发送文本{action:subscribe,channel:orders}WebSocket Read Sampler等待服务器推送设置Max Wait Time5000msJSON Extractor从推送消息中提取channel字段Response Assertion验证channel等于orders注意事项WebSocket连接是长连接WebSocket Open Connection必须放在Setup Thread Group中确保每个线程只连一次。WebSocket Read Sampler的Close Connection选项若勾选读取后立即断开无法进行后续交互。通常应取消勾选。6.3 大文件上传突破100MB限制与内存优化上传大文件如1GB视频时JMeter默认会将整个文件读入内存导致OOM。必须启用流式上传。配置步骤在HTTP取样器中Files Upload区域勾选Use multipart/form-data for POST。在Send Files With the Request表格中填写File Path:/path/to/large-file.mp4Parameter Name:file与后端约定的字段名MIME Type:video/mp4最关键在JMeter的jmeter.properties中修改httpsampler.max_buffer_size104857600 # 100MB缓冲区 httpsampler.file_upload_modeHTTPCLIENT4 # 强制使用HttpClient4支持流式验证流式生效启动JMeter时添加JVM参数-XX:PrintGCDetails运行上传脚本观察GC日志。若无Full GC且Used Heap稳定在200MB内说明流式生效若出现OutOfMemoryError说明仍在内存加载。实战技巧上传前用JSR223 PreProcessor计算文件MD5上传后调用校验接口验证文件完整性。代码def file new File(vars.get(filePath)) def md5 file.bytes.encodeHex().toString() vars.put(fileMd5, md5)7. 性能调优与避坑指南让JMeter自身不成为瓶颈当你的压测目标是5000 TPSJMeter客户端自身的性能就成了天花板。我们曾因忽略这点将服务端瓶颈误判为“JMeter能力不足”浪费两天排查时间。7.1 JMeter客户端调优从JVM参数到元件精简JVM参数jmeter.bat/.sh中修改set HEAP-Xms4g -Xmx4g set NEW-XX:NewSize1g -XX:MaxNewSize1g set SURVIVOR-XX:SurvivorRatio16 set TENURING-XX:MaxTenuringThreshold2-Xms与-Xmx设为相等避免运行时扩容。新生代设为1g确保大对象如10MB响应体直接进入老年代减少Minor GC。SurvivorRatio16Eden:Survivor16:1增大Eden区降低GC频率。元件精简原则删除所有监听器压测时监听器是性能杀手。禁用断言功能测试用压测时仅保留Response Assertion检查200。关闭结果保存若无需详细日志去掉-l result.jtl参数TPS可提升15%。减少JSR223使用Groovy脚本比Java慢3倍。能用内置函数如__Random的绝不用脚本。7.2 分布式压测Master-Slave架构的可靠性保障单机JMeter极限约2000线程受操作系统socket限制。突破需分布式。部署要点Master与Slave必须时间同步NTP服务否则timeStamp错乱聚合报告失效。Slave机器需关闭防火墙开放1099RMI registry和2000-2010RMI server端口。Master的jmeter.properties中remote_hosts192.168.1.10:1099,192.168.1.11:1099 server.rmi.localport2000常见故障与修复故障1Connection refused原因Slave的jmeter-server未启动或防火墙拦截。修复./jmeter-server -Djava.rmi.server.hostname192.168.1.10故障2Master报告中Active Threads为0原因Master与Slave的JMeter版本不一致。修复所有节点统一版本如5.6.3且lib/ext目录下插件完全一致。故障3结果聚合不全原因Slave生成的.jtl文件未回传Master。修复在Master的jmeter.properties中client.rmi.localport2001确保回传端口畅通。7.3 终极避坑清单那些让你加班到凌晨的隐藏雷区以下是我用2700万次测试换来的血泪清单每一条都对应一次真实故障Cookie Manager的Domain匹配陷阱当服务器返回Set-Cookie: tokenabc; Domain.example.com而JMeter请求api.example.com时Cookie会被自动附加但若请求www.example.com则不会。必须确保请求域名与Cookie Domain完全匹配或在Cookie Manager中勾选Clear cookies each iteration强制重置。JSON Extractor的空值处理若JSON路径不存在Default Value会被赋给变量但该变量值为字符串Default Value而非null。后续If Controller中${var} 永远为false。正确写法${var} Default