资讯动态

JMeter脚本助手Skill:让AI生成BeanShell与Groovy压测片段

发布时间:2026/10/17 5:10:49 来源:尧图企业网站定制
做接口压测这些年我在 JMeter 里写过的脚本片段加起来能绕线程组转三圈。BeanShell 断言、JSR223 前置处理器、Groovy 签名、参数化文件读取——每一样都是现用现搜或者从旧项目里翻出来改。时间长了你会发现真正花时间的不是压测本身而是这些零碎脚本的编写和调试。后来我换了个思路把这些脚本助手整理成一个可复用的 Skill让 AI 直接照着写我只需要负责审核和组装。这个 Skill 不是玄学也不是把一堆脚本打个包就完事。它是一套结构化的技能定义告诉 AI 助手在什么场景下输出什么类型的 JMeter 脚本片段包含哪些模板、遵守哪些规则、避开哪些坑。做完之后我在项目里让 AI 生成 beanshell 断言、生成签名逻辑、生成随机测试数据基本都是一次成型改几行就能跑。这篇文章就把我的完整做法、踩过的坑、以及怎么把它做成团队可复用的资产全部拆开讲清楚。1. 为什么要把脚本助手做成了 Skill1.1 散装脚本的痛做过压测的人都懂先说说我最早的状态。本地有一个文件夹叫 jmeter_scripts里面按功能存了几十个 .txt 和 .jmx 文件什么“登录态提取.jmx”“MD5签名.txt”“CSV参数化.txt”。看着挺全但真到用的时候问题一个接一个。首先是找得慢。你记得自己写过“一个提取 token 的正则”但想不起来放在哪个文件里只能挨个打开翻。其次是改得乱。旧脚本里绑定的变量名、参数名跟新接口对不上复制过去之后一堆报错。第三是没法保证质量。很多脚本是当年在某个特定版本、特定压测场景下写的换了个 JMeter 版本或者换个接口风格直接跑就失败。我意识到散装脚本的本质问题是有内容没结构。它们沉淀了经验但没有沉淀“什么时候用哪段、怎么改、为什么这么写”。这些东西全在我脑子里AI 帮不上忙同事也用不上。1.2 Skill 到底是什么跟普通脚本库有什么区别后来我开始用 AI 辅助写压测脚本遇到一个新问题AI 不是不会写而是太爱自由发挥。你问它“帮我写个 BeanShell 断言”它给你写一个能跑的你再问一次它给你写一个风格完全不同的。变量名一会儿 snake_case 一会儿驼峰JSON 解析一会儿用 fastjson 一会儿用内置 JSONObject。每次都要人工校对等于没省时间。Skill 解决的就是这个问题。它不是脚本本身而是给 AI 的一套“操作手册 模板库”。它告诉 AI你遇到这类请求时优先使用哪些模板输出代码必须包含什么不允许出现什么变量名统一用什么风格。AI 还是那个 AI但你给了它一个明确的边界和工作方式产出就稳定了。打个比方普通脚本库等于扔给你一堆零件Skill 等于给了一张图纸外加装配要求和质检标准。我封装的就是这样一套东西一面写“怎么做”一面写“不许怎么做”。注意Skill 和 Agent 是两码事。Skill 是被调用的静态规则集Agent 是能自主规划的执行体。这里做的不是 Agent不需要常驻决策只是让 AI 在收到指令时按照固定规范产出脚本。轻量、可控、没有额外的模型调用开销这才是 Skill 适合做脚本助手的核心理由。2. Skill 里都放了哪些核心内容2.1 BeanShell 断言压测场景里最刚需的一类整个 Skill 里断言脚本的模板数量最多使用频率也最高。因为压测时最烦的不是造请求而是写断言——尤其当你压的是内部系统接口返回一堆 JSON你只要判断其中某个业务字段是否正常。我在 Skill 里内置了三个层级的断言模板按需选用基础层判断 HTTP 响应码和响应体是否为空。适合冒烟场景。业务层解析 JSON 并判断业务 code。这类最重要因为 HTTP 200 不代表接口成功很多系统业务失败时也返回 200。数据层从响应体中提取指定字段与预期值比对。适合下单、查询等有明确返回值的接口。业务层的模板长这样import org.apache.jmeter.assertions.AssertionResult; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; String body prev.getResponseDataAsString(); if (body null || body.isEmpty()) { Failure true; FailureMessage 响应体为空; } else { try { JSONObject obj JSON.parseObject(body); if (obj.getIntValue(code) ! 0) { Failure true; FailureMessage 业务失败: obj.getString(msg); } } catch (Exception e) { Failure true; FailureMessage JSON解析失败: e.getMessage(); } }这里有一个值得注意的细节模板里用的是prev.getResponseDataAsString()而不是prev.getResponseData()。后者在 JMeter 5.x 里返回 byte 数组处理中文时经常出现编码问题前者直接拿到字符串配合 UTF-8 就不会乱码。这个坑我在早期踩过好几次所以直接写死在模板注释里AI 生成时不会跑偏。2.2 参数处理脚本随机数、时间戳、加解密签名接口压测跟功能测试最大的不同是请求参数不能是写死的常量。每次请求都要不同的手机号、时间戳、随机字符串有时还要按照接口规范做签名。这个工作量手写几次还行每次都让 AI 从零写就太浪费了。Skill 里内置的参数处理模板按用途分成几类随机数据生成。包括随机手机号、身份证号、订单号前缀 时间戳、UUID、指定位数的随机字符串。这些全部封装成短小的代码块AI 在生成过程中可以直接引用。时间戳处理。压测里经常遇到“当前时间戳 N 天”这种需求。脚本里直接给出System.currentTimeMillis()、Calendar加减、以及格式化输出的写法避免 AI 每次瞎写导致时区错乱。加密签名。这是重头戏。现在接口基本都要验签HMAC-SHA256 是最常见的。Skill 内置了标准模板import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; import org.apache.commons.codec.binary.Hex; String secret ${sign_secret}; long ts System.currentTimeMillis() / 1000; String data appIdtestts ts; Mac mac Mac.getInstance(HmacSHA256); SecretKeySpec keySpec new SecretKeySpec(secret.getBytes(UTF-8), HmacSHA256); mac.init(keySpec); byte[] raw mac.doFinal(data.getBytes(UTF-8)); String sign Hex.encodeHexString(raw); vars.put(timestamp, String.valueOf(ts)); vars.put(sign, sign);这段脚本放在 JSR223 前置处理器里用 Groovy 语言执行。为什么用 Groovy 不用 BeanShell因为签名涉及加密库Groovy 在 JMeter 5.x 里默认支持更好import 路径不会因为 JDK 模块化出问题。这个选型也写在了 SKILL.md 的规则里AI 不会自己去选 BeanShell。2.3 前置处理与后置提取让脚本串起来单接口压测用不着太多前置后置但一旦做业务流程压测——比如“登录 - 下单 - 支付”这种串行链路——脚本之间的参数传递就成了关键。Skill 内置了后置提取的两大模板正则提取器和 JSONPath 提取器。常规做法是前者匹配任意文本后者专门处理 JSON。这里我要求 AI 在生成时优先推荐 JSONPath因为 JSON 结构比正则好维护得多尤其接口文档一更新正则大概率就废了JSONPath 改起来快很多。提取到变量之后下游接口直接用${token}引用这个约定也写进了 Skill 规则。还有一个容易踩的坑正则表达式里如果遇到$、.、\这些字符必须转义否则提取结果永远是空。模板注释里专门加了一行提醒。{ token: $.data.token, orderId: $.data.orderId }3. 从 0 到 1 构建一个可用的 Skill3.1 Skill 的目录结构与命名规范如果你只是想“让 AI 帮我写 JMeter 脚本”那直接写一个长提示词就够了。但要做“可复用的 Skill”必须有一套稳定的目录结构和命名规范不然用几次就变成新的“散装脚本库”。我的目录结构长这样jmeter-script-assistant/ ├── SKILL.md ├── templates/ │ ├── assertion/ │ │ ├── http_status.bhs │ │ ├── business_code.bhs │ │ └── field_compare.bhs │ ├── generator/ │ │ ├── random_mobile.groovy │ │ ├── timestamp_cal.groovy │ │ ├── hmac_sha256.groovy │ │ └── base64_codec.groovy │ ├── extractor/ │ │ ├── jsonpath_extract.groovy │ │ └── regex_extract.bhs │ └── common/ │ └── variable_conventions.md └── examples/ ├── business_flow_demo.jmx └── assertion_demo.jmx命名规范有两个要点一是按功能分类而不是按接口分类二是文件和其中的代码必须具备“可迁移性”——不写死某个业务的变量名、接口地址、数据库连接串全部用${}占位符表达。提示Skill 目录里不放 .jmx 压测工程文件或者只放 demo 级别的简化示例。原因是 .jmx 是 XML 格式AI 修改它很容易破坏结构而且里面绑定了具体的线程数、监听器、环境信息迁移成本太高。Skill 的核心产出是脚本片段不是完整压测工程。3.2 SKILL.md 怎么写才有效SKILL.md 是整个 Skill 的灵魂。它不是说明书而是给 AI 的“行为准则”。我前后改了很多版最后发现有效的 SKILL.md 不需要长篇大论但必须包含四个板块能力边界、输出规则、模板索引、禁忌清单。能力边界是告诉 AI 这个 Skill 管什么、不管什么。比如“本 Skill 只负责生成 JMeter 脚本片段不负责搭建压测环境、不负责分析压测报告”。没有边界AI 就会什么你都让它干最后 Skill 逐渐膨胀成一坨分不清职责的杂物。输出规则要落到非常细的操作层面。我写的规则包括脚本必须包含完整 import变量名统一使用 lowerCamelCase线程组级别的变量用${var}引用不能用vars.get(var)时间戳一律使用毫秒并显式标注单位涉及中文时统一使用 UTF-8 声明。模板索引是给 AI 的目录指针。AI 在收到“写个签名”这样的指令时应该先去templates/generator/下找 hmac_sha256.groovy而不是凭空创作。这一点非常重要Skill 的模板库是“参考答案”AI 的默认行为是“自己写”必须用索引把 AI 引导到参考答案上。禁忌清单同样关键。我列的最多的两条不允许使用System.out.println()作为输出方式必须使用log.info()不允许在 BeanShell 中用 JDBC 连接字符串方式拼 SQL因为 JMeter 的变量替换时机导致 SQL 里的引号永远转义不对。下面是 SKILL.md 的核心模板骨架# JMeter 脚本助手 ## 功能边界 - 生成与 JMeter 相关的脚本片段包括断言、参数化、签名、提取 - 提供可直接粘贴到 JMeter 中的完整脚本包含 import 与注释 - 不负责生成压测方案、不负责分析性能瓶颈 ## 输出规则 - 脚本使用 Groovy 或 BeanShell优先 GroovyJMeter 5.x 内置支持 - 所有业务可变项使用 ${} 占位符 - 中文输出统一 UTF-8禁止硬编码编码格式 - 每个脚本必须包含使用说明注释适用场景、依赖项、修改项 ## 模板索引 - 断言模板templates/assertion/ - 数据生成模板templates/generator/ - 参数提取模板templates/extractor/ ## 禁忌清单 - 禁止使用 System.out.println 输出 - 禁止在 JSR223 中直接操作数据库连接 - 禁止在 BeanShell 中声明与 JMeter 内置函数同名的方法 - 禁止输出没有 import 的脚本片段3.3 必备的验证清单Skill 做完不是直接丢给 AI 就完事还要经过一套验证流程确保 AI 基于这个 Skill 生成的脚本确实能用。我自己验证时会拿一份真实接口文档让 AI 按 Skill 生成脚本然后逐个跑一遍。验证清单里最核心的几条脚本能否被 JMeter 5.x 直接解析执行。特别检查 Groovy 引擎版本是否匹配import 路径是否存在。断言失败时能否给出明确的错误信息。很多 AI 生成的断言失败就是一句 Assertion failed根本不知道哪里错了。Skill 要求失败时必须带有实际值和预期值。中文字符是否乱码。在响应体断言场景里如果prev.getResponseDataAsString()用错返回结果全是问号。变量作用域是否正确。线程组之间传递的变量和线程组内传递的变量在 JMeter 里作用域完全不一样写错就取不到值。这些验证不是每次都要跑全量但至少在 Skill 的初版和新模板加入时必须完整过一遍。版本一多你就会发现脚本模板最大的风险不是写不出来而是写出来跑不通又因为“看着对”而直接进到压测里最后整个测试结果作废。4. 让 Skill 真正融入到工作流里4.1 接入 AI 助手的方式具体怎么接入每个 AI 编程助手的方式不太一样。有些支持在项目根目录放一个说明文件作为全局上下文有些支持单独引用某个 Skill 文件还有一些支持把 Skill 做成插件在对话里直接激活。我的做法是通用优先把 SKILL.md 放在项目根目录让 AI 助手在首次交互时就加载它。在文件顶部写一行“本仓库使用 JMeter 脚本助手 Skill所有生成的压测脚本必须遵守 SKILL.md 规范”这样整个对话过程都会受到约束。如果你用的工具支持多文件上下文还可以把 templates 目录作为附属资料让 AI 读取。有一个小技巧是把模板文件本身写成“带使用说明的代码”而不仅仅是裸代码。比如 hmac_sha256.groovy 开头几行用注释说明适用场景、需要修改的参数、输出变量名这样 AI 在读取模板时能更快理解使用方法。接入之后我的日常使用模式变成先在对话里描述接口场景AI 根据 Skill 自动生成脚本片段我审查一遍关键逻辑后贴进 JMeter跑一次验证没问题再进入正式的压测计划。原来一个带签名的接口从写脚本到跑通大概要半小时现在压缩到五到十分钟而且基本不用返工。4.2 让 Skill 成为团队资产的可复用经验单机版 Skill 是自嗨能给别人用才是资产。要让 Skill 真正可复用有几个经验非常关键。第一个经验是把 Skill 当成代码来维护。放入 Git 仓库每次更新都要写提交说明谁改了模板、为什么改都有迹可查。否则团队里几个人各自往里面加模板最后 Skill 膨胀得连 AI 都找不到重点。第二个经验是“模板贵精不贵多”。每次加模板之前先问自己这个场景是不是足够通用如果只是某个项目的一次性需求那应该写到项目的 JMeter 工程里而不是进 Skill。Skill 里每多一个模板AI 在匹配时的选择成本就高一分。第三个经验是版本与 JMeter 强相关。Skill 的 README 里明确标注“适用于 JMeter 5.4 及以上版本”遇到升级 JMeter 大版本时要重新跑一遍验证清单。最典型的例子是 JMeter 4 到 5 迁移时很多 BeanShell 库的加载方式变了不更新 Skill 的话 AI 生成的旧式脚本就会全部报错。DISCLAIMER不同 AI 产品对 Skill 的加载机制各不相同迁移到新工具时不要强行照搬目录先确认新的工具是否支持自定义上下文或技能文件。规则内容可以复用但加载方式要看工具文档。5. 常见问题与排查技巧实录5.1 高频问题速查表在使用这个 Skill 的过程中我和团队成员至少遇到过二十多种问题。这里把最高频的几类整理成一个速查表按“症状 - 原因 - 处理”的顺序排列方便以后直接查症状可能原因处理方式断言一直失败但接口返回正常脚本里用的 JSON 解析库没引入或者引错了包检查 import 是否完整BeanShell 下确认处于 JSR223 而不是 BeanShell SamplerresponseDataAsString 中文乱码JMeter 默认编码不对或脚本里用了 getResponseData() 数组统一用 getResponseDataAsString()HTTP 测试计划采样器设置 UTF-8生成的签名值服务端验签失败时间戳单位不一致签名原文拼接顺序错误确认是毫秒还是秒用 log.info 打印签名原文逐一核对提取出来的变量在下一个请求取不到变量作用域写错或者后置处理器名称写错检查 JSONPath 后置处理器放在取样器子节点还是同一层级压测跑了一半脚本报编译错误多个线程同时使用静态变量导致的问题将静态变量改为 ThreadLocal或在线程组中设置独立变量AI 生成的脚本每次都不同Skill 规则没加载或者提示词里没引用 SKILL.md重新确认 SKILL.md 是否在工作目录并在对话中明确“使用 JMeter 脚本助手 Skill”5.2 更隐蔽的坑以及我总结的避坑技巧速查表里的问题比较基础下面这几个才是真正让人头痛的坑。关于 JSR223 和 BeanShell 的选择我的结论是能用 Groovy 不用 BeanShell。BeanShell 在 JMeter 中的表现很不稳定尤其是在 Java 17 以上的 JDK 环境下反射访问受限导致内置变量vars、prev经常不可用。Groovy 从 JMeter 5.x 开始就是默认推荐语言内置支持好语法也更接近 Java。Skill 里的模板除了几个历史遗留的断言外新模板全部换成了 Groovy 版本。另一个容易忽略的细节是 JMeter 内置函数的断言时机。__time()这类函数在脚本执行前就已经被替换成字符串所以你在 BeanShell 里引用${__time()}时拿到的可能是一个旧值。更可靠的做法是在脚本内部用System.currentTimeMillis()获取时间再通过vars.put()存到变量里供其他请求使用。这个坑在压测高并发场景下非常致命因为${__time()}在每个线程里的取值时机会漂移。多线程共享变量的坑也值得单独拎出来说。JMeter 的vars对象是线程独立的这点很多人知道但如果你在一个 BeanShell 里写了static变量来缓存数据那就等于所有线程共享一份。高并发下面是典型的“看起来能用跑起来全错”。压测一启动多个线程同时写同一个静态变量后面的请求全用到被覆盖的数据。Skill 里我专门加了一条规则JSR223 脚本中禁止使用static关键字定义可变状态。结语做这个 Skill 的过程本质上是一次“把自己的经验结构化”的练习。以前我说“我有一堆 JMeter 脚本”现在我可以明确地说我有一套规则、一堆模板、一组禁忌AI 照着它能稳定地产出可用的脚本。这个转变带来的效率提升是实打实的但我更看重的是另一件事——这套东西从我的个人经验变成了团队能共同维护的资产。新同事上手压测不再需要靠翻我的聊天记录找脚本我自己的工作量也从“写代码”变成了“审代码和丰富模板”。如果你也在做接口压测我建议从最小版本开始别一上来就搞大而全的 Skill。先把你最常用的三类脚本收进去写好 SKILL.md用两三个真实接口验证一遍然后边用边加。过程中你一定会发现真正有价值的不是那段脚本本身而是你在 SKILL.md 里写下的那些“为什么”。每次有人踩坑就补一条规则进去。半年之后回头看这个 Skill 沉淀的东西比你自己脑子里记得的要多得多。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