☰
Sim 工作流异步执行压测指南:用 Artillery 验证队列与并发隔离
2026/10/15 7:50:36 网站建设 项目流程

Sim 工作流异步执行压测指南:用 Artillery 验证队列与并发隔离

【免费下载链接】simSim is the collaborative workspace to build, deploy, and monitor AI agents and workflows. Used by 100,000+ builders.项目地址: https://gitcode.com/GitHub_Trending/sim16/sim

本篇技术指南围绕 Sim 仓库内置的本地压测场景(apps/sim/scripts/load/)展开,讲解如何针对POST /api/workflows/[id]/execute异步执行接口进行基线并发、队列冲击波与跨工作空间隔离三类压测。读完本文,你将掌握各场景的默认参数、全部可调环境变量、底层准入控制(Admission Gate)的工作原理,以及如何根据429/ETIMEDOUT结果调整压测强度。

压测前置条件与环境准备

这些 Artillery 场景是本地专用(local-only)的,全部针对POST /api/workflows/[id]/execute的异步模式(async mode)进行施压。开始前需要满足三项要求:

  1. 本地运行应用:使用仓库根目录package.json中定义的dev:full脚本启动完整开发环境(同时拉起 App 与 Realtime 两个进程):
    bun run dev:full

    仓库中还提供了dev:full:capped变体(package.json),可在需要限制本地资源时使用。

  2. 有效的工作流 ID 与 API Key:每个场景都需要传入目标工作流的WORKFLOW_ID以及对该工作流所在工作空间有执行权限的 API Key。
  3. 默认目标地址:所有场景默认请求http://localhost:3000,可通过BASE_URL环境变量覆盖。

默认压测速率是针对本地限流参数ADMISSION_GATE_MAX_INFLIGHT=500调校的。该准入阀(admission gate)会限制每个 Pod的在途(in-flight)请求总数。

补充:底层实现准入阀的源码位于 apps/sim/lib/core/admission/gate.ts。它是一个纯进程内(in-process)原子计数器:MAX_INFLIGHT通过Number.parseInt(env.ADMISSION_GATE_MAX_INFLIGHT ?? '') || 500解析,默认值为 500。每个 Pod 维护自己的计数器,因此 N 个 Pod 的聚合上限是N × MAX_INFLIGHT——源码注释明确建议“按每个 Pod 能承受的量来配置ADMISSION_GATE_MAX_INFLIGHT”。 在 apps/sim/app/api/workflows/[id]/execute/route.ts 中,POST处理器会先调用tryAdmit()获取 Admission Ticket,拿不到则直接返回admissionRejectedResponse()(即 429);拿到后在finally中调用ticket.release()归还名额。会话型请求(带 cookie)不经过准入阀,而 API Key / 公开 API / 内部 JWT 调用都会受其约束。

关于 429 响应:可安全重试的准入失败

当请求被准入阀拒绝时,服务端返回的 429 响应体与Retry-After头由 apps/sim/lib/core/admission/transient-failure.ts 定义:

失败类别状态码code是否可自动重试Retry-After
准入阀容量(GATE_CAPACITY)429ADMISSION_GATE_CAPACITY是5 秒
计费方并发(RESERVATION_CONCURRENCY)429EXECUTION_CONCURRENCY_LIMIT是5 秒
计费方余量不足(PAYER_HEADROOM)402EXECUTION_PAYER_HEADROOM_EXHAUSTED否—
成员余量不足(MEMBER_HEADROOM)402EXECUTION_MEMBER_HEADROOM_EXHAUSTED否—
基础设施过载(INFRASTRUCTURE)503SERVICE_OVERLOADED是5 秒

classifyTransientAdmissionFailure()只把retryable === true且 code 匹配的失败识别为“可安全重试的瞬态准入失败”,这对理解压测中出现的 429/503 含义很有帮助:429 通常意味着“再等等(约 5 秒)重试即可”,而 402 表示余量类硬性拒绝,不应盲目重试。

场景一:基线并发(Baseline Concurrency)

该场景用于向单个工作流逐步加压,观察正常的排队(queueing)行为。

默认施压曲线:

  • 起始2请求/秒
  • 爬坡至8请求/秒
  • 以8请求/秒保持20秒
  • 该曲线适合用于验证针对Free 工作空间并发数为 5的排队表现

运行命令:

WORKFLOW_ID=<workflow-id> \ SIM_API_KEY=<api-key> \ bun run load:workflow:baseline

可选环境变量(均带默认值,见 apps/sim/package.json):

变量默认值说明
BASE_URLhttp://localhost:3000压测目标地址
WARMUP_DURATION10爬坡阶段时长(秒)
WARMUP_RATE2爬坡起始速率(请求/秒)
PEAK_RATE8峰值速率(请求/秒)
HOLD_DURATION20峰值保持时长(秒)

更高套餐工作空间的本地起点:

  • Pro:PEAK_RATE=20到40
  • Team 或 Enterprise:PEAK_RATE=50到100

YAML 结构解析:场景配置位于 apps/sim/scripts/load/workflow-concurrency.yml。它定义了两个 phase:baseline-ramp(duration=WARMUP_DURATION、arrivalRate=WARMUP_RATE、rampTo=PEAK_RATE)和baseline-hold(duration=HOLD_DURATION、arrivalRate=PEAK_RATE)。请求体通过{{ $processEnvironment.WORKFLOW_ID }}、{{ $processEnvironment.SIM_API_KEY }}注入环境变量,runId使用 Artillery 内建的{{ $uuid }}保证每次请求唯一。默认请求头包含content-type: application/json、x-api-key与x-execution-mode: async。

