FastApi的最佳实践-async def or def

async def or def

async def

async def 为全链路异步路由,Python 的 asyncio 默认是单线程模型,整个服务的所有并发请求都共享同一个事件循环线程,FastAPI 会直接将这个协程放入事件循环中运行。在使用async def时要遵循异步契约:FastAPI 会信任你只执行非阻塞的 I/O 操作,如需执行,则应主动让出控制权,通过 await调用。因为信任这份契约,FastAPI 不会开启额外的线程池来隔离这个函数。

例如:正确操作

1
2
3
4
5
6
7
8
9
10
@router.get("/perfect-ping")
async def perfect_ping():
await asyncio.sleep(10) # 非阻塞的 I/O 操作
return {"pong": True}


async def get_data():
async with httpx.AsyncClient() as client:
resp = await client.get("https://api.example.com/data")
return resp.json()

错误操作:

如果在 async def 路由中调用了同步阻塞函数,例如:

  • time.sleep(5) (而非 await asyncio.sleep(5)
  • requests.get() (而非 httpx.AsyncClient
  • 原生同步数据库驱动(如 pymysql, psycopg2 的同步查询)
  • CPU 密集型计算(大循环、复杂加密、图片处理)
  • 同步文件读写 open().read()

这些操作不包含 await,它们会直接占用当前线程的执行权,且不会主动释放

1
2
3
4
async def get_data():
# requests 是同步阻塞库!这里会卡死整个事件循环
resp = requests.get("https://api.example.com/data")
return resp.json()
1
2
✅ 正常 async:  [请求A] → await IO → [请求B] → await IO → [请求A恢复] ...
❌ 阻塞 async: [请求A] → 🔒阻塞5秒(整个服务器卡死) → [请求B排队等待]...
def

def为同步路由,FastAPI 会将 sync 路由放入线程池中运行,阻塞型 I/O 操作不会阻止 事件循环 执行其他任务,但是在高并发场景下,要考虑占满40线程的情况。

对比
特性 async def 路由 def 同步路由
执行方式 直接在事件循环线程运行 放入外部线程池 (默认40线程) 运行
阻塞操作影响 💥 阻塞整个事件循环 ✅ 仅占用一个线程池槽位,不影响其他请求
适用场景 纯异步I/O (httpx, motor, aiofiles) 同步库、CPU计算、遗留代码
性能上限 极高(无上下文切换开销) 受限于线程池大小和GIL