FastApi的最佳实践
FastApi的最佳实践-async def or def
async def or def
async def
async def 为全链路异步路由,Python 的 asyncio 默认是单线程模型,整个服务的所有并发请求都共享同一个事件循环线程,FastAPI 会直接将这个协程放入事件循环中运行。在使用async def时要遵循异步契约:FastAPI 会信任你只执行非阻塞的 I/O 操作,如需执行,则应主动让出控制权,通过 await调用。因为信任这份契约,FastAPI 不会开启额外的线程池来隔离这个函数。
例如:正确操作
1 | @router.get("/perfect-ping") |
错误操作:
如果在 async def 路由中调用了同步阻塞函数,例如:
time.sleep(5)(而非await asyncio.sleep(5))requests.get()(而非httpx.AsyncClient)- 原生同步数据库驱动(如
pymysql,psycopg2的同步查询) - CPU 密集型计算(大循环、复杂加密、图片处理)
- 同步文件读写
open().read()
这些操作不包含 await,它们会直接占用当前线程的执行权,且不会主动释放。
1 | async def get_data(): |
1 | ✅ 正常 async: [请求A] → await IO → [请求B] → await IO → [请求A恢复] ... |
def
def为同步路由,FastAPI 会将 sync 路由放入线程池中运行,阻塞型 I/O 操作不会阻止 事件循环 执行其他任务,但是在高并发场景下,要考虑占满40线程的情况。
对比
| 特性 | async def 路由 |
def 同步路由 |
|---|---|---|
| 执行方式 | 直接在事件循环线程运行 | 放入外部线程池 (默认40线程) 运行 |
| 阻塞操作影响 | 💥 阻塞整个事件循环 | ✅ 仅占用一个线程池槽位,不影响其他请求 |
| 适用场景 | 纯异步I/O (httpx, motor, aiofiles) |
同步库、CPU计算、遗留代码 |
| 性能上限 | 极高(无上下文切换开销) | 受限于线程池大小和GIL |
All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.
