topfans/backend/scripts/loadgen
2026-06-16 22:48:32 +08:00
..
loadgen feat:修改压测 2026-06-16 22:19:44 +08:00
monitor feat:添加压测工具脚本 2026-06-15 15:28:25 +08:00
recover feat:添加压测工具脚本 2026-06-15 15:28:25 +08:00
scripts feat:修改压测 2026-06-16 22:19:44 +08:00
seed feat:修改压缩工具的配置 2026-06-16 22:48:32 +08:00
README.md feat:修改压测配置 2026-06-15 20:10:56 +08:00
REPORT_GUIDE.md feat:修改压测配置 2026-06-15 20:10:56 +08:00
RUNBOOK.md feat:修改压测 2026-06-16 22:19:44 +08:00

后端服务压测工具 (loadgen)

给阿里云单机 (4G/2C) TopFans 后端微服务用的压测 + 数据准备工具集。 凌晨 02:00-06:00 业务低峰执行,数据物理隔离 star_id=999900


📚 文档地图

文档 用途 谁要看
README.md (本文) 工具集概览 + 5 分钟入门 所有人
RUNBOOK.md 凌晨压测一步一步操作手册 on-call 工程师
REPORT_GUIDE.md 压测报告怎么读 + 瓶颈定位 + 行动项模板 看报告的工程师 / TL
seed/README.md seed 工具细节 (数据准备) 第一次跑压测的人

🧰 工具集概览

loadgen/
├── seed/      # 数据准备 CLI (生成 1000 个测试用户 + 资产 + JWT)
├── loadgen/   # 压测主程序 (7 个场景,6 维熔断,带 reporter)
├── monitor/   # 监控栈 (Prometheus + Grafana,可选)
├── recover/   # 紧急灭火 (一键停 + 数据库恢复)
├── scripts/   # 部署到 prod 的辅助脚本
└── reports/   # 跑测产出 (gitignore,scp 拉回本地)

核心 CLI: bin/seed + bin/loadgen

命令 作用
./bin/seed 灌测试数据 → users.csv + 数据库
./bin/seed --cleanup 清理测试数据 (保留 1000 用户)
./bin/seed --cleanup --full 全部删掉 (账号本身)
./bin/seed --reset-tokens 只重签 JWT (跨周压测用)
./bin/loadgen --cmd=preflight 7 项开压前检查
./bin/loadgen --cmd=run --scenarios=S1 跑场景
./bin/loadgen --cmd=report 生成 markdown 报告 + PNG 图表

7 个场景

ID 场景 默认 RPS 写/读 关键 API
S1 Login 15 写(轻) POST /api/v1/auth/login
S2 Read 250 GET /api/v1/assets/{id}
S3 Like 50 写(轻) POST/DELETE /api/v1/social/assets/{id}/like
S4 Mint 1-5 写(重) POST /api/v1/assets/mints/precreate
S5 Dashboard 读聚合 (dashboard 聚合)
S6 Ranking 300 GET /api/v1/rankings/hot
S7 Place 1-5 写(重) (摆展事务)

🚀 5 分钟入门 (本地 docker)

# 1. 编译 (Linux prod 部署用,本地 darwin 直接 go build)
cd backend
make loadgen-build

# 2. 准备数据 (需要本地 docker postgres)
cd scripts/loadgen/seed
# 生成 bcrypt 哈希 (与 tokens.go 硬编码的 "Test@123" 匹配)
python3 -c "import bcrypt; print(bcrypt.hashpw(b'Test@123', bcrypt.gensalt(rounds=10)).decode())" \
  > loadtest_bcrypt.txt
# 跑 seed (用本地 docker 的 env)
DB_PASSWORD=123456 \
JWT_SECRET=topfans-secret-key-local-dev-only \
/Users/liulujian/Documents/code/TopFansByGithub/backend/bin/seed \
  --db-name=top-fans --db-host=localhost --db-port=15432 --db-user=postgres

# 3. 复制 users.csv 到 backend 目录
cp users.csv ../../../users.csv

# 4. 开压前检查
cd ../../../  # = backend
JWT_SECRET=topfans-secret-key-local-dev-only \
  ./bin/loadgen --cmd=preflight --target=http://localhost:8080

# 5. 烟雾测试 (30 秒,1 RPS)
JWT_SECRET=topfans-secret-key-local-dev-only \
  ./bin/loadgen --cmd=run --scenarios=S1 --stage=baseline --rps=1 --duration=30s \
    --target=http://localhost:8080 --monitor=off

# 6. 生成报告
JWT_SECRET=topfans-secret-key-local-dev-only \
  ./bin/loadgen --cmd=report --input=./reports --output=./reports/final-report.md
open reports/final-report.md  # macOS

🔨 编译

cd backend
make loadgen-build    # 编译 seed + loadgen 到 bin/
make loadgen-test     # 单元测试 (23 个)
make loadgen-vet      # go vet
make loadgen-ci       # vet + test + build (CI 单步)

手动编译 (Linux prod):

