在开发高性能、高可靠性的 Web 应用时,资源管理是一个不容忽视的环节。无论是数据库连接、消息队列客户端,还是缓存服务实例,这些资源在应用启动时需要正确初始化,在关闭时也得妥善清理。FastAPI 提供的lifespan
生命周期机制,正是处理这类问题的有力工具,它能帮助开发者优雅地管理应用从启动到关闭的整个过程
理解 FastAPI 的 lifespan 生命周期
lifespan
本质上是一个异步上下文管理器,通过定义yield
关键字前后的代码块,分别对应应用启动和关闭时执行的逻辑。在 FastAPI 应用启动过程中,所有路由和中间件加载前,yield
之前的代码会率先执行,通常用于初始化资源;当应用接收到关闭信号,在服务完全停止前,yield
之后的代码将被运行,负责释放或清理之前初始化的资源。这种设计确保了资源在应用生命周期内合理使用,避免资源泄漏等问题
lifespan 的基本使用方法
使用lifespan
,首先要创建一个异步上下文管理器函数,然后将其传递给 FastAPI
应用实例。下面以数据库连接管理为例,展示lifespan
的基础用法:
from fastapi import FastAPI
from contextlib import asynccontextmanager
import databases
# 数据库连接配置
DATABASE_URL = "sqlite:///./test.db"
database = databases.Database(DATABASE_URL)
@asynccontextmanager
async def lifespan(app: FastAPI):
# 应用启动时执行
await database.connect()
app.state.database = database # 将数据库连接存储在应用状态中
yield # 应用在此期间运行
# 应用关闭时执行
await database.disconnect()
app = FastAPI(lifespan=lifespan)
@app.get("/items/")
async def read_items():
query = "SELECT * FROM items"
return await app.state.database.fetch_all(query)
上述代码中,lifespan
函数在应用启动时建立数据库连接,并将连接对象存储在app.state中,方便后续路由函数使用;应用关闭时,lifespan函数负责断开数据库连接,保障资源合理释放
lifespan 在实际场景中的应用
1. 数据库连接管理
除了前面的 SQLite
示例,在实际项目中,lifespan
常被用于管理 MySQL
、PostgreSQL
等数据库连接池。通过在yield
前初始化连接池,yield
后释放连接池资源,确保应用在整个生命周期内高效、稳定地访问数据库
from fastapi import FastAPI
from contextlib import asynccontextmanager
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
# 数据库连接配置
SQLALCHEMY_DATABASE_URL = "postgresql://user:password@localhost/dbname"
engine = create_engine(SQLALCHEMY_DATABASE_URL)
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
@asynccontextmanager
async def lifespan(app: FastAPI):
# 应用启动时执行
app.state.session = SessionLocal()
yield
# 应用关闭时执行
app.state.session.close()
app = FastAPI(lifespan=lifespan)
@app.get("/users/")
async def get_users():
session = app.state.session
# 执行数据库查询操作
users = session.query(User).all()
return users
2. 消息队列客户端初始化与清理
当应用需要与消息队列(如 RabbitMQ
、Kafka
)交互时,lifespan
可以在应用启动时建立客户端连接,关闭时断开连接。例如,使用aiormq
库连接 RabbitMQ
from fastapi import FastAPI
from contextlib import asynccontextmanager
import aiormq
@asynccontextmanager
async def lifespan(app: FastAPI):
connection = await aiormq.connect("amqp://guest:guest@localhost/")
app.state.channel = await connection.channel()
yield
await connection.close()
app = FastAPI(lifespan=lifespan)
3. 缓存服务初始化
对于使用 Redis
等缓存服务的应用,lifespan
能确保在应用启动时连接到缓存服务器,关闭时释放连接。以下是使用aioredis
库连接 Redis
的示例:
from fastapi import FastAPI
from contextlib import asynccontextmanager
import aioredis
@asynccontextmanager
async def lifespan(app: FastAPI):
redis = await aioredis.from_url("redis://localhost")
app.state.redis = redis
yield
await redis.close()
app = FastAPI(lifespan=lifespan)
与旧版事件机制的对比
在 FastAPI 0.70.0 版本之前,开发者通过@app.on_event("startup")
和@app.on_event("shutdown")
装饰器来处理应用启动和关闭事件。虽然这两种方式都能实现资源管理,但lifespan
在代码组织和资源管理上更具优势。lifespan
通过异步上下文管理器,将启动和关闭逻辑集中在一个函数内,代码结构更清晰,也更符合 Python 上下文管理的规范
# 旧版事件机制示例
from fastapi import FastAPI
app = FastAPI()
database = None
@app.on_event("startup")
async def startup_event():
global database
# 数据库连接初始化
database = connect_to_database()
@app.on_event("shutdown")
async def shutdown_event():
global database
# 数据库连接关闭
close_database_connection(database)
注意事项
异常处理:在lifespan
函数中,yield
前的代码若发生异常,应用将无法正常启动;yield
后的代码出现异常,可能导致资源无法完全清理。因此,务必做好异常处理,避免因错误导致资源泄漏或应用启动失败
版本兼容性:lifespan
是 FastAPI 0.70.0 及以上版本才有的特性,若使用旧版本,需采用@app.on_event("startup")
和@app.on_event("shutdown")
方式
资源共享:通过app.state
在lifespan
函数和路由函数间共享资源时,要注意资源的线程安全性和并发访问问题,避免出现数据不一致或资源冲突。