FastAPI 的异步能力依赖 ASGI 协议,其核心是 async def app(scope, receive, send) 三元接口;FastAPI 应用本身即 ASGI callable,路由协程由 Uvicorn 事件循环统一调度;禁用 asyncio.run(),同步函数则交由线程池执行。

FastAPI 的异步能力并非来自框架自身“魔法”,而是深度依赖 ASGI(Asynchronous Server Gateway Interface)这一标准协议。它让 Web 框架能真正以协程方式处理请求,而非靠多线程或进程模拟并发。
ASGI 是什么:Web 服务器与异步框架之间的契约
ASGI 是 WSGI 的异步演进版,定义了服务器(如 Uvicorn、Hypercorn)如何调用 Python 异步应用。它的核心是一个可等待的 callable,签名通常是:
async def app(scope, receive, send): ...其中:
- scope:包含请求元数据(类型、路径、HTTP 版本、headers 等),是字典结构,一次请求只读
- receive:异步函数,用于接收客户端发来的事件(如 HTTP 请求体分块、WebSocket 消息)
- send:异步函数,用于向客户端发送响应事件(状态码、headers、响应体、关闭信号等)
这个三元接口让服务器可以按需拉取数据、非阻塞地发送响应,天然支持长连接、流式响应和 WebSocket。
FastAPI 如何接入 ASGI:从路由到协程执行
FastAPI 应用本身就是一个符合 ASGI 协议的 async callable。当你写:
@app.get("/items")async def read_items():
return {"items": [...]}
FastAPI 会自动将该函数包装为一个 awaitable 路由处理器。请求到达时:
- Uvicorn 解析 HTTP 请求,构造 scope,并准备 receive/send 实例
- FastAPI 根据路径匹配到
read_items,启动其协程 - 若函数内有
await(如数据库查询、HTTPX 调用、文件读写),事件循环挂起当前任务,调度其他请求协程运行 - 协程返回后,FastAPI 将结果序列化,通过 send 异步写出响应
为什么不能只靠 asyncio.run()?—— 事件循环必须由服务器托管
你不能在 FastAPI 路由里手动调用 asyncio.run() 启动新事件循环,原因很直接:
- Uvicorn 已在主线程托管一个运行中的
asyncio.EventLoop - 所有协程必须在这个统一循环中调度,才能实现真正的高并发和资源共享(如连接池)
- 手动新建 loop 不仅低效,还会导致上下文丢失、资源竞争甚至死锁
所以 FastAPI 要求所有 async def 路由函数都运行在服务器提供的同一个事件循环中 —— 这正是 ASGI 协议所保障的执行环境一致性。
同步代码也能跑,但会阻塞事件循环
FastAPI 允许写 def(同步)路由,此时 Uvicorn 会自动将其提交到线程池(concurrent.futures.ThreadPoolExecutor)执行,避免阻塞主循环。
但要注意:
- 频繁调用同步函数会快速耗尽线程池,默认只有 40 个线程
- I/O 密集型同步操作(如 requests.get)仍会阻塞线程,无法发挥异步优势
- 真正想释放并发能力,应优先使用
async/await+ 异步库(httpx、databases、aiomysql 等)

















