Community编程与开发github.com

DataMonarch/locust-load-test

使用 Python、locust 和 uv 对任何项目的 API 或 Web 服务进行负载测试。扫描代码库中的每个端点,将其分类为核心、支持和外围功能,询问用户每个端点要运行哪个测试级别(冒烟、预期负载、压力)以及要模拟哪种用户负载场景,并根据项目自身的流量、限制和超时提供选项。

locust-load-test 是什么?

该技能自动化了从代码发现到生成可操作报告的完整负载测试工作流。它适合希望在开发早期集成性能验证的团队,支持 locust、Python 和 uv。工作流包括七个步骤:发现端点、分类、向用户询问测试参数、构建 Locust 测试工具、安全运行、收集服务器指标和生成 HTML 报告。它通过扫描项目代码库识别所有 API 端点,根据业务重要性将其分类为核心、支持和外围,然后针对不同的负载模式(如冒烟测试、预期负载和压力测试)提供选项。该技能专注于端到端 API 和 Web 服务测试,不适用于单个函数的微基准测试或前端页面速度审计。它通过隔离负载测试环境、在共享系统上逐步运行以及遵守停止规则来优先考虑安全性,以防对目标系统造成损害。

兼容平台✓Claude Code~Codex CLI~Cursor
npx skills add https://github.com/DataMonarch/agent-skills/tree/HEAD/skills/locust-load-test

Installed? Explore more 编程与开发 skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

在你喜欢的 AI 中提问

打开一个已预加载此 Agent Skill 的新对话。

文档

使用 locust 和 uv 进行负载测试

本技能将项目从“它能承受多少?”转变为团队可以据此采取行动的报告。请按顺序执行步骤,因为每一步都是下一步的基础。用户需要做出两个决定:测试级别和流量场景,而且只有在看到您在代码中发现的内容之后才能做出决定。

$SKILL 是本技能的文件夹,即您阅读此 SKILL.md 的目录。在 shell 中设置一次(SKILL=/path/to/locust-load-test)。负载测试本身位于目标存储库中,在 loadtest/ 或用户选择的任何位置。

1 发现  -> loadtest/endpoints.json   每个端点,包括其调用者和成本提示
2 分类  -> loadtest/PLAN.md          核心/支持/外围表;向用户展示
3 询问  -> 层级 + 场景 + 环境 + 约束
4 构建  -> loadtest/ (一个 uv 项目)   harness.py, payloads/, run 脚本
5 运行  -> loadtest/results/<run>/    先冒烟,然后选择层级,风险最高的最后运行
6 收集  -> results/<run>/metrics, profiles   服务器端视图(当可访问时)
7 报告  -> loadtest/report.html + 用通俗语言描述发现

1. 发现端点

uv run $SKILL/scripts/discover_endpoints.py . -o loadtest/endpoints.json --markdown loadtest/endpoints.md

扫描器读取代码,不写其他内容。它能发现常见技术栈中的路由声明:Python、Node、JVM、Go、Ruby、.NET 和 PHP 框架,以及 OpenAPI 文件、GraphQL 模式、gRPC protos、websocket 和 SSE。它会尽可能解析路由器前缀和挂载点。它还会计算每个端点的客户端调用点(fetch、axios、httpx、requests、生成的客户端、测试,以及服务器渲染模板中的链接、表单和 htmx 属性),以便您了解产品自身的 UI 和服务使用了什么。每个条目包含方法、路径、file:line、处理器、认证提示、请求体提示、流提示、成本提示、处理器所做的调用以及谁调用了它。

框架自身的路由列表优于任何静态扫描,因为框架已经解析了所有前缀、挂载点和生成的路由。当应用可以提供一个时,请使用它作为真实来源。这意味着来自正在运行的开发服务器的 OpenAPI 文档(/openapi.json)、flask routes、bin/rails routes、php artisan route:list --json 或 Spring 的 /actuator/mappings:

uv run $SKILL/scripts/discover_endpoints.py . --route-dump http://localhost:8000/openapi.json -o loadtest/endpoints.json

然后在信任输出之前批判性地阅读它:

  • unmatched_client_calls 要么是扫描遗漏的路由,要么是调用其他服务的调用。
  • prefix_resolved: false 和 notes 标记了猜测。shared_handler: true 表示一个函数服务多个方法,并且扫描无法判断其成本提示属于哪个方法;请阅读该处理器。
  • existing_load_tests 列出了存储库中已有的 locustfile 或 k6 脚本。重用它们的载荷。
  • 对照源文件抽查五个条目。

当清单看起来单薄、某个技术栈未被覆盖,或者您需要某个框架的路由导出命令时,请阅读 references/discovery.md。

2. 分类:核心、支持、外围

根据判断对端点进行排序。不要按关键字排序:相同的 /search 在商店中是核心功能,在管理控制台中则是支持功能。

