用 AI 选工具与生成压测脚本
压测工具选型不靠偏好,靠五个输入:协议(Protocol)、接口(Interface)、鉴权方式(Auth)、目标 QPS、数据来源(Data)。五项给全,AI 能选出合适工具并写出可直接运行的脚本;给不全,它会给你语法正确、参数全错的命令——压测脚本的错误不像单元测试那样立刻红灯,它会安静地压出一个假数字。
一、五个必给的输入
| 输入 | 要给到什么粒度 | 缺了会怎样 |
|---|---|---|
| 协议 | HTTP/1.1、HTTP/2、gRPC、WebSocket、原始 TCP 帧 | 用 wrk 去压 WebSocket,或给 gRPC 生成 cURL 命令 |
| 接口 | 方法、路径、请求体字段与取值分布、响应结构 | 字段名靠猜,请求全 400,却被记成「服务端拒绝」 |
| 鉴权 | token 从哪来、放 header 还是 query、有效期多长 | 跑 30 秒后大量 401,被误读成服务端不稳定 |
| 目标 QPS | 目标值、并发上限、是否限速 | 生成不限速脚本,压测机自己先打满 |
| 数据来源 | 固定夹具、CSV 文件、还是运行时随机生成 | 所有请求打同一个 uid,命中缓存后得出「性能很好」的假结论 |
二、工具对比:先按协议筛,再按场景选
| 工具 | 适用 | 短板 |
|---|---|---|
ab | 单 URL 的 HTTP/1.1 快速冒烟 | 只支持 HTTP/1.1,不能压长连接,无脚本化多请求,只给平均值 |
hey | ab 的替代品,支持 HTTP/2 与请求头定制 | 无脚本能力,做不了参数化与多接口串联 |
wrk | 高并发 HTTP 接口吞吐,Lua 脚本可参数化 | 压不了 WebSocket/gRPC;Lua 生态小,分位要自己算;压测机容易先到瓶颈 |
k6 | 场景化压测(阶段曲线、阈值断言、CI 集成),HTTP 与 WebSocket 都支持 | 单机连接数受运行时限制,十万级长连接不适合;JS 运行时吞吐不如原生 |
Locust | 复杂业务链路、Python 生态、分布式压测 | 单机吞吐偏低,协程模型在高 QPS 下容易压测机先到极限 |
JMeter | 图形化编排、协议覆盖面广、团队已有资产 | 资源占用高,脚本是 XML 不便评审与入库,CI 维护成本高 |
ghz | gRPC 专用,proto 直连、metadata、限速与流式调用 | 只压 gRPC,不支持跨协议的业务链路编排 |
选型顺序:先按协议划掉不可能的,再看要不要阶段曲线与阈值断言(要就选 k6),最后看规模——十万级长连接只能自研客户端。
三、提示词模板
角色:性能测试工程师。只输出可执行方案,不解释压测原理。
被测对象:gRPC 服务 im.v1.MsgService/Send,proto 在 ./proto/im.proto
协议:gRPC over HTTP/2,明文端口 10.0.0.12:9090
鉴权:metadata 里带 authorization: Bearer <token>;启动前用登录接口批量取 200 个 token 轮换
目标:稳定 8000 QPS,并发上限 200,P99 < 100ms,错误率 < 0.1%,持续 5 分钟
数据来源:./users.csv 每行一个 uid,请求体 uid 取自 CSV,content 固定 200 字节文本
输出要求:
1) 工具选型与理由,一句话,不要给多个候选
2) 压测脚本完整内容,标明文件路径
3) 运行命令,逐条可直接复制
4) 结果中必须读取的字段清单,每个字段给含义与合格线
5) 单独一节「待确认」,列出你不确定的参数名、默认值与版本差异,不要编造
第 5 条是关键:明确要求它标注不确定的参数,比事后逐个核实便宜得多;不要求,它会把记忆里的默认值当成事实写进命令。
四、它产出的脚本长什么样
// bench/http-post.js:恒定到达率(constant-arrival-rate),不是固定并发
import http from 'k6/http';
import { check } from 'k6';
import { SharedArray } from 'k6/data';
import { Trend } from 'k6/metrics';
const users = new SharedArray('users', () => open('./users.csv').split('\n').filter(Boolean));
const bizLatency = new Trend('biz_latency', true); // 单独统计业务耗时,便于与官方口径对照
export const options = {
scenarios: {
steady: {
executor: 'constant-arrival-rate', // 到达率模型:并发数由 k6 按需伸缩
rate: 500,
timeUnit: '1s',
duration: '5m',
preAllocatedVUs: 200,
maxVUs: 1000,
},
},
thresholds: {
http_req_failed: ['rate<0.001'], // 错误率 < 0.1%
http_req_duration: ['p(99)<100'], // 官方口径:含排队、建连与传输
biz_latency: ['p(99)<80'],
},
discardResponseBodies: false, // 需要断言响应体时必须为 false
};
export default function () {
const uid = users[__VU % users.length];
const res = http.post(
`${__ENV.BASE}/api/v1/message/send`,
JSON.stringify({ uid: uid, content: 'x'.repeat(200) }),
{
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${__ENV.TOKEN}`,
},
tags: { name: 'send' }, // 多接口脚本靠 tag 分组看分位
},
);
bizLatency.add(res.timings.duration);
check(res, {
'status is 200': (r) => r.status === 200,
'biz code is 0': (r) => r.json('code') === 0,
});
}
k6 run -e BASE=http://10.0.0.12:8080 -e TOKEN="$(cat token.txt)" \
--summary-export=summary.json --out json=raw.json bench/http-post.js
两处必须人工确认:constant-arrival-rate 的字段名与默认值随版本变化,跑一次即可暴露;thresholds 的时间单位默认是毫秒,写成 p(99)<100 与 p(99)<100ms 含义相同,但写成 p(99)<0.1 不会报错,只会永远失败。
五、结果里必须读的字段
| 字段 | 含义 | 合格线示例 |
|---|---|---|
http_req_duration 的 p(99) | 端到端耗时(含排队、建连、传输) | < 100ms |
biz_latency 的 p(99) | 去掉建连后的业务耗时,用于区分是哪一层慢 | < 80ms |
http_req_failed 的 rate | 非 2xx 或断言失败的比例 | < 0.1% |
http_reqs 的 rate | 实际达成的 RPS,与目标比对 | ≥ 目标的 95% |
dropped_iterations | 到达率模型下没来得及启动的迭代数 | 必须为 0,非 0 说明瓶颈在压测机或服务端 |
vus_max | 达到目标速率所需的并发数 | 看趋势,缓慢上涨意味着单请求变慢 |
http_req_blocked / http_req_connecting | 排队与建连耗时 | 抬升说明连接池或压测机端口有问题 |
六、让它标注不确定的参数
| 常见不确定项 | 为什么会错 | 核实方式 |
|---|---|---|
| 执行器名称与字段 | 版本间新增或改名 | 官方文档 + 直接 k6 run 看报错 |
ghz 的 --rps 与 -q | 长短参数记忆混淆 | ghz --help 逐项对照 |
| 结果导出参数 | 版本演进中已被替换或弃用 | 帮助输出与官方 release notes |
| 阈值单位(ms 与 s) | 写错不报错,只表现为永远不达标 | 跑一次看数量级是否合理 |
| 端口、路径、字段名 | 从上下文推断而来 | 用 curl 或 ghz 单发一次实测 |
常见坑
| 坑 | 现象 | 做法 |
|---|---|---|
| 只给「压一下这个接口」 | 脚本字段名、鉴权头全靠猜 | 把五要素写进提示词,附上真实 curl 示例 |
| 把并发当速率 | 脚本设 500 VU,实际 QPS 随延迟浮动 | 说明目标是 QPS 还是并发,二选一写清楚 |
| 用 AI 生成的「参考数值」当基线 | 报告里出现没见过实测的吞吐数字 | 只允许写实跑结果,示例量级必须标注为示例 |
| 不区分压测机与被压机 | 压测机先打满,数字全废 | 脚本里打印压测机负载,或在提示词里写明两台机器规格 |
| 脚本不入库 | 下次压测无法复现同一组参数 | 脚本、数据文件、运行命令一起提交 |
| 忽略响应体校验 | 服务端返回 200 + 错误码也算成功 | 断言业务码,而不只看 HTTP 状态码 |
小结:让 AI 选工具与写脚本,本质是把「协议、接口、鉴权、目标 QPS、数据来源」五项输入喂全,再要求它按「脚本 + 运行命令 + 结果字段说明 + 待确认参数」四段式输出;工具按协议筛选、按是否需要阶段曲线与阈值断言定档,十万级长连接只能自研客户端。凡是它没有标出「待确认」的参数,都要自己用 --help、官方文档和单发实测核一遍再入库。