From d9aeff2114a2756c750b2a717a628dd8a9d8d163 Mon Sep 17 00:00:00 2001 From: WorkBuddy Date: Mon, 21 Sep 2026 19:51:41 +0800 Subject: [PATCH] =?UTF-8?q?debt(D7):=20=E9=99=90=E6=B5=81/KeyRing=20?= =?UTF-8?q?=E5=8D=95=E8=BF=9B=E7=A8=8B=E9=99=90=E5=88=B6=E8=AF=B4=E6=98=8E?= =?UTF-8?q?=20+=20=E7=94=9F=E4=BA=A7=E5=90=AF=E5=8A=A8=E4=B8=80=E6=AC=A1?= =?UTF-8?q?=E6=80=A7=E5=91=8A=E8=AD=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - app.py lifespan:APP_ENV=production 时 logger.warning 提醒 进程内 rate-limit/KeyRing 仅单进程有效(不 sys.exit、不引入 Redis) - key_ring.py docstring 补充多 worker 不共享的后果说明 - deps.py 注释已具备(单进程有效,生产前置 Nginx),无需改动 --- src/api/app.py | 10 ++++++++++ src/data/key_ring.py | 3 ++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/api/app.py b/src/api/app.py index 102d74f..8630d51 100644 --- a/src/api/app.py +++ b/src/api/app.py @@ -30,6 +30,16 @@ async def lifespan(app: FastAPI) -> AsyncIterator[None]: await ensure_admin_password_hashed() # .env 明文密码 → scrypt 哈希(幂等) await assert_security_on_startup() # 启动安全校验(生产拒绝/开发警告) + # D7(工程债): 进程内限流(_RateLimiter)与 KeyRing 均为单进程状态; + # 多 worker 部署时各进程独立计数,限流阈值会按 worker 数放大、KeyRing 不共享。 + # 生产环境应将限流前置到 Nginx/网关,或以单 worker 运行(见 src/api/deps.py 注释)。 + # 此处仅提醒一次,不阻断启动,也不引入 Redis 等外部依赖。 + if settings.APP_ENV == "production": + logger.warning( + "APP_ENV=production: 进程内 rate-limit 与 KeyRing 仅单进程有效;" + "多 worker 部署请将限流前置到 Nginx/网关,或以单 worker 运行" + ) + # 注册默认定时任务(如果数据库中没有) from src.db.base import AsyncSessionLocal from sqlalchemy import select diff --git a/src/data/key_ring.py b/src/data/key_ring.py index 1792e9c..a8d8ff8 100644 --- a/src/data/key_ring.py +++ b/src/data/key_ring.py @@ -1,7 +1,8 @@ """API Key 轮换环:多 key 自动切换,遇到限流(429)自动跳过已冷却 key。 设计: - - 进程内纯内存状态(限速是短时状态,无需持久化) + - 进程内纯内存状态(限速是短时状态,无需持久化;D7: 多 worker 部署时各进程 + 独立计数、不共享,上游限速额度应按 worker 数分摊,或前置网关统一管理) - 单 key 场景零开销:直接透传 - 多 key 场景:429 时把当前 key 标记冷却(默认 60s),轮转到下一个可用 key - 全部 key 都在冷却时:使用最早冷却的那个 key 并等待(退化到单 key 重试)