层级是什么典型示例
core(核心)用户来的目的,通常是繁重工作发生的地方:计算、模型调用、数据处理、主要写入路径商店中的结账;文档服务中的转换或提取;聊天 API 中的补全
support(支持)使核心功能可用登录、会话、设置、列表和搜索用户自身的数据、为核心功能提供输入的上传、管理员 CRUD
peripheral(外围)产品并非为其存在健康检查、指标、文档、静态文件、调试路由、来自非自有服务的 webhooks

要考虑的证据:

  • README 和文档说明产品的用途;
  • UI 调用最多的是什么(client_calls),以及测试使用了什么(test_calls);
  • 处理器的成本:跟踪 calls 进入服务层,因为 cost_hints 只能看到处理器主体。模型调用、数据库写入、外部调用、CPU 工作(密码哈希、压缩、图片:cpu)、文件处理和队列发布是使端点昂贵的原因;
  • 命名;
  • 如果您能看到,团队自身的监控或日志显示最繁忙的是什么。

编写 loadtest/PLAN.md,包含此表,每个您打算加载的端点一行,以及您省略的端点和原因的简短列表:

| endpoint | tier | why | cost hints | payload source |
|---|---|---|---|---|
| POST /api/orders/{id}/checkout | core | 购买路径;UI 从 3 个地方调用它 | db writes, payment API call | tests/fixtures/order.json |

在询问问题之前向用户展示此表,并邀请更正。错误的层级会使后面的每个数字都回答错误的问题,而用户比任何扫描都更了解他们的产品。

3. 询问用户

使用 AskUserQuestion 工具。如果不可用,请以纯编号列表的形式询问相同的问题并等待答案。仅在用户看到计划表之后才询问。

  • Q1 核心层级。 层级 1、2 或 3。
  • Q2 支持层级。 层级 1、2 或 3,或者省略支持。
  • Q3 用户负载场景。 从该项目派生的两个到四个选项(见下文),您推荐的选项在前,标记为“(推荐)”,并附上一行说明其为何合适。
  • Q4 目标环境。 要加载哪个部署(staging、本地堆栈、负载测试副本)。切勿加载生产系统,除非用户明确同意。负载会溢出到共享数据库、租户和账单上。
层级运行方式回答的问题成本
1 冒烟/基准测试低稳定负载(1 到几个用户),持续几分钟每条路径是否都能工作,在没有争用的情况下延迟是多少?几分钟;风险可忽略
2 预期负载逐步增加到预期峰值,保持,然后在该水平进行短期浸泡它在其最繁忙时是否达到延迟目标,有多少余量?大约一小时;目标在其真实峰值下运行
3 压力/容量逐步增加直到停止规则触发(拐点和破坏点),外加一个尖峰它在哪里失效,如何失效,以及它是否恢复?一到两小时;目标将饱和并可能崩溃,因此需要一个时间窗口和有人监视

从项目对其流量的描述中推导出 Q3 的选项:README 和文档(谁在使用它、席位数量、租户)、配置(速率限制、工作线程和副本数量、自动缩放器边界、队列大小、连接池)、cron 和批处理作业,以及调用者自身的超时和重试。常见的形状包括:营业时间逐步增加、早晨尖峰、稳定浸泡、来自另一个服务的集成突发、批量导入,以及混合的交互式和后台流量。每个选项都陈述其数字:用户数、生成速率、思考时间、持续时间和请求组合。当项目没有说明其流量时,请说明,命名每个数字所依赖的假设,并在 Q3 中询问预期的峰值。references/tiers-and-scenarios.md 展示了如何将配置转化为这些数字。在询问之前阅读它。

在回答之后,用纯文本解决两个问题:

  • 凭证。 它们来自测试工具读取的文件或环境变量。切勿粘贴到聊天中。
  • 约束。 列出您发现的,并询问其余部分:速率限制、许可或激活窗口、重启成本、共享租户以及每个请求的成本(模型令牌、付费 API)。许可窗口内的重启可能会将服务锁定一天,而几个并行的高负载作业加上一个巨大的输入可能会使目标宕机。在加载之前询问,而不是之后。
  • 数据状态。 在实际使用中列表和搜索需要多少行数据,数据是否可以在运行之间重置,以及测试用户可以编辑或删除谁的行。空表使列表页面成本为零,而运行添加的行会使每个后续步骤读取更多内容。

4. 构建测试工具

mkdir -p loadtest && cd loadtest
cp $SKILL/templates/pyproject.toml . && uv sync   # 它自己的 .venv 和 uv.lock;locust 固定版本,Python 3.11+
cp $SKILL/templates/{harness.py,run_local.sh,run_plan.sh,collect.sh,seed.py,loadtest.env.example} .
cp $SKILL/templates/run_docker.sh .      # 当生成器在目标的 Docker 网络上运行时
cp -r $SKILL/templates/{run_k8s.sh,k8s} .  # 当它在目标的集群内运行时
git -C .. status --short                 # 只有 loadtest/ 可能显示