GOOS=linux GOARCH=amd64 go build -ldflags="-s -w" -o bin/seed ./scripts/loadgen/seed/
GOOS=linux GOARCH=amd64 go build -ldflags="-s -w" -o bin/loadgen ./scripts/loadgen/loadgen/

🛡️ 安全设计

数据隔离

所有测试数据用 star_id = 999900 物理隔离,不影响真实业务 star_id (87, 88, 91, 93, 94, 95)。

CLAUDE.md 序列重置

seed 工具末尾自动同步所有相关表的 PG 序列(避免后续 GORM 插入报 duplicate key)。

凌晨窗口

执行窗口:02:00 - 06:00 业务低峰。 紧急灭火: recover/emergency-stop.sh 一键停 + restore-from-backup.sh 5-8min 还原。

6 维红线熔断 (自动停)

# 红线 阈值 数据源
R1 客户端错误率 > 5% 持续 30s loadgen HDR
R2 客户端 P99 > 3000ms 持续 30s loadgen HDR
R3 5xx 比例 > 10% 持续 10s loadgen status
R4 PG 连接数 > 42 持续 30s metrics-feed
R5 磁盘空闲 < 5GB 持续 30s metrics-feed
R6 OOM 事件 瞬时触发 metrics-feed

📊 报告产出

跑完 + --cmd=report 后,reports/ 下:

reports/
├── S1.json              # 原始数据 (含 stages)
├── S2.json
├── S4.json
├── baseline.csv         # Excel 友好的汇总
├── s1.png               # RPS / P99 / Error 曲线
├── s2.png
├── s4.png
└── final-report.md      # ← 主要看这个

final-report.md 包含:

  1. 总览表 (所有场景一行一个,7 列)
  2. 每个场景的 ⚠️ 拐点 RPS (自动算:第一个 p99 涨 >50% 的 stage)
  3. 阶梯结果表 (每 stage 的 RPS / p50 / p95 / p99 / err / 5xx)
  4. PNG 曲线图 (RPS / P99 / Error 三条线)

详细读法见 REPORT_GUIDE.md


🧪 测试状态

seed:        5/5  PASS
loadgen/lib: 16/16 PASS
scenarios:   2/2  PASS
TOTAL:       23/23 PASS

📁 完整目录

backend/scripts/loadgen/
├── README.md                  # ← 你在这里
├── RUNBOOK.md                 # ← 凌晨压测操作手册
├── REPORT_GUIDE.md            # ← 报告怎么读
├── seed/                      # 数据准备工具
│   ├── main.go                # CLI 入口
│   ├── stars.go users.go profiles.go assets.go
│   ├── slots_and_exhibits.go friendships.go
│   ├── tokens.go sequences.go cleanup.go
│   ├── seed_test.go           # 单元测试
│   ├── loadtest_bcrypt.txt    # Test@123 哈希 (与 tokens.go 匹配)
│   └── README.md
├── loadgen/                   # 压测主程序
│   ├── main.go                # CLI 入口
│   ├── preflight.go verify.go # 7 项开压前检查 + 压后验证
│   ├── lib/                   # 核心库
│   │   ├── csv.go             # users.csv 解析
│   │   ├── client.go          # HTTP client
│   │   ├── hdr.go             # 延迟直方图 + per-stage 计数
│   │   ├── log.go ramp.go     # 日志 + 阶梯调度
│   │   ├── circuit.go         # 6 维熔断
│   │   ├── ssh_metrics.go     # prod server metrics 抓取
│   │   ├── config.go
│   │   └── *_test.go          # 16 个测试
│   ├── scenarios/             # 7 个场景
│   │   ├── s1_login.go
│   │   ├── s2_read.go
│   │   ├── s3_like.go
│   │   ├── s4_mint.go         # 支持多 stage
│   │   ├── s5_dashboard.go
│   │   ├── s6_ranking.go
│   │   ├── s7_place.go
│   │   ├── common.go          # doRequest + DefaultBaseURL
│   │   ├── scenarios.go       # 注册表
│   │   ├── helpers.go
│   │   └── scenarios_test.go
│   └── reporter/              # 报告生成
│       ├── json.go            # RunReport + StageReport
│       ├── csv.go             # baseline.csv
│       ├── plot.go            # PNG 曲线 (gonum)
│       ├── markdown.go        # final-report.md
│       └── knee.go            # KneeRPS 自动算
├── monitor/                   # 监控栈 (可选)
│   ├── sample.sh              # 后台采样到 metrics-feed.jsonl
│   ├── docker-compose.monitor.yml
│   ├── prometheus.yml
│   └── grafana-dashboards/    # 4 个预置面板
├── recover/                   # 紧急灭火
│   ├── emergency-stop.sh
│   └── restore-from-backup.sh
├── scripts/                   # prod 辅助
│   ├── mint_reset.sh          # S4 之间的 mint 数据清理
│   └── prod_seed.sh           # 一键跑 seed (读 prod env)
└── reports/                   # 跑测产出 (gitignore)

详细设计

  • 设计文档: docs/superpowers/specs/2026-06-12-load-testing-design.md
  • 实施计划: docs/superpowers/plans/2026-06-12-load-testing.md
  • seed 工具说明: seed/README.md