load-test skill 解析¶
load-test 是一套面向服务级性能测试的负载测试框架。它的核心设计思想是:高质量负载测试的第一步不是"跑点流量看看",而是先定义 SLO——没有 SLO,任何性能数字都只是无法判断好坏的噪声。定好 SLO 之后,再由它驱动场景选择、脚本设计和负载生成器的资源规划,最后用百分位数(而不是平均值)、场景纪律、分层评分卡,以及一份"永远不为空的未覆盖风险",把"这次测试到底证明了什么、还有什么没证明"讲清楚。 因此它把上下文收集、SLO 优先、范围分类、输出完整性四道门禁,连同深度选择、降级模式、19 项检查清单、场景与工具选择、反例目录、三层评分卡和输出契约,串成了一条固定流程。
1. 定义¶
load-test 用于:
- 为 HTTP / gRPC 服务编写 k6 / vegeta / wrk 负载测试脚本
- 在写脚本之前先帮用户把 SLO(延迟、吞吐、错误率、可用性)定清楚
- 根据测试目标选择合适的场景(smoke / load / stress / breakpoint / soak / spike)
- 分析测试结果、给出 SLO 通过/失败判定并定位瓶颈
- 做容量规划,或在生产事故中排查延迟 / 吞吐问题
它输出的不只是脚本或一句"测过了",而是一份结构固定的报告:
- 上下文摘要(目标服务、协议、部署、SLO)
- 模式与深度(Write / Review / Analyze × Lite / Standard / Deep)及理由
- SLO 定义
- 场景设计
- 脚本,或对脚本的评审
- 结果分析(百分位表、错误分类、逐项 SLO 判定)
- 瓶颈评估
- 优先级建议
- 未覆盖风险(强制,永不为空)
- 一行三层评分卡结论
从设计上看,它更像一个"负载测试治理框架",而不是一个只会吐出 k6 脚本的提示词。它要回答的是"这次测试凭什么算数",而不只是"帮我发点请求"。
2. 背景与问题¶
这个 skill 要解决的,不是"模型不会写 k6 脚本"——恰恰相反,评估显示基础模型本身已经相当懂负载测试。真正的问题在于:脱离框架的负载测试,非常容易滑向几种"看起来做了、其实不算数"的模式。
常见问题大致集中在 8 类:
| 问题 | 典型后果 |
|---|---|
| 没有 SLO 就开跑 | 得到 p99=312ms、4500 RPS 这类数字,却没人知道算好还是坏,无法据此做任何决策 |
| 用平均值汇报延迟 | 平均 45ms 很漂亮,却掩盖了 p99=2.1s——1% 的用户在等 2 秒以上 |
| 不做 warmup 就测量 | 头 30 秒混进了连接池建立、缓存冷启动,p99 被抬高 5-10 倍 |
| 时长太短就宣布通过 | 30 秒跑完喊"通过",但 GC、连接池耗尽、内存泄漏都要几分钟才暴露 |
| 每次都请求同一条数据 | 缓存命中率 100%,真实的数据库路径根本没被测到 |
| 负载生成器和被测服务同机 | 两者抢 CPU,测出来的是资源竞争,不是服务性能 |
| 负载生成器自己被压垮 | maxVUs 设太大,服务一饱和 k6 就狂开 VU,几分钟后生成器 OOM,还被误读成"服务崩了" |
| 报告不说"没测什么" | 团队把结果当成完整结论,悄悄漏掉了 spike、单副本失效、幂等性等关键未验证场景 |
load-test 的设计逻辑,就是先把"这次测试的通过标准是什么、要回答什么问题、负载怎么建模、生成器扛不扛得住、哪些结论有数据支撑、还有什么没测"想清楚,再决定写什么脚本、跑哪种场景、怎么下判定。
3. 与常见替代方案的对比¶
| 维度 | load-test skill | 直接让模型"写个 k6 脚本" | 把压测当成"跑点流量看数字" |
|---|---|---|---|
| SLO 先行 | 强 | 弱 | 几乎没有 |
| 百分位纪律 | 强 | 中 | 弱(容易报平均值) |
| 场景由目标驱动 | 强 | 中 | 弱 |
| 工具 / 负载模型选择 | 强(开环 vs 闭环有讲究) | 弱 | 弱 |
| 负载生成器健康 | 强(Little's Law 定 maxVUs、算内存预算) | 几乎没有 | 几乎没有 |
| 瓶颈归因 | 强(分应用 / 基础设施 / 测试假象三层) | 中 | 弱 |
| 结果可审计 | 强(三层评分卡 + 未覆盖风险) | 弱 | 弱 |
| 对"没测什么"的诚实 | 强制说明 | 通常不提 | 通常不提 |
评估里有个值得强调的结论:它的价值不在"教模型怎么做压测",而在把压测从一组零散数字,提升成一套有通过标准、可复现、可审计、且诚实交代边界的结论。
4. 核心设计逻辑¶
4.1 先定义 SLO,再写脚本¶
load-test 的四道门禁里,第二道就是 SLO 优先,而且是硬阻断:没有 SLO,不许开始写脚本。这一步是整个 skill 的思想核心。
原因很直接:负载测试的产物是一个判断——"这个服务在目标负载下达标了没有"。没有 SLO,就没有"达标"这条线,测出来的 p99、RPS 全是无法解释的数字。skill 把最小 SLO 集合固定为四项:延迟(p50 与 p99)、吞吐(最低持续 RPS)、错误率(明确到"< 0.1% 5xx"而不是"错误率低")、可用性。
更关键的是,SLO 不是写在文档里给人看的,而是要落到 k6 的 thresholds 里,成为机器判定通过 / 失败的依据。评估明确要求 SLO 必须写成 thresholds,而不是用 check() 逐条比较——因为 check() 是每个 VU 独立判断,拿不到统计意义上的聚合结果。
这解决的是最常见的一种失真:测了一大堆,最后只能说"看起来还行"。
4.2 把百分位数当成正确性,而不是口味¶
load-test 反复强调:延迟必须报 p50 / p95 / p99 / p99.9 / max,绝不能用平均值下结论。参考文档里的说法很直白——如果 99% 的请求是 10ms、1% 是 5 秒,平均值是 60ms,而这 60ms 是"没有任何一个用户真正体验到的数字"。
它甚至把百分位的分布形状做成了可读的诊断信号:p99 小于 3 倍 p50 是健康的窄分布;p99 超过 10 倍 p50 说明存在双峰或资源竞争(缓存命中 / 未命中、GC 停顿、连接池耗尽);某个百分位突然跳一个台阶,往往意味着撞上了连接池、线程池或限流的硬上限。
这层设计的意思是:报平均值不是"风格问题",而是一个会给出错误结论的缺陷。
4.3 场景由测试目标驱动¶
skill 把负载场景拆成六种,每一种都对应一个明确的问题,而不是笼统地"跑点压力":
- smoke:能不能在轻负载下正常工作
- load:能不能扛住目标 RPS
- stress:超过目标之后从哪里开始劣化
- breakpoint:绝对上限在哪
- soak:长时间跑会不会内存泄漏、连接池枯竭
- spike:突发 10 倍流量再回落,能不能恢复
它明确要求"按测试目标选场景",而不是随手发一批请求。深度较高时,多个场景还会组合起来(smoke → load → stress → breakpoint)。
4.4 工具选择背后是负载模型的区别¶
k6 和 vegeta 的取舍,skill 讲得很清楚,而且落在一个容易被忽略的点上——开环模型 vs 闭环模型。
k6 用虚拟用户(VU)加 think time 模拟真实用户行为,是闭环模型;vegeta 按固定到达率发请求,不管服务多慢都照发,是开环模型。参考文档点出关键:当服务变慢时,闭环模型会自动少发请求,反而把延迟问题藏起来;而开环的固定到达率会让请求排队堆积,"这正是你找饱和点的方式"。
所以 skill 的立场是:要模拟真实用户行为、做 soak,用 k6;要精确控制速率、找容量上限,用 vegeta 的到达率模型。这其实是在有意识地规避"协调遗漏"(coordinated omission)这个压测经典陷阱,而不只是"哪个工具顺手用哪个"。
4.5 把负载生成器本身当成一等公民¶
这是 load-test 最有辨识度、也最见功力的一层。很多压测指南只关心被测服务,这个 skill 却花了大量篇幅关心"发压的机器会不会先垮"。
它强制几件事:
- 负载生成器必须和被测服务分开部署,绝不同机、同 pod、同网络瓶颈——否则测出来的是 CPU 争抢,不是服务性能
- 生成器自己的容量要先验证,k6 要盯着
dropped_iterations - k6 的
maxVUs要用 Little's Law 来定:需要的 VU ≈ 速率 × 健康期 p95,maxVUs 取两倍即可
最后一条尤其重要。参考文档里有个真实事故:把 4k TPS 的 maxVUs 随手设成 8000("留点余量"),结果服务一饱和,k6 为了维持速率不停开 VU,每个 VU 约占 3 MB,8000 × 3 MB = 24 GB,生成器在六到八分钟时 OOM——而且这还会被误读成"压测崩了",其实是服务撑不住目标速率。正确做法是把 maxVUs 压到 1600,服务饱和时 k6 会如实报 dropped_iterations,那才是"服务扛不住目标速率"的正确信号,而不是一次测试失败。
skill 把这条经验固化成公式、反例(AE-7、AE-8)和评分卡里的 Hygiene 项,就是不想让任何人再踩一次同样的坑。
4.6 warmup 与足够时长是硬约束¶
load-test 要求测量必须在 warmup 之后才开始——JVM 预热、连接池填充、缓存预热都要排除在测量之外,k6 里用独立的 warmup 场景加 phase 标签,让 warmup 的样本不污染 SLO 判定。它同样强调时长:smoke 至少 1 分钟,load 3-5 分钟,soak 15 分钟以上,因为 GC、连接池耗尽、内存泄漏都要几分钟才显形。评估里基础模型最典型的一次实质性失误,正是没能指出"30 秒时长不够"。
4.7 测试数据要真实,避免缓存偏差¶
每次都打同一个 ID,缓存命中率就是 100%,真实的数据库路径永远测不到。skill 要求用参数化、有真实分布的数据源,让缓存命中 / 未命中的比例接近生产。这类问题开发者并不是不懂,而是写脚本时不会自然想到,固化成清单后就不再依赖临场记忆。
4.8 降级模式与"绝不编造性能数字"¶
现实里输入往往不全:可能只有脚本没有结果,或只有结果没有 SLO。load-test 为此设计了降级模式表,明确每种输入下"能交付什么、不能声称什么",并要求用 # DEGRADED: 显式标注。它有一条铁律:绝不编造性能数字,没有数据就不能声称 SLO 达标。这让 skill 在信息不足时依然诚实,而不是硬凑一个看起来完整的结论。
4.9 三层评分卡是上线门槛,不是打分游戏¶
每次分析后,skill 会套一张三层评分卡:Critical(3 项必须全过,任何一项挂掉就要重测)、Standard(5 项过 4)、Hygiene(5 项过 3)。Critical 包含"测试前已定义 SLO""warmup 已排除""稳态时长足够"这类不可退让的项。
这层设计的意义在于,它把"这个脚本能不能拿去跑生产压测"从"感觉差不多"变成一个可追踪的状态:Critical 没全过,脚本就是 FAIL,不是"还能改改"。评估里基础模型评审缺陷脚本时,恰恰给不出这种带明确 Critical 0/3 的判定。
4.10 瓶颈归因分三层,先分清"服务问题"还是"测试问题"¶
分析结果时,load-test 用一套三层瓶颈分类:应用层(串行处理、N+1 查询、连接池缺失、goroutine 无上限、同步外部调用)、基础设施层(副本不够、数据库瓶颈、网络带宽),以及一个特别重要的第三层——测试方法本身的问题(生成器成了瓶颈、生成器与目标跨了慢网络、缓存预热假象)。
每一类都配了可观察的信号(比如"加副本也没用、数据库查询时间占主导"指向数据库瓶颈;"min 延迟等于网络 RTT"指向跨网络部署)。单独列出第三层是很成熟的设计——它专门防止把测试台的毛病算到服务头上。
4.11 未覆盖风险永远不为空¶
输出契约里的 §9.9"未覆盖风险"是这个 skill 最有代表性的发明,而且规定它绝不能为空。它逼着每次交付都写清楚这次没测到什么:soak 没跑、内存泄漏风险未验证;只测了读路径、写路径容量未知;单区域测试、跨区延迟没测……
它的价值在于,一份结果如果不写这一段,很容易被当成完整答案,悄悄漏掉 spike、单副本失效、幂等性这些关键场景。这条规则把这些盲点从"没人提"变成"明确列出的待办项"。这正是裸跑一次 k6 run 永远给不出的东西。
4.12 缺陷用编号可追溯,而不是随口点评¶
load-test 把常见缺陷做成了带编号的反例目录(AE-1 没有 warmup、AE-6 用平均值下结论……)。评审脚本时,它会说"CRITICAL-3 — AE-1:缺少 warmup / ramp-up",而不是一句含糊的"建议加个预热"。
好处是团队有了一个可以回指 SKILL.md 的精确坐标,评审标准全队一致、可复现,而不是每次凭个人经验给出不同的意见。
4.13 在触发和边界上认真下功夫¶
load-test 的 description 用"任务类型枚举"的方式覆盖触发词,评估里触发准确率 F1 约 87%,该触发的 10 个场景全中,不该触发的 8 个里对了 6 个("CPU 高""帮我开个 k6 Cloud 账号"这类是软边界,靠适用性门禁兜底)。它还明确把自己和 go-benchmark 分工:后者做函数级微观剖析,load-test 做服务级端到端压测,一个偏微观、一个偏宏观。
5. 这个设计解决了哪些具体问题¶
| 问题类型 | skill 中的对应设计 | 实际效果 |
|---|---|---|
| 没有通过标准就开跑 | SLO 优先门禁 + thresholds | 每次测试都有明确的达标线 |
| 平均值掩盖长尾 | 百分位纪律(p50/p95/p99/p99.9/max) | 长尾问题不再被漂亮的平均值盖住 |
| 随手发压、没有目标 | 六种场景按目标选择 | 场景与要回答的问题对齐 |
| 慢响应把延迟藏起来 | 开环 / 闭环模型选择 | 找得到真实饱和点 |
| 发压机器先垮 | Little's Law 定 maxVUs + 内存预算 | 生成器不 OOM,结果算被测服务的 |
| 冷启动 / 短时长污染数据 | warmup 分离 + 时长下限 | 数字反映稳态,不是启动噪声 |
| 缓存偏差 | 参数化真实数据 | 数据库路径真正被测到 |
| 把测试台问题算到服务头上 | 三层瓶颈归因 | 先分清是服务还是测试的问题 |
| 结果被当成完整结论 | §9.9 未覆盖风险(强制) | 盲点变成显式待办 |
| 评审全凭个人经验 | 反例编号 + 三层评分卡 | 评审可复现、可审计 |
6. 主要亮点¶
6.1 它把负载测试从"跑数字"改造成"有通过标准的判断"¶
SLO 优先是整个 skill 的地基,也是它和"随手跑个 k6"最根本的区别。
6.2 它的真正增量在结构和盲点,而不是领域知识¶
这是评估里最诚实、也最重要的一条结论。评估显示,基础模型本身已经很懂负载测试:在 Analyze(结果分析)场景,用不用 skill 的差距只有 +14.3pp,两边都能自己算出饱和 RPS、都指向了数据库连接池。skill 的真正杠杆在 Review(评审)场景——差距高达 +62.5pp,因为这里拼的是"能不能系统地指出缺陷、给出带编号的判定、并列出盲点"。整体上,带 skill 在干净场景下达到 15/15(100%),不带只有 9/15(60%),差距约 +40pp。
换句话说,load-test 的价值主要不是"懂得更多",而是"逼你把结论写得结构完整、把盲点显式列出来"。这一点和 go-benchmark 的价值分布很像。
6.3 对负载生成器健康的关注非常罕见¶
用 Little's Law 定 maxVUs、算内存预算、把生成器 OOM 和 --out csv 的内存开销都写成反例,这些是从真实事故里长出来的经验,一般的压测教程根本不会提。
6.4 §9.9 未覆盖风险是很有辨识度的发明¶
强制每次都交代"没测什么",把静默遗漏变成显式待办,这在团队协作里极其实用。
6.5 三层评分卡让"能不能上线"变得可判断¶
Critical / Standard / Hygiene 三层,把"这个脚本安不安全"量化成可追踪的状态,而不是靠感觉。
6.6 瓶颈归因区分了"服务问题"和"测试假象"¶
单独把"测试方法错误"列为第三层,专门防止把生成器 OOM、跨网络延迟这类测试台问题误判成服务瓶颈。
7. 什么时候适合用,什么时候不该硬用¶
| 场景 | 是否适合 | 原因 |
|---|---|---|
| 为 HTTP/gRPC 服务写压测脚本、定 SLO | 非常适合 | 这是核心场景 |
| 评审已有压测脚本的质量 | 非常适合 | 评审正是它增量最大的地方(约 +62.5pp) |
| 分析压测结果、定位瓶颈、做容量规划 | 适合 | 有用,但基础模型本身也不弱,增量相对小 |
| 生产事故里排查延迟 / 吞吐 | 适合 | 三层瓶颈归因很有针对性 |
| 函数级微基准 | 不适合 | 交给 go-benchmark |
| 纯数据库基准、浏览器 / UI 性能 | 不适合 | 超出服务级压测范围 |
| 混沌工程故障注入、基础设施编排 | 不适合 | 不在 scope 内 |
另外要有心理预期:load-test 是对比过的几个 skill 里上下文和运行开销最高的一个(运行开销约 +107%),因为它要加载 SKILL.md 和上千行参考文档。对只是想快速冒烟的场景,用 Lite 深度就够了,不必每次都上重型流程。
8. 结论¶
load-test 的真正亮点,不是它能更快写出一个 k6 脚本,而是它把负载测试里最容易走过场的部分固化了下来:先定义 SLO,再由 SLO 驱动场景和脚本,同时把负载生成器本身的资源健康、百分位纪律、warmup 分离、缓存偏差、瓶颈归因和"没测什么"都纳入约束,最后用三层评分卡给出一个可审计的通过 / 失败判定。
从设计上看,这个 skill 很清楚地体现了一条原则:高质量负载测试的关键,不是把流量跑得更大,而是让每一次测试都能说清楚自己的通过标准是什么、负载是怎么建模的、数字是不是统计上站得住、瓶颈到底出在服务还是测试台,以及哪些风险这次根本没覆盖。 这也是它特别适合写脚本、评审脚本和上线前性能验证的原因——而在纯结果分析上,它更多是在帮你补齐结构和盲点,而不是提供你本来不知道的知识。
9. 文档维护¶
当以下内容发生变化时,这份文档应该同步更新:
skills/load-test/SKILL.md中的门禁、深度选择、降级模式、检查清单、场景与工具选择、反例目录、三层评分卡或输出契约发生变化。skills/load-test/references/k6-patterns.md、vegeta-patterns.md或analysis-guide.md中的关键模式、内存模型或瓶颈归因方法发生变化。evaluate/load-test-skill-eval-report.md或evaluate/load-test-skill-eval-report.zh-CN.md中支撑本文判断的核心结果(评分、触发准确率、带 / 不带 skill 的差距等)发生变化。
建议按季度复查一次;如果 load-test 的 SLO 门禁、场景定义、评分卡结构或触发条件描述发生明显重构,则应立即复查。
10. 相关阅读¶
skills/load-test/SKILL.mdskills/load-test/references/k6-patterns.mdskills/load-test/references/vegeta-patterns.mdskills/load-test/references/analysis-guide.mdskills/load-test/scripts/tests/COVERAGE.mdevaluate/load-test-skill-eval-report.mdevaluate/load-test-skill-eval-report.zh-CN.md