不要在此处运行 uv init。在本身就是一个 uv 项目的存储库中,它会将 loadtest 添加到应用的 [tool.uv.workspace],将 locust 安装到应用的 .venv 中,并写入应用级别的 uv.lock。如果发生了这种情况,请撤消:在应用中 git checkout -- pyproject.toml,删除新的 uv.lock,然后在那里运行 uv sync。然后填写模板:

  • REQUESTS。 根据 PLAN.md 填写 harness.py 中的表:层级、名称、权重、组合、请求体和预期状态。从项目的测试、fixtures 和模式中获取载荷,并将其放在 loadtest/payloads/ 中。切勿使用生产数据。服务器渲染的应用对其登录页面和错误页面也会返回 200,因此检查只有正确页面才有的内容:expect_text,以及重定向作为答案时的 expect_location。重定向但未说明的行会失败。
  • REQ_TIMEOUT。 将其设置为真正调用者的超时时间:网关、前端的 abort、客户端 SDK 或代理的读取超时。测试工具将每个请求都保持为此截止时间,因此较晚的答案会作为“deadline exceeded”失败,而不是计为产品本会报告为失败的成功。
  • MIX。 为每个具有不同成本流量组合赋予自己的标签,并将每个组合作为自己的场景运行(短聊天大小的输入、文档大小的输入、额外成本的选项)。它们的容量可能相差数倍,混合运行会隐藏昂贵的那个。
  • Auth。 设置 AUTH,使每个模拟用户只登录一次,而不是每个请求一次。带有会话 cookie 的表单登录还需要 LOGIN_EXPECT_LOCATION 或 LOGIN_EXPECT_TEXT,因为错误的密码也会返回 200(参见 loadtest.env.example)。登录进入阶段 login,并保持在步骤编号和停止规则之外:每个登录都落在添加其用户的步骤的开头,并且慢速密码哈希会在稳定负载永远不需要支付的成本上结束运行。要加载登录本身,请将其设为一行。
  • 数据。 为应用填写 seed.py 并运行 python3 seed.py up --users N --rows M:通过应用的 API,每个测试帐户获得自己的行,SEED_MANIFEST 将其交给其用户;行键 capture 和 consume 跟踪用户创建和删除的内容。将创建与删除配对,或在运行之间重新播种。
  • 旅程。 多步骤流程(上传然后轮询、创建然后读取)是用户类上的一个方法;模板中有一个示例。

references/harness.md 涵盖了每个字段、形状、认证模式、流式传输、非 HTTP 协议和三种部署方式。在构建时阅读它。

5. 安全运行

  1. 先冒烟。 运行 RUN_NAME=smoke SHAPE=smoke ./run_local.sh:每个用户类一个用户,按表顺序发送每一行(将创建 id 的行放在使用该 id 的行之前),然后空闲。日志以 NOTE smoke: every row was sent 结尾,或者列出未发送的行。检查零失败、合理的延迟和正确的响应体。它证明了连接正常,然后才能承受负载成本。
  2. 检查机器是否空闲。 在运行测量容量之前,在生成器和目标主机上运行 uptime 和 vmstat 1 5。同一内核上的其他作业会使每个数字成为争用的度量。测试工具在每次运行之前将其自身的主机采样到 run.json(host_check 和一个注释);当目标是另一台机器时,将目标主机的数字放入 NOTES。报告会在其步骤期间主机或生成器 CPU 达到 90% 的运行上放置横幅,并对其拐点进行说明。如果机器繁忙,等待,将其固定到空闲内核(taskset),或移动,并说明。
  3. 然后运行所选层级,最安全的优先。 先运行预期负载,然后是浸泡,然后是压力,最后是尖峰。高风险输入(最大的载荷、额外成本的选项)放在最后,这样如果它们损害了目标,之前的所有内容都不会丢失。run_plan.sh 按顺序运行列表,并且仅在目标健康检查通过后才开始每次运行。
  4. 将生成器放置在真实调用者附近 当网络路径很重要时:同一集群、VNet 或 compose 网络(run_k8s.sh、run_docker.sh)。笔记本电脑运行测量的互联网和服务一样多。每次运行都会记录 GENERATOR,生成器自身的 CPU 会进入 metrics/client.csv;当 CPU 超过约 90% 时,数字描述的是生成器。
  5. 遵守停止规则。 当失败率超过 STOP_FAIL_RATIO,当 p95 超过 STOP_P95_MS,当过去 20 秒有超过一半失败,或者当大多数登录失败时,测试工具会结束运行。许多服务将请求排队而不是丢弃负载:零错误,但延迟只会增长。因此将 p95 上限设置为调用者的超时时间或 SLO。不要“为了看看”而推动超越停止规则。
  6. 在运行之间监视健康状态 并让队列排空(run_plan.sh 中的 COOLDOWN)。
  7. 清理您创建的所有内容: ./run_k8s.sh down(Pod、ConfigMap、Secret)、停止的容器、测试用户和测试数据。为用户列出任何无法删除的内容,例如软删除的行或审计条目。

在第一次运行超出冒烟测试之前,以及在任何针对共享系统的运行之前,阅读 references/safety.md。

6. 收集服务器端数据

客户端数字说明发生了什么。服务器数字说明为什么。在 locust 之前启动收集器,在 locust 之后停止它们,并写入相同的运行目录:

uv run $SKILL/scripts/...

相关技能