(二十)同步路由、异步路由与连接复用
1. 根据实际调用选择函数
本项目使用同步 SQLAlchemy Session 和 SQLite 驱动,所以书籍与认证路由用普通 def。FastAPI 将普通路由及普通依赖安排在线程池运行,数据库等待不会直接堵住处理其他异步任务的事件循环。
async def 适合内部会执行异步等待的动作。例如前面已经学过的 HTTPX AsyncClient,请求外部接口时使用 await client.get(...)。仅把函数头改成 async def,里面仍调用同步数据库或 time.sleep,这些调用就会直接占住事件循环。FastAPI 自动安排的是它调用的路由和依赖,普通辅助函数在被你直接调用时仍在当前执行位置运行。
2. 让异步客户端随应用复用
下面的短例转发请求到上一章启动在 8767 的书架。客户端在 lifespan 中创建,应用结束时关闭;每条请求复用同一客户端的连接池。
# 项目文件:demos/async_client.py
from contextlib import asynccontextmanager
import httpx
from fastapi import FastAPI, Request
@asynccontextmanager
async def lifespan(app: FastAPI):
# 复用客户端的连接池;相对地址用于请求前章本机 Web 服务。
async with httpx.AsyncClient(base_url="http://127.0.0.1:8767", timeout=5) as client:
app.state.http = client
yield
app = FastAPI(lifespan=lifespan)
@app.get("/upstream")
async def upstream(request: Request):
# HTTPX 异步请求会在等待网络时交回事件循环。
response = await request.app.state.http.get("/")
response.raise_for_status()
return {"status": response.status_code, "bytes": len(response.content)}# 先按第十二章启动 8767 的 web-books serve,再运行这份独立演示。
uv run python -m uvicorn async_client:app --app-dir ../demos --port 8769
访问 upstream 接口,观察上游状态和响应字节数。这里等待网络的语句会让出事件循环;async with AsyncClient(...) 的退出负责关闭连接池。数据库中的同步会话没有因为这个演示而变成异步对象。