关于 performance-testing
面向开发与 QA 团队的性能测试技能,涵盖后端负载测试和前端 Web 性能。它将 k6 阈值与 Lighthouse CI 断言用于 CI 门禁,并区分实验室指标和真实用户数据。
主要功能
- 01使用 k6 测试负载与吞吐量
- 02配置恒定、爬坡、突发和浸泡负载
- 03以阈值在 CI 中阻止性能回归
- 04通过 Lighthouse CI 检查 Web 性能预算
- 05测量 LCP、INP 和 CLS
- 06按步骤定位性能瓶颈
适用场景
- 01验证后端在预期流量下的表现
- 02在 CI 中阻止超出预算的构建
- 03检查页面核心 Web 指标
- 04排查性能预算超标的原因
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ petrkindlmann/ qa-skills --skill performance-testing安装后在 Claude Code 中输入/performance-testing确认已加载。
附带文件references/recipes.md
详细介绍
快速导航
| 你需要…… | 前往 |
|---|---|
| 测试流量下的后端容量/吞吐量/延迟 | k6 负载测试 → references/recipes.md |
| 选择负载模式(恒定/渐增/骤增/持续) | 负载配置表 |
| 衡量真实用户的前端速度(LCP、INP、CLS) | Web 性能 + Core Web Vitals |
| 在 CI 中设置页面性能门禁 | Lighthouse CI(设置 TBT 门禁,而非 INP——见下文) |
| 超出预算,需要找出原因 | 瓶颈识别 |
| 将现有测试套件从 k6 v1 迁移到 v2 | k6 v2 迁移提示框 |
调研问题
先检查 .agents/qa-project-context.md——如果文件存在,就使用其中的信息,并跳过已回答的问题。
衡量什么
- Web 性能还是负载测试? Web 性能衡量用户感知到的速度(Core Web Vitals、页面加载)。负载测试衡量服务器容量(RPS、负载下的延迟)。大多数产品两者都需要。
- 哪些用户旅程对性能至关重要? 并非每个端点都需要负载测试。重点关注流量大、对收入至关重要或对延迟敏感的流程。
- 是否已有性能预算? 如果有,目标是什么?如果没有,这项技能可帮助建立预算——先测量基线。
当前状态
- 当前基线是多少? 设定目标前需要有具体数据。先测量,再定义预算。
- 过去有哪些性能问题? 导致用户流失的慢页面、负载下超时的端点、卡死的查询——这些都能指明重点。
- 是否已有监控? APM(Datadog、New Relic)、RUM 或合成探针可提供现场数据,帮助构建贴近实际的负载模型。
基础设施
- 负载测试在哪里运行? 测试目标应为预发布环境或专用的负载测试环境;未经明确的运维协调,绝不可在生产环境中运行。
- 预期流量模式是什么? 稳定流量、每日高峰、季节性峰值(黑色星期五)或事件驱动的突发流量——这将决定负载配置。
- 链路中是否有速率限制、WAF 规则或自动扩缩容? 这些因素会扭曲结果,必须在测试设计中考虑。
核心原则
1. 先测量,再优化
性能直觉并不可靠;开发者经常优化错误的对象。先进行性能分析,找出实际瓶颈,再进行优化。在正确位置经分析确认的 50ms 提升,胜过在错误位置臆测的 500ms 提升。
2. 只有 CI 强制执行预算,预算才真正有效
在 Wiki 中记录、却不在 CI 中检查的预算,几周内就会被打破。将预算接入流水线,设置 k6 阈值和 Lighthouse 断言,让回归导致构建失败,而不是等到季度审查才发现。
3. 贴近实际的负载胜过最大压力
将流量提升到 10 倍的压力测试能告诉你系统的崩溃点。按预期流量的 1.5 倍进行负载测试,则能告诉你明天的真实用户是否会有良好体验。两者都有价值,但贴近实际的负载测试运行频率更高,也能更早发现回归。
4. Core Web Vitals 衡量的是用户的实际感受
服务端指标(延迟、吞吐量)很重要,但用户是在浏览器中体验性能的。LCP、INP 和 CLS 衡量的是感知速度。API 再快,如果渲染很慢,对用户来说依然很慢。
5. 性能是一项功能
性能不会凭空实现。它需要专用的测试基础设施、预算和监控,也需要像保障功能正确性一样持续关注。应以处理功能缺陷同样的紧迫性处理性能回归。
k6 负载测试
k6 是一款开源负载测试工具,使用 JavaScript/TypeScript 编写脚本,可通过 CLI 运行,并可集成到 CI 中。当前稳定版本:k6 v2.0.0(于 2026-05 最终发布)。v2 存在相较于 v1 的破坏性变更——请参阅下方的迁移提示。
负载测试由三个部分构成:负载配置(stages/scenarios 的形态)、检查项(针对每个请求的断言)和阈值(决定通过或失败的预算,并设置退出码)。始终通过 __ENV 设置基础 URL,这样同一脚本就能用于本地、预发布环境和 CI。
完整的基础负载测试、自定义指标和场景示例,请参阅 references/recipes.md。
最简阈值代码块:
export const options = {
stages: [
{ duration: '1m', target: 20 },
{ duration: '3m', target: 20 },
{ duration: '1m', target: 0 },
],
thresholds: {
http_req_duration: ['p(95)<500', 'p(99)<1000'],
http_req_failed: ['rate<0.01'],
},
};
负载配置
| 配置 | 要回答的问题 | 形态 | 持续时间 |
|---|---|---|---|
| 恒定负载 | 系统能否承受正常流量? | vus: 50, duration: '10m' | 10 分钟 |
| 逐步加压(压力测试) | 系统会在什么负载下开始退化? | 50 → 100 → 200 → 400 → 800 → 0 | 12 分钟 |
| 突增 | 系统能否从流量突然激增中恢复? | 50 → 突增至 500 → 持续 → 降至 50 → 恢复 | 6 分钟 |
| 长时间稳定负载 | 系统是否会随时间推移发生资源泄漏? | 增至 100,持续 4 小时,然后逐步降低 | 4 小时以上 |
只有把“能否恢复?”变成断言,而不是注释,突增测试才算完成。为突增后的时间窗口添加标签(例如 phase:recovery),并针对该窗口设置阈值,这样如果 p95 持续偏高,测试运行就会失败。请参阅 references/recipes.md 中的恢复检测示例。
自定义指标和场景
k6 有四种指标类型:Counter(累计计数)、Rate(非零/真值所占比例,0..1)、Trend(统计分布——p50/p95/p99)、Gauge(最新值)。为请求添加 { tags: { name: 'endpoint' } } 标签,以便按端点、场景或流程筛选指标。
场景可并发运行不同的用户流程,每个场景都有自己的执行器和单独的阈值('http_req_duration{scenario:checkout}': ['p(95)<500'])。可用它们模拟真实的流量组合——同时施加浏览器、结账和以 API 为主的负载。完整的自定义指标和场景示例,请参阅 references/recipes.md。
k6 CI 集成
使用官方 grafana/setup-k6-action@v1 安装 k6,不要手动编写 apt/gpg 密钥服务器代码块(脆弱、容易失效,且无法固定版本)。任何阈值超限时,k6 都会以非零状态码退出,因此无需额外配置,超出预算就会使任务失败。完整的 GitHub Actions 工作流(checkout → setup-k6 → run → upload artifact)请参阅 references/recipes.md。
k6 v1 → v2 迁移(v2.0.0 最终版,2026-05):
k6/experimental/websockets→k6/websockets(移除experimental/前缀;现已稳定)k6/experimental/redis→k6/x/redis— 并未移除。导入会自动解析xk6-redis扩展(默认启用自动扩展解析;JavaScript 用法不变)。 不要手动编写 Redis 客户端。- 已移除
externally-controlled执行器- 已移除
options.ext.loadimpact→ 改用options.cloud(Grafana Cloud k6,原名为 k6 Cloud / Load Impact)- CLI:
--no-summary→--summary-mode=disabled;--upload-only→k6 cloud upload script.js; 已移除k6 login/pause/resume/scale/status(改用k6 cloud login等);已移除位置参数形式的k6 cloud script.js- 新增退出码 97:由云端中止,且并非阈值导致。请在 CI 处理中加入此情况。 参考:https://grafana.com/docs/k6/latest/get-started/migrating-to-v2/
Web 性能
Lighthouse CI
Lighthouse CI(@lhci/cli)可自动执行 Google Lighthouse 审计,并通过 lighthouserc.js 断言在流水线中强制执行预算。当前版本:@lhci/cli 0.15.x,使用 Lighthouse 12.6 引擎。该项目处于维护模式(距上次发布约一年),且尚不支持 Lighthouse 13(需要 Node 22.19+);它仍是 Lighthouse 的标准 CI 接入方式,但在新项目中采用前请关注上游动态。
完整的 lighthouserc.js(LCP/CLS/TBT/性能评分断言)和 lhci autorun CI 步骤,请参阅 references/recipes.md。
INP 仅能通过现场数据衡量——在实验室中以 TBT 设门槛
这是 Web 性能领域最容易被误解的一点,务必准确理解:
- INP(Interaction to Next Paint,交互到下一次绘制)是仅能通过现场数据衡量的指标。 Lighthouse 实验室审计加载页面时没有用户交互,因此无法测量或评分 INP。在标准的
lhci autorun运行中断言interaction-to-next-paint,等于对 Lighthouse 根本不会生成的指标设门槛。 - 在实验室中,应以总阻塞时间(TBT)作为代理指标设门槛:断言
'total-blocking-time': ['error', { maxNumericValue: 200 }]。TBT 与 INP 相关,但两者并不相同(页面的 TBT 可能为 0ms,但现场 INP 仍可能不达标)。 - 从现场数据跟踪真实的 INP——使用 Chrome UX Report(CrUX)或你的 RUM 工具。这才是用户实际体验到的数值,也是 Google 用于排名的指标。
- 只有在必须测量脚本化交互延迟时,才使用 Lighthouse user-flow / timespan 模式配合脚本化点击,或使用
k6/browser通过PerformanceObserver观察event条目。应将其视为脚本化的实验室代理指标,而非现场 INP。
**FID 已退出历史舞台:**FID 已弃用,且
web-vitalsv5+ 已移除该指标;INP 于 2024 年 3 月成为 Core Web Vital。不要在任何新代码中对 FID 设断言。
在 Playwright 中测量 CWV
如需在现有 Playwright 测试套件中进行逐页实验室检查,可使用 page.evaluate 和
PerformanceObserver 捕获 LCP 与 CLS(这两个指标在页面加载时都能稳定观测),然后断言
阈值。完整测试见 references/recipes.md。如需在负载下捕获 CWV,请使用同一文件中的
k6/browser 配方。
Core Web Vitals
Google 用于衡量用户感知性能的三项指标。
| 指标 | 衡量内容 | 良好 | 需要改进 | 较差 |
|---|---|---|---|---|
| LCP(Largest Contentful Paint,最大内容绘制) | 加载——最大元素的渲染时间 | ≤ 2.5s | 2.5s–4.0s | > 4.0s |
| INP(Interaction to Next Paint,交互到下一次绘制) | 响应能力——交互 → 下一次绘制(仅限现场数据) | ≤ 200ms | 200ms–500ms | > 500ms |
| CLS(Cumulative Layout Shift,累积布局偏移) | 视觉稳定性——意外的布局移动 | ≤ 0.1 | 0.1–0.25 | > 0.25 |
CLS 按面积加权:宽视口上的小型应用如果因迟到的横幅下移 240px,得分也可能只有 0.07。在实验室测试中,还应在首次渲染后固定关键控件的边界框位置
(见 references/recipes.md)。
常见修复方法:
- LCP——服务器速度慢(使用缓存/CDN、对 LCP 内容进行 SSR/SSG),CSS/JS 阻塞渲染(延迟加载、内联关键 CSS),图片加载慢(使用 WebP/AVIF、对 LCP 图片使用
preload)。 - INP——JS 长任务(使用
scheduler.yield()、requestIdleCallback),处理器开销大(防抖/节流、使用 Web Workers),布局抖动(通过requestAnimationFrame批量读取/写入 DOM)。 - CLS——为图片设置
width/height,为注入的内容预留空间(使用aspect-ratio/min-height),使用font-display: swap配合size-adjust,为广告/嵌入内容设置固定尺寸容器。
现场数据与实验室数据
| 实验室数据 | 现场数据 | |
|---|---|---|
| 来源 | Lighthouse、WebPageTest、Playwright、k6/browser | Chrome UX Report(CrUX)、RUM 工具 |
| 环境 | 模拟环境、受控条件 | 真实用户、真实设备、真实网络 |
| 用途 | 调试、CI 门槛、部署前检查 | 了解用户的实际体验 |
| 局限性 | 不包含现实世界的差异;没有现场 INP | 无法复现特定条件 |
使用实验室数据设置 CI 门槛并进行调试;使用现场数据了解用户的真实体验。若页面的 Lighthouse 得分为 100,但 CrUX 数据不佳,就说明存在实际问题——而 INP 只会出现在现场数据这一栏。
瓶颈识别
超出预算时,按以下顺序排查——切勿猜测,也不要在分析性能之前优化:
- 确定缓慢的端点。 k6 中按端点划分的
Trend指标会给出每个 API 的 p50/p95/p99。最慢的端点是首要排查目标。 - 数据库。 检查是否缺少索引(
EXPLAIN)、是否存在 N+1 模式(JOIN/批处理)、写入密集型表是否存在锁竞争(优化事务),以及结果集是否不受限制(分页)。 - CDN/缓存。 验证静态资源的
cache-control(public, max-age=31536000, immutable)以及x-cache: HIT的命中率。 - 第三方脚本。 使用
blockedUrlPatterns屏蔽分析、聊天和跟踪脚本后运行 Lighthouse;对比屏蔽前后的分数,以量化其开销。 - 结合服务端指标进行分析。 仅看客户端延迟无法发现原因——检查测试期间的 CPU、内存、磁盘 I/O、连接池饱和度和查询执行时间。
反模式
1. 未协调便对生产环境进行负载测试
负载测试可能触发自动扩缩容(成本高昂)、限流(导致测试失败)、告警(产生不必要的通知)或服务中断。务必与运维团队协调,并将目标设为预发布环境或专用负载测试环境。
2. 负载场景不切实际
产品只有 500 DAU,却测试 10,000 个并发用户;或者实际流量有峰值,却使用均匀流量。根据分析数据建立负载模型;如果没有数据,就从预估峰值的 2 倍开始,逐步增加。
3. 忽略服务端指标
只关注 k6 的客户端响应时间。服务端可能已达到 95% CPU 使用率、连接池已耗尽,或出现内存泄漏。将负载测试结果与服务端指标结合分析。
4. 只在发布前进行性能测试
每季度才进行一次发布前负载测试,会让数十个回归问题不断累积,且难以追溯根因。在 CI 中对每次合并到 main 的更改运行测试,并设置预算以便立即发现回归。
5. 没有性能预算
“LCP 为 3.2s”只是信息;“LCP 必须低于 2.5s”才是门槛。定义预算,并通过 k6 阈值和 Lighthouse 断言强制执行,将超标视为 bug。
6. 在标准 Lighthouse CI 中断言 INP
标准 lhci autorun 实验室模式无法生成 INP 审计结果——因为没有用户交互。在此处设置 interaction-to-next-paint 门槛,相当于对一个 Lighthouse 从未测量的数值设门槛。将 total-blocking-time 设为实验室模式下的代理指标,并通过 CrUX/RUM 跟踪真实 INP。
7. 未经性能分析就进行优化
花几天时间优化一个只占响应时间 2% 的函数。先进行性能分析,找出真正的瓶颈,再进行优化。200ms 的查询每次都比 5ms 的 JS 函数更值得优先处理。
8. 使用空数据库进行测试
生产环境有 1,000 万行数据,却只用 100 行种子数据进行负载测试。数据规模扩大后,查询性能会截然不同。先用生产规模的(匿名化)数据填充负载测试环境。
验证
在声称完成之前,证明这些产物确实有效:
- k6 脚本:
k6 run --summary-mode=disabled load-tests/api-load.js以 0 退出,且测试结束摘要中每一行thresholds都显示绿色(✓)。红色阈值行表示预算超标,且退出码非零。 - 尖峰恢复: 运行尖峰脚本,并确认摘要中出现
{phase:recovery}阈值且测试通过——这证明恢复过程经过了断言,而不只是写在注释里。 - Lighthouse CI:
lhci autorun以 0 退出,且所有['error', ...]断言均通过;LCP/CLS/TBT 断言超标时,退出码为非零。
完成标准
- k6 脚本涵盖目标负载配置:基线(恒定负载)、压力(逐步升至崩溃点)和持久负载,每个脚本都通过
__ENV指定基础 URL。 - 峰值测试通过限定在峰值后时间窗口内的阈值断言恢复情况(例如
http_req_duration{phase:recovery}),而不只是添加// recovery注释。 - 将性能预算编码为 k6 阈值(例如
p(95)<500、http_req_failed rate<0.01),超出预算时使 CI 任务失败。 - Lighthouse CI 的
lighthouserc.js通过['error', ...]断言,对largest-contentful-paint、cumulative-layout-shift和total-blocking-time(INP 的实验室环境代理指标)进行合并门禁;真实 INP 通过 CrUX/RUM 跟踪,而非在实验室环境中断言。 - 为每个关键页面(首页、结账页、仪表板)记录 Core Web Vitals 基线,并标注 Good/Needs Improvement/Poor 分类。
- 测试结果包含每种场景的 p95 和 p99 延迟、错误率及吞吐量,并作为 CI 构建产物保存。
参考文件(位于 references/ 中)
- recipes.md — 可运行的示例:基础 k6 负载测试、带恢复检测的峰值测试、自定义指标、场景、k6 CI 工作流(
grafana/setup-k6-action)、lighthouserc.js、k6/browser在负载下测试 CWV 的示例,以及 Playwright LCP/CLS 测试。
相关技能
- ci-cd-integration — 配置 k6 和 Lighthouse CI 的流水线、安排每晚运行,并通过预算控制部署。
- qa-metrics — 将 LCP/INP/CLS 和 p95 延迟纳入更全面的 QA 指标仪表板。
- release-readiness — 在发布与否检查清单中纳入性能基准。
- synthetic-monitoring — 发布后安排生产环境 CWV/正常运行时间探测;此技能用于发布前的实验室环境门禁。
- observability-driven-testing — 在生产遥测数据作为设计新性能测试的输入时使用。
- qa-project-context — 记录性能预算、流量模式和待测试的关键流程。