feat(gateway): 全局串行化(并发=1) + 记录入参/出参/耗时日志

并发模型从「每worker并发1 + 多worker并行」改为全局串行:同一时间
只处理1个请求,其余排队;多 worker 仅作热备(主 worker 坏了才用备机)。
接口4(face/features) 走豆包、不占 GPU,不纳入串行。

- pool: asyncio.Semaphore(max_global_concurrency=1) + acquire/release_global_slot
  (依赖单进程 uvicorn 部署,已在注释中标注)
- forward: proxy_request 最外层 acquire 全局槽、try/finally 全路径释放;
  入参 multipart 解析挂 request.state;原重试/故障转移逻辑抽到 _dispatch_with_retries
- reqlog(新): 标量入参保留;图片(file/base64)存盘转URL,绝不内嵌base64;
  出参递归摘要截断(landmarks/长串/大数组)
- logging_middleware: RequestLogEntry 加 request_params/response_data,
  jsonl 全量记录;_load_from_logfile 同步映射防重启丢字段;get_stats recent 暴露
- /gateway-health 暴露 global_max/global_busy/global_waiting
- config: dispatch 新增 max_global_concurrency / max_queue_wait_seconds
- tests: test_reqlog + test_gateway_serialization(9 用例)

顺带提交此前未提交的网关统计(daily stats 接口)与耗时看板(api_timing_dashboard.html)。

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Ubuntu
2026-07-23 22:56:48 +08:00
co-authored by Claude
parent 98b9108837
commit bb9f55e93c
10 changed files with 1037 additions and 7 deletions
+6 -1
View File
@@ -24,6 +24,8 @@ DEFAULTS = {
},
"dispatch": {
"per_worker_concurrency": 1,
"max_global_concurrency": 1,
"max_queue_wait_seconds": 590,
"queue_wait_seconds": 30,
"request_timeout_seconds": 600,
"retry_on_failure": True,
@@ -105,12 +107,15 @@ def load_config() -> dict:
logger.info(
"配置加载完成 | workers=%s | public_base_url=%s | "
"hc_interval=%ds | dispatch_timeout=%ds | queue_wait=%ds",
"hc_interval=%ds | dispatch_timeout=%ds | queue_wait=%ds | "
"global_concurrency=%d | max_queue_wait=%ds",
cfg["workers"],
cfg["public_base_url"],
cfg["health_check"]["interval_seconds"],
cfg["dispatch"]["request_timeout_seconds"],
cfg["dispatch"]["queue_wait_seconds"],
cfg["dispatch"]["max_global_concurrency"],
cfg["dispatch"]["max_queue_wait_seconds"],
)
_config_cache = cfg