在开发高性能、高可靠性的 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常被用于管理 MySQLPostgreSQL 等数据库连接池。通过在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. 消息队列客户端初始化与清理

当应用需要与消息队列(如 RabbitMQKafka)交互时,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.statelifespan函数和路由函数间共享资源时,要注意资源的线程安全性和并发访问问题,避免出现数据不一致或资源冲突。

最后修改:2025 年 06 月 08 日
如果觉得我的文章对你有用,请随意赞赏