Python 现代开发学习笔记

(二十)同步路由、异步路由与连接复用

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(...) 的退出负责关闭连接池。数据库中的同步会话没有因为这个演示而变成异步对象。

比较同步数据库在工作线程等待与异步 HTTP 在 await 处交回事件循环,展示错误地把阻塞调用放进 async def 时其他任务无法前进

笔记正文与全部题解 · Ctrl / ⌘ K