Files
sundynix-agentix/LOAD_TEST_REPORT.md
T
Blizzard 468107ea7a docs: 容量压测报告 + 架构拆分验证(LOAD_TEST_REPORT.md)
把本次压测的方法/数据/结论独立成报告落项目根目录:单节点平台天花板 ~110 任务/s、
单任务开销 ~42ms、瓶颈是多跳管线而非 DB 连接、C=256 崩于 DSN localhost DNS 抖动(易修);
正面验证'平台不是瓶颈 GPU 才是'→ 横向拆服务+队列组喂满 GPU 集群的设计成立,并列出
网关 HA / PG 状态写 / 集群复测三个待补点。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-26 11:57:57 +08:00

6.2 KiB
Raw Blame History

sundynix-agentix 容量压测报告(首版)

日期:2026-06-26 环境:单机本地(macOS / Docker 基建)| 撰写:Opus 4.8(基于本次实测,刻意压低水分) 部署相关讨论留待开发完成后另议;本报告只回答一个问题:平台自身扛得住多少、瓶颈在哪。


1. 目的

回答两个一直悬而未决的问题:

  1. 平台(去掉 LLM 推理本身)的吞吐天花板是多少?单任务固有开销多大?
  2. 把系统拆成「网关 / 总线 / 调度 / 工具」多服务,到底有没有意义?

2. 方法

  • 加压工具sundynix-dispatcher/cmd/loadtest。闭环加压(N 个 worker 不停 submit → 等收尾 → 再来), 阶梯提升并发,每档跑 10s。
  • 完成判定:经 SSE token 流读到收尾(而非轮询状态)。

    早期版本用 25ms 轮询,结果把"轮询放大"自身测成了瓶颈(海量 status 请求压垮网关); 改用流后每任务仅 1 条推送连接,量到的才是真实任务管线吞吐。

  • 隔离 LLMdispatcher 开 LLM_FORCE_STUB=1 + LLM_STUB_TTFT_MS=0 + LLM_STUB_INTERTOKEN_MS=0 绕开真实模型推理与计费,只压全链路 plumbing 网关 HTTP → NATS/JetStream → 调度消费 → 图执行 → Token 回流(SSE) → PG 状态写
  • 配置:单 dispatcher 实例,DISPATCHER_CONCURRENCY=64,网关放开限流(RATE_LIMIT_PER_MIN)。

3. 实测数据

并发 吞吐 (任务/s) p50 p95 max 说明
1 23 42ms 46ms 54ms 单任务纯平台开销 ≈ 42ms
2 37 53ms 56ms 70ms
4 54 74ms 77ms 83ms
8 76 103ms 115ms 157ms
16 96 161ms 176ms 257ms
32 112 269ms 345ms 402ms 吞吐峰值
64 112 557ms 676ms 707ms 饱和:延迟翻倍、吞吐不增
128 102 1173ms 1590ms 1629ms 过载,优雅降速(未崩)
256 0 硬崩(见 §4.3

吞吐随并发上升到 ~32 后见顶(~110/s),之后增并发只增延迟不增吞吐 —— 典型饱和曲线。


4. 分析

4.1 单节点平台天花板 ≈ 110 全链路任务/秒

单任务固有开销 ~42ms,由这些串起来:网关收请求 → JetStream 持久化发布(含 fsync)→ 调度消费 → 建图执行 → Token 经 core NATS 回流 + Redis 录制 → 3 次 PG 状态写(submitted / running / done)。

4.2 瓶颈不是数据库连接数,是「每任务多跳」

把 PG 连接池从 25 调到 80,吞吐纹丝不动(仍 ~110/s)。说明瓶颈不在某一处资源,而是 每个任务要走的一长串同步跳数JetStream 落盘 + 多次 NATS 回写 + Redis 录制 + 多次 PG 写)的累加成本。 要把单节点吞吐再往上抬,得减少每任务的同步跳数(如状态写批量化/异步化),而非简单加资源。

4.3 C=256 硬崩的根因(易修)

网关日志:failed to connect ... lookup localhost: operation was canceled。 DSN 用了主机名 localhost,pgx 每新建一条连接都做一次 DNS 解析;极端并发下连接抖动 → 大量 DNS 查询 在请求超时里被取消 → 雪崩。修法简单DSN 改 127.0.0.1(免 DNS)+ 连接复用调优。属配置级修复,非架构问题。

4.4 核心判断:平台不是瓶颈,GPU 才是

  • 平台固有开销 ~42ms / 任务
  • 真实 LLM 出一轮答案(哪怕自部署 Qwen 32B)是 秒级(几百 ms ~ 数秒)。
  • 平台开销比 LLM 推理小 1~2 个数量级,淹没在模型延迟里、可忽略。

→ 一个 dispatcher 副本就能喂 ~110 turns/s 的调度能力;而单张 GPU 跑 32B 大约出 几十 turns/s平台调度能力 > 单 GPU 产能,意味着:

  • 平台永远不会先于 GPU 成为瓶颈;
  • 要提总吞吐,正确做法是加 dispatcher 副本 + 加 GPU(NATS 队列组自动负载均衡),而非优化平台代码。

5. 架构 / 服务拆分是否成立?—— 成立

本次压测正面验证了"拆服务"的设计前提:

设计选择 压测给出的判断
网关 / 调度 / 工具 拆成独立服务 成立。各层可独立横向扩;调度是无状态消费者,加副本即扩吞吐
NATS 队列组做负载均衡 成立。这是"加副本即扩容"的基础,且天花板(110/s)远高于单 GPU 产能
调度与工具用消息总线解耦 成立。42ms 的总线开销相对 LLM 秒级延迟可忽略,换来的解耦/弹性是划算的
dispatcher 不持有 DB、状态经总线回网关落库 成立。让调度保持无状态、可水平复制

一句话:对"喂满自部署 GPU 集群、按 LLM 产能横向扩"这个生产目标,当前拆分是对的。 总线那 ~42ms 的代价,买到了无状态、可水平扩、故障隔离 —— 在 LLM 秒级延迟面前完全值。

但有三个要正视的点(不是"拆错了",是"拆了之后要补齐的")

  1. 网关目前是单点且偏重HTTP + 每任务一个 Token 录制 goroutine + 多个订阅者)。横向扩/HA 还没做(P1)。
  2. PG 是全局共享瓶颈:每任务 3 次状态写。规模再上一个量级时,状态写需要批量/异步化。
  3. 单机数字 ≠ 集群数字:本报告是单节点天花板;多副本下还需验证 NATS 集群、PG 连接总量、跨副本一致性。

6. 待补(后续)

  • 长稳压测(小时级):看内存 / 文件句柄 / goroutine 是否泄漏。
  • 真实 LLM 端到端容量:受在线 provider 限流,需自部署模型后再测。
  • DSN localhost → 127.0.0.1(顺手修,消除 §4.3 崩溃)。
  • 状态写批量/异步化探索(抬高单节点天花板)。
  • 多副本 + NATS 集群下的容量复测。

复现

# dispatcher 以 stub 模式起(绕开 LLM),网关放开限流
LLM_FORCE_STUB=1 LLM_STUB_TTFT_MS=0 LLM_STUB_INTERTOKEN_MS=0 DISPATCHER_CONCURRENCY=64 ./sdx-dispatcher
RATE_LIMIT_PER_MIN=1000000 ./sdx-gateway

# 加压(阶梯并发,每档 10s
go run ./cmd/loadtest -url http://localhost:8080 \
  -email <user> -pass <pass> -levels 1,2,4,8,16,32,64,128,256 -dur 10s