系列12-接口压测怎么做成平台能力?自研 httpx 引擎、分布式 Worker 与 Locust 选型对照
团队做性能测试,常见两条路:
- Locust / 自写脚本 :灵活,但 URL/Body/Token 与接口回归 两套维护,报告散落本地 HTML
- JMeter GUI:功能全,但 XML 难维护,CI 集成重
BrickCore 选第三条:复用接口自动化用例(ApiTestCase)+ asyncio/httpx 自研压测引擎 ,可选 Perf Worker 水平加并发。
需要先说清一点:平台 没有嵌入 Locust ,Docker 镜像也不带 Locust 依赖;执行层借鉴 Locust 的 并发用户 / Ramp-up / 分布式 Worker 产品思路,HTTP 请求与变量替换与 run_single_case 同源 (build_perf_request → httpx)。
演示与源码
| 地址 | |
|---|---|
| 功能演示 | http://43.142.83.156/showcase/ (性能测试相关说明见文档中心;平台 admin / BrickCore123456) |
| 开源仓库 | https://gitee.com/BanZhuanKeOrz/BrickCore |
线上路径:性能测试 → 场景管理 / 执行机 / 执行记录 。Worker 推荐 BrickCoreRunner v1.3.14+ 选「压测执行机」角色上线。
一、平台化压测要解决什么
| 脚本压测痛点 | 平台化做法 |
|---|---|
| Swagger 已导入,压测又要抄一遍 URL | 场景 引用已有 ApiTestCase |
| 测试/预发 Host、Token 各写各的 | Environment + Token 授权 + merge_execution_variables |
| 报告不在统一看板 | PerfRecord 落库 + 曲线 + 多轮对比 |
| 单机并发不够 | Perf Worker 注册、按比例拆并发 |
| 大模型 SSE 接口只看总 RT | stream_burst + qa_sse_v1 阶段指标 |
二、架构一图读懂
PerfScene(场景:scene_items + config + csv)
↓ POST /perf/exec/{scene_id}?use_workers=
PerfRecord(config_snapshot 不可变快照)
↓ run_perf_scene(BackgroundTasks)
┌─────────────────┬──────────────────────────┐
│ 本机 asyncio │ 分布式 use_workers=true │
│ Backend httpx │ distribute_concurrent │
│ 虚拟用户协程池 │ → Perf Worker(perf_worker)│
└─────────────────┴──────────────────────────┘
↓ 秒级聚合 time_series_data
报告:QPS / P95 / error_rate / case_aggregations / phase_metrics
与 UI Runner 分工 :Perf Worker 只跑 HTTP 压测;UI Runner 跑 Playwright,队列协议不同(系列09)。
三、数据模型
PerfScene
├─ scene_items: [{case_id, weight, delay_ms}, ...]
├─ config: {mode, concurrent_users, ramp_up_seconds, ...}
└─ csv_data + csv_config
PerfRecord(一次执行)
├─ config_snapshot / scene_items_snapshot
├─ total_requests, qps, p95, error_rate, time_series_data
├─ case_aggregations(接口维度)
├─ phase_metrics / request_details(SSE 场景)
└─ distribution_info(分布式 Worker 分配)
PerfWorker
├─ register / heartbeat / unregister
├─ max_concurrent
└─ project_id(须与场景项目一致)
快照设计 :场景事后被改,历史报告仍对应 当时 的配置与用例列表------发版对比靠 record_id,不靠「还记得上次参数」。
四、五种压测模式
| mode | 说明 | 典型场景 |
|---|---|---|
fixed |
固定并发 + 持续时长(秒) | 稳态容量验证 |
loop |
固定并发 + 每用户循环 N 次 | 固定请求总量 |
stepping |
多阶段递增并发与时长 | 梯度加压、找拐点 |
stream_burst |
每虚拟用户 1 次 SSE 流式请求,采集问答各阶段耗时 | 大模型 / 流式 API |
journey_fixed / journey_loop |
业务链路:多阶段顺序执行,步骤间 extractors 传参 | 登录→业务接口 |
固定/循环/梯度模式还可勾选 流式问答 :在持续压测中对指定用例走 SSE 引擎(use_stream_execution)。
场景项示例 (权重 + 思考时间,概念对齐 Locust @task(weight) + between):
json
[
{"case_id": 101, "weight": 3, "delay_ms": 500},
{"case_id": 102, "weight": 1, "delay_ms": 0}
]
配置示例:
json
{
"mode": "fixed",
"concurrent_users": 50,
"ramp_up_seconds": 10,
"duration_seconds": 120,
"error_rate_threshold": 5,
"request_detail_level": "brief"
}
error_rate_threshold:错误率 连续 3 秒 超阈值自动停压(workers.py 中 ERROR_RATE_AUTO_STOP_MSG)。
五、执行链路(源码)
5.1 启动
python
# routers/perf/exec.py --- start_perf
record = await PerfRecord.create(
scene_id=scene_id, status="pending", trigger_type="manual",
config_snapshot=config,
scene_items_snapshot=scene.scene_items,
)
background_tasks.add_task(run_perf_scene, record.id, use_workers)
5.2 run_perf_scene 核心步骤
- 读 快照 ,解析
env_id、Host(可target_host覆盖环境) - Host 连通性探测 (HEAD),失败则
record.status=failed - 预加载
ApiTestCase+ApiDefinition,merge_execution_variables合并项目/环境变量 - 构建
scene_items_for_worker(含 merged headers/body,减少 Worker 侧查库) - 分支:本机 asyncio 或 分布式 Worker
5.3 本机:虚拟用户 + 信号量
python
# _perf_worker_fixed:random.choices(case_pool, weights=...) 按 weight 选接口
async with sem: # sem = concurrent_users
result = await _execute_perf_case_request(case, env, target_host, client, csv_row, ...)
if delay_ms > 0:
await asyncio.sleep(delay_ms / 1000)
_execute_perf_case_request 走 httpx,成功判定:status < 500 且断言通过(与功能回归同源断言逻辑)。
5.4 分布式:并发拆分
python
# routers/perf/workers.py --- distribute_concurrent
# 按各 Worker max_concurrent 比例分配,且不超过单节点上限
assignments = distribute_concurrent(total_concurrent, workers)
Worker 侧引擎与 Backend 同一套 runner/perf_worker.py(BrickCoreRunner 内置或命令行启动),秒级指标 POST /workers/{record_id}/report,结束后 finalize 汇总。
硬约束 :journey_* 模式若含阶段 sync_before 屏障 ,不支持分布式 ------引擎会直接 failed 并提示改单机执行(journey_has_sync_barrier 校验)。
六、复用接口用例:变量与断言
压测不另写 locustfile,请求构建走 build_perf_request:
python
all_variables = ensure_dt_cache(await merge_execution_variables(case.project_id, env.id))
# + csv_row 列 → ${{username}}
# + journey 步骤间 session_variables(extractors)
method, url, headers, params, body, timeout, body_type = ...
| 能力 | 说明 |
|---|---|
${``{env_var}} |
与接口回归同一套替换 |
${``{df:标签}} / ${``{dt:...}} |
数据工厂、插入工具 |
| CSV 列 | ${``{username}} 等 |
| 断言 | 压测仍执行;失败计入 error_rate(可弱化非关键断言) |
实操 :压测前用 接口用例单跑 确认 Host、Token、Body;再挂到场景里加权。
七、CSV 参数化
csv_data 存 JSON 数组,csv_config.strategy:
| strategy | 行为 |
|---|---|
round_robin |
虚拟用户轮询各行 |
unique |
每用户绑定一行(用户数 ≤ 行数) |
random |
随机取行 |
分布式下 get_next_csv_row(..., worker_id, total_workers) 避免多 Worker 抢同一行。
八、Perf Worker 部署(推荐 BrickCoreRunner)
8.1 客户端方式(推荐)
- 安装 BrickCoreRunner v1.3.14+
- 登录 → 执行角色 仅压测 或 UI + 压测
- 选择 压测项目 (与平台顶部项目 ID 一致)→ 上线
- 性能测试 → 执行机 确认 在线
- 场景执行勾选 使用分布式 Worker
客户端会话区 + runner/logs/perf_worker.log 可看秒级 QPS/RT;下线自动 unregister,不必等心跳超时。
8.2 命令行(CI / 高级)
bash
cd runner
python perf_worker.py --master http://你的平台地址 \
--token my-local-token \
--name "压测机-01" \
--max-concurrent 200 \
--project-id 当前项目ID
生产环境浏览器访问一般为 80 端口(Nginx) ,--master 不要写 :8000。
8.3 何时勾选 Worker
| 场景 | 建议 |
|---|---|
| 小并发冒烟、链路调试 | 不勾选,Backend 本机执行 |
| 高并发、多机叠加 QPS | 勾选 Worker |
| 无在线 Worker | 自动 降级本机(cron 同理) |
九、流式 / SSE 与大模型压测
stream_burst 模式 + stream_profile.parser_id(默认 qa_sse_v1):
- 每用户 一次 完整 SSE 会话
- 采集:首字时间、思考耗时、各阶段 RT →
phase_metrics - 报告支持 SSE 阶段汇总 、流式明细懒加载、导出 Excel
request_detail_level=detailed可保留成功 trace,不影响 QPS/RT 聚合
固定/循环/梯度模式也可对指定用例勾选流式执行,适合「混合流量里夹 SSE 接口」。
十、业务链路(Journey)
journey_fixed / journey_loop:多 phase 顺序/并行,步骤间 extractors → session_variables,等价压测版「登录→下单」:
json
{
"mode": "journey_fixed",
"concurrent_users": 20,
"duration_seconds": 300,
"journey": {
"phases": [
{
"name": "登录",
"execution": "serial",
"steps": [{"case_id": 101, "delay_ms": 0}]
},
{
"name": "下单",
"execution": "serial",
"sync_before": false,
"steps": [{"case_id": 102, "delay_ms": 200}]
}
]
}
}
sync_before: true 为阶段屏障(全链路用户齐步走)------仅单机 ;分布式请关闭或改 journey_fixed 单机压测。
十一、报告读什么
| 指标 | 说明 | 发版决策 |
|---|---|---|
| QPS / 成功 QPS | 吞吐 | 是否达 SLA |
| avg / P90 / P95 / P99 | 延迟分布 | 长尾是否恶化 |
| error_rate | 失败占比 | 对照阈值、自动停压 |
| time_series_data | 秒级曲线 | 是否「越压越慢」 |
| case_aggregations | 分接口统计 | 定位慢接口 |
分位采样说明 :接口维度 P90/P95/P99 基于 最多 5000 条 RT 样本(蓄水池采样,MAX_CASE_RT_SAMPLES);总请求数、QPS、错误率、Min/Max 仍统计全量。超大规模压测时分位为近似值,报告页有说明。
支持 多 record 对比 、HTML 导出、完成后 邮件/钉钉/企微 推送(maybe_auto_push_perf_report)。
十二、与 Locust 选型对照
| 维度 | Locust 脚本 | BrickCore 平台压测 |
|---|---|---|
| 接口定义 | locustfile 手写 | 引用 ApiTestCase |
| 环境切换 | 改代码/环境变量 | 换 env_id |
| 分布式 | master/worker | Perf Worker + 比例分配 |
| 权重/间隔 | @task / between |
weight / delay_ms |
| SSE 阶段 | 需自写解析 | stream_burst + qa_sse_v1 |
| 报告 | 本地 HTML | 落库 + 曲线 + 对比 + 通知 |
| 依赖 | Locust | asyncio + httpx(镜像更轻) |
外部仍有 Locust 脚本时,结果需自行对接;平台侧重 与接口资产、定时任务、看板一体。
十三、发版前压测 SOP
- 场景 :核心 3~5 个接口,
weight按生产流量比例 - 环境 :独立压测环境,勿误打生产
- 冒烟:10 并发 × 60s 本机验证用例与 CSV
- 加压 :
stepping或提高concurrent_users,勾选 Worker - 阈值 :设
error_rate_threshold(如 5%) - 对比 :与同场景上一版
record_id比 P95、error_rate - 归档 :评审贴 record 链接;可选 定时压测 (
PerfCronJob+use_workers)
十四、常见排错
| 现象 | 原因 | 处理 |
|---|---|---|
| Host 不可达 | env.host 错 / 网络 | 检查环境;exec 前有 HEAD 探测 |
| Worker 不分配 | 无在线 / project_id 不一致 | 执行机页看 heartbeat;Runner 选对项目 |
| 分布式启动 failed | journey + sync_before | 关屏障或单机 |
| error_rate 100% | Token/Body 错 | 接口用例单跑通过再压 |
| QPS 很低 | delay_ms 大 / 断言过重 / 瓶颈在被测方 | 调参;看 time_series |
| 与 Locust 数值差 | 连接复用、模型不同 | 看 趋势 与相对对比,不必抠绝对值一致 |
| 分位与 Locust 差很多 | 5000 样本蓄水池 | 看全量 QPS/错误率;知悉分位为近似 |
十五、小结
- 平台压测 = ApiTestCase 资产 + asyncio/httpx 自研引擎,不是嵌入 Locust。
- 五种模式 覆盖稳态、梯度、SSE、业务链路;快照保证报告可追溯。
- Perf Worker 与 Backend 同引擎 ,
distribute_concurrent按容量比例拆并发。 - CSV + 变量合并 与接口回归同源;报告支持对比、SSE 阶段、自动停压与通知。
附录 A:源码文件索引
| 顺序 | 文件 | 关注点 |
|---|---|---|
| 1 | models/perf.py |
PerfScene / PerfRecord / PerfWorker / PerfCronJob |
| 2 | routers/perf/exec.py |
start_perf、run_perf_scene、各 mode worker |
| 3 | routers/perf/workers.py |
register、distribute_concurrent、秒级上报 |
| 4 | modules/perf/perf_journey.py |
链路编排、sync 屏障 |
| 5 | modules/stream_phase/ |
SSE 解析、stream_burst |
| 6 | runner/perf_worker.py |
分布式 Worker 执行引擎 |
| 7 | routers/perf/cron.py |
定时压测、use_workers 降级 |
选型背景 (docs/ROADMAP.md):零 Locust 依赖、直接复用接口用例变量/断言,Docker 部署更轻。
支持与交流
- 演示:http://43.142.83.156/showcase/ · 源码:https://gitee.com/BanZhuanKeOrz/BrickCore
- 觉得有用欢迎 Star ⭐,问题评论区留言或 Gitee Issues