← 返回知识库
后端工程FastAPI异步高并发连接池限流降级

FastAPI 高并发与后端工程

Python 异步后端的关键坑:async def 里不能有阻塞调用、worker 模型、连接池、HTTP 客户端复用、限流熔断降级、幂等,以及 MySQL/Redis/JWT/SSE 速记。

FastAPI 高并发与后端工程

FastAPI 是 Python 高性能异步框架,但「会写」和「能在高并发下正确运行」是两回事。

最高频事故:async def 里的阻塞调用

FastAPI 的 async def 端点跑在事件循环里。如果里面出现阻塞调用(同步的数据库查询、requests 同步请求、CPU 密集计算、time.sleep),会卡死整个事件循环,所有请求都被拖慢——这是「看起来 LLM 慢、其实是自己堵住」的元凶。

# ❌ 错误:async def 里用同步阻塞调用
@app.get("/ask")
async def ask(q: str):
    result = requests.post(llm_url, json={"q": q})  # 阻塞!卡死事件循环
    return {"answer": result.json()}

# ✅ 正确:同步调用丢给线程池,或用异步客户端
@app.get("/ask")
async def ask(q: str):
    result = await asyncio.to_thread(requests.post, llm_url, json={"q": q})
    return {"answer": result.json()}

该怎么选

场景写法
纯 IO、下游有异步客户端async def + await
只能同步(同步 DB 驱动)def,FastAPI 会自动丢到线程池执行,不阻塞事件循环
CPU 密集(embedding、解析、图像处理)进程池 / 独立推理服务 / 消息队列,绝不放事件循环

Worker 模型与连接池

  • worker 模型uvicorn 单进程单事件循环。生产两种:gunicorn + uvicorn worker(按 CPU 核数起,通常 2×核数+1 起手再压测校准),或 K8s 每容器 1 worker + 多副本(对 HPA 更友好)
  • 连接池:池大小要与 worker 数、每 worker 并发匹配——池不是越大越好,数据库有最大连接数上限,池过大反而把 DB 打挂。获取连接的超时必须设,否则请求会无限等待
  • HTTP 客户端复用httpx.AsyncClient 全局复用一个实例(连接池 + keep-alive),别每次请求新建——这也是 TTFT 优化的一部分

真实踩坑

AI 小灵踩过「单连接被多线程并发复用导致 InterfaceError」的坑,修法是连接池 + 每请求借一条连接(保证单连接同时只有一个使用者)。

超时三件套(必须设)

  1. 上游 LLM 调用超时
  2. 连接池获取超时
  3. 整体请求超时

没有超时的系统在故障时会无限堆积。

高并发七层手段

graph LR
  A[缓存] --> B[信号量]
  B --> C[队列]
  C --> D[优先级]
  D --> E[多副本/凭证路由]
  E --> F[降级]
  F --> G[背压]
  1. 缓存:精确缓存(相同 query)+ 语义缓存(相似 query),命中即 0 成本 0 延迟
  2. 并发信号量asyncio.Semaphore 限住对上游的实际并发数,保护自己和上游的第一道闸
  3. 队列削峰:入队即返回,worker 匀速消费——长任务必须异步化
  4. 优先级排队:交互式请求优先,离线批处理让路
  5. 多副本 + 多凭证路由:多副本水平扩展;多个 API Key/供应商轮询分摊配额
  6. 降级:换小模型、返回缓存结果、模板兜底——有损但可用,不能直接 500
  7. 背压与快速失败:队列满就明确拒绝(含 Retry-After),队列无限堆积比直接拒绝更糟

限流 / 熔断 / 降级三件套

手段目的关键点
限流挡掉过量请求令牌桶/漏桶/滑动窗口;按用户/租户/接口/全局分层;网关粗 + 应用细
熔断防止故障扩散三态:关闭 → 打开 → 半开;打开期间快速失败
降级牺牲非核心保核心模型降级(strong→fast→缓存→模板兜底)、功能降级、结果降级(部分结果 + 风险提示)

Agent 特有:任务级预算——一次 Agent 任务可能调几十次 LLM,必须把 token 预算和时间预算作为整个任务的硬约束,超预算就中止并返回当前最优结果 + 风险提示。

幂等与并发写

  • 幂等:唯一约束 / 幂等键 + 状态机,同一幂等键重复提交返回首次结果
  • append-only 表的并发安全不靠「单连接串行化」,而靠:① 每条是独立原子 INSERT(无读改写、无长事务,天然免锁)② 唯一约束防重 ③ 连接池保证单连接同一时刻只有一个使用者

后端速记(MySQL / Redis / JWT / SSE)

MySQL

  • B+ 树索引:叶子存数据且有序链表,范围查询高效
  • 最左前缀:联合索引 (a,b,c),条件必须从最左列开始
  • 覆盖索引:查询列都在索引里,免回表
  • 隔离级别:读未提交 → 读已提交 → 可重复读(MySQL 默认) → 串行化

Redis(面试必问三连)

  • 穿透:查不存在的数据 → 布隆过滤器 / 缓存空值
  • 击穿:热点 key 过期瞬间 → 互斥锁 / 逻辑过期
  • 雪崩:大量 key 同时过期 → 过期时间加随机值 / 集群

JWT

无状态、验签不查库;缺点无法主动失效 → 短过期 + refresh token;必须 HTTPS。

SSE vs WebSocket

  • SSE:单向(服务端→客户端)、HTTP 复用、自动重连;够用且简单
  • WebSocket:双向全双工;需要双向/实时交互时用
  • 项目:服务端推任务事件 → SSE 足够