场景二:排队冲击波(Queueing Waves)

该场景用于向同一工作空间中的单个工作流反复发送突发流量,检验排队与吞吐的承受能力。

默认施压曲线:

阶段速率时长
Wave 16请求/秒10秒
静默间隔1请求/秒5秒
Wave 28请求/秒15秒
静默间隔1请求/秒5秒
Wave 310请求/秒20秒

运行命令:

WORKFLOW_ID=<workflow-id> \ SIM_API_KEY=<api-key> \ bun run load:workflow:waves

可选环境变量(见 apps/sim/package.json):

变量默认值说明
BASE_URLhttp://localhost:3000压测目标地址
WAVE_ONE_DURATION10第一波时长(秒)
WAVE_ONE_RATE6第一波速率(请求/秒)
QUIET_DURATION5两波之间的静默时长(秒)
WAVE_TWO_DURATION15第二波时长(秒)
WAVE_TWO_RATE8第二波速率(请求/秒)
WAVE_THREE_DURATION20第三波时长(秒)
WAVE_THREE_RATE10第三波速率(请求/秒)

YAML 结构解析:apps/sim/scripts/load/workflow-waves.yml 定义了 5 个 phase:wave-one、quiet-gap、wave-two、quiet-gap-two、wave-three。其中静默阶段用arrivalRate: 1保持极低流量而非完全归零,便于观察队列在低负载窗口的消化能力。请求体除source与runId外,还携带了waveProfile: single-workspace标记,便于在服务端日志中区分不同压测场景的请求来源。

场景三:双工作空间隔离(Two-Workspace Isolation)

该场景用于同时向来自不同工作空间的两个工作流发送混合流量,对比观察:一个工作空间承受队列压力时,是否会对另一个工作空间产生可见影响。

默认施压曲线:

  • 总速率:9请求/秒,持续30秒
  • 权重分配:8 : 1
  • 实际效果:工作空间 A 承受重压,同时工作空间 B 仍保持轻量流量

运行命令:

WORKFLOW_ID_A=<workspace-a-workflow-id> \ SIM_API_KEY_A=<workspace-a-api-key> \ WORKFLOW_ID_B=<workspace-b-workflow-id> \ SIM_API_KEY_B=<workspace-b-api-key> \ bun run load:workflow:isolation

注意:该场景需要两组独立的工作流 ID 与 API Key,分别对应工作空间 A 与 B。

可选环境变量(见 apps/sim/package.json):

变量默认值说明
BASE_URLhttp://localhost:3000压测目标地址
ISOLATION_DURATION30混合施压时长(秒)
TOTAL_RATE9总请求速率(请求/秒)
WORKSPACE_A_WEIGHT8工作空间 A 的流量权重
WORKSPACE_B_WEIGHT1工作空间 B 的流量权重

YAML 结构解析:apps/sim/scripts/load/workflow-isolation.yml 定义了两个带weight的场景:workspace-a-traffic(默认权重 8)与workspace-b-traffic(默认权重 1),Artillery 会按权重比例分配TOTAL_RATE的流量。两个场景各自通过独立的WORKFLOW_ID_A/SIM_API_KEY_A与WORKFLOW_ID_B/SIM_API_KEY_B环境变量注入目标与鉴权头,并在请求体中以workspace: a/workspace: b标记来源。x-execution-mode: async位于config.defaults.headers,两个场景共用。

压测输出解读与调优建议

Artillery 输出会展示请求计数与响应码分布,对于本地快速验证通常已经足够。README 给出了两条核心判读经验:

  1. 看到 429 是预期行为:在默认参数下,当在途请求接近ADMISSION_GATE_MAX_INFLIGHT=500时,你会看到 429 响应。这正是准入阀按预期工作的信号——它把超过容量的请求挡在门外并让调用方稍后重试。
  2. 429 或 ETIMEDOUT 过多时先降速:如果本地压测出现大量429或ETIMEDOUT,应先把速率调低,再考虑延长持续时间,而不是继续加大压力。这是一种稳健的“先验证正确性、再验证容量”的思路。

从源码角度看,这个建议与准入阀的进程内计数器设计一致:tryAdmit()在inflight >= MAX_INFLIGHT时直接返回null,路由随即返回 429(apps/sim/app/api/workflows/[id]/execute/route.ts)。本地开发机资源有限时,降低PEAK_RATE等速率参数比单纯延长HOLD_DURATION更能稳定复现排队行为。

附:场景速查表

场景命令别名目标默认速率典型观察点
基线并发load:workflow:baseline(load:workflow为其别名)单工作流2 → 8 req/s,保持 20s正常排队行为、Free 并发 5 下的表现
排队冲击波load:workflow:waves单工作流(同工作空间)6 / 8 / 10 req/s 三波突发流量下的队列消化能力
双工作空间隔离load:workflow:isolation两个工作空间的工作流9 req/s,权重 8:1工作空间间的队列压力隔离

三个场景的公共行为:全部发送x-execution-mode: async请求头;均通过bun run <script>调用 apps/sim/package.json 中定义的脚本,底层由 Artillery 2.0.34 执行位于 apps/sim/scripts/load/ 的 YAML 场景文件;$processEnvironment机制让所有可调参数都以环境变量形式注入,无需修改任何配置文件即可完成多轮不同强度的压测。

【免费下载链接】simSim is the collaborative workspace to build, deploy, and monitor AI agents and workflows. Used by 100,000+ builders.项目地址: https://gitcode.com/GitHub_Trending/sim16/sim

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询