FastAPI Lifespan 事件完全指南:用 lifespan 参数管理应用启动与关闭逻辑
在 FastAPI 中,很多资源(数据库连接池、机器学习模型、缓存实例等)需要在应用开始接收请求之前一次性初始化,并在应用停止处理请求之后统一清理。FastAPI 通过 lifespan 参数和异步上下文管理器(async context manager)机制,让你把这类"应用生命周期(lifespan)"逻辑集中到一处管理。读完本文,你将掌握:如何编写 startup/shutdown 共享逻辑、@asynccontextmanager 的工作原理、已废弃的 @app.on_event 替代方案,以及子应用(Mount)下的注意事项。
典型使用场景:加载共享的机器学习模型
先看一个官方文档给出的经典用例。假设你有一个机器学习模型,需要用它来处理请求,且这个模型被所有请求共享——不是每个请求、每个用户各加载一份。加载模型可能非常耗时,因为它需要从磁盘读取大量数据,因此绝不能为每个请求都加载一次。
你可能会把模型加载代码写在模块的顶层,但这会导致一个问题:即使你只是运行一个简单的自动化测试,也会先花大量时间加载模型,拖慢本不需要它的测试代码。
我们要解决的就是这个矛盾:在应用开始接收请求之前、但在代码被加载(import)之后加载模型。这正是 startup 逻辑的职责。
推荐方案:lifespan 参数 + 异步上下文管理器
启动(startup)与关闭(shutdown)逻辑通过 FastAPI 应用的 lifespan 参数定义。完整示例可参考 官方示例源码,核心代码如下:
from contextlib import asynccontextmanager
from fastapi import FastAPI
def fake_answer_to_everything_ml_model(x: float):
return x * 42
ml_models = {}
@asynccontextmanager
async def lifespan(app: FastAPI):
# Load the ML model
ml_models["answer_to_everything"] = fake_answer_to_everything_ml_model
yield
# Clean up the ML models and release the resources
ml_models.clear()
app = FastAPI(lifespan=lifespan)
@app.get("/predict")
async def predict(x: float):
result = ml_models"answer_to_everything"
return {"result": result}
这段代码用字典 ml_models 模拟"昂贵的启动操作"(加载模型):
yield之前的代码在应用开始接收请求之前执行(即 startup 阶段)。示例中,把(假的)模型函数放进ml_models字典,模拟加载完成。yield之后的代码在应用结束处理所有请求之后、shutdown 前一刻执行。示例中调用ml_models.clear()释放资源,例如释放内存或 GPU。
::: tip
shutdown 会在你停止应用时发生——比如你要部署新版本,或者单纯不想再让它运行了。
:::
lifespan 函数:yield 划分的两段逻辑
首先注意,lifespan 是一个用 yield 定义的 async 函数,这与 FastAPI 中"带 yield 的依赖项(Dependencies)"的写法非常相似:
@asynccontextmanager
async def lifespan(app: FastAPI):
# 应用启动前执行
...
yield
# 应用结束后执行
...
函数体中 yield 之前的部分在应用启动前执行;yield 之后的部分在应用收尾后执行。这种"以 yield 为界"的结构,天然适合表达"先获取资源、后释放资源"的成对逻辑。
异步上下文管理器(async context manager)
上例中函数还带有一个 @asynccontextmanager 装饰器:
from contextlib import asynccontextmanager
@asynccontextmanager
async def lifespan(app: FastAPI):
...
它把这个函数转换成一个异步上下文管理器(async context manager)。
Python 中的上下文管理器是可以用于 with 语句的对象,例如 open():
with open("file.txt") as file:
file.read()
较新版本的 Python 还支持异步上下文管理器,配合 async with 使用:
async with lifespan(app):
await do_stuff()
当我们像上面这样创建一个(异步)上下文管理器时,它的行为是:进入 with 块之前,执行 yield 之前的代码;退出 with 块之后,执行 yield 之后的代码。
在示例代码中我们并没有直接 async with 它,而是把它交给 FastAPI 来使用。FastAPI 应用的 lifespan 参数接受的正是这样一个异步上下文管理器:
app = FastAPI(lifespan=lifespan)
从源码可以看到,FastAPI 应用构造函数 中 lifespan 的类型标注为 Lifespan[AppType] | None,文档注释明确写着它"用单个上下文管理器取代 startup 和 shutdown 函数"(A Lifespan context manager handler. This replaces startup and shutdown functions with a single context manager),并在 应用初始化 时原样透传给底层的 APIRouter。
在应用状态中共享 lifespan 数据(补充)
如果需要在请求处理逻辑中访问 lifespan 里加载的资源,除了示例中使用的模块级字典 ml_models 之外,从源码结构看,FastAPI 还继承了 Starlette 的 app.state 对象(见 applications.py 中的 state 属性定义):它是整个应用生命周期内同一个对象,不随请求变化,可以把它当作跨请求共享的"全局状态"来存放 lifespan 阶段加载的数据。
替代方案:on_event 事件(已废弃)
⚠️ 重要提示:处理 startup 与 shutdown 的推荐方式是上文描述的
lifespan参数。如果你提供了lifespan参数,startup和shutdown事件处理函数将不再被调用——要么全用lifespan,要么全用事件,二者不能混用。如果你的项目是新项目,这部分可以直接跳过。
替代方式是注册独立的事件处理函数。这些函数既可以用 async def 声明,也可以用普通 def 声明。
startup 事件
用事件 "startup" 声明一个在应用启动前运行的函数(示例见 startup 事件源码):
from fastapi import FastAPI
app = FastAPI()
items = {}
@app.on_event("startup")
async def startup_event():
items["foo"] = {"name": "Fighters"}
items["bar"] = {"name": "Tenders"}
@app.get("/items/{item_id}")
async def read_items(item_id: str):
return items[item_id]
这里 startup 事件处理函数把若干初始数据写入 items 这个 dict(充当"数据库")。你可以注册多个 startup 处理函数;在所有 startup 事件处理函数执行完毕之前,应用不会开始接收请求。
shutdown 事件
用事件 "shutdown" 声明一个在应用关闭时运行的函数(示例见 shutdown 事件源码):
from fastapi import FastAPI
app = FastAPI()
@app.on_event("shutdown")
def shutdown_event():
with open("log.txt", mode="a") as log:
log.write("Application shutdown")
这里 shutdown 事件处理函数会向文件 log.txt 追加写入一行文本 "Application shutdown"。
open()中的mode="a"表示追加(append),新内容会写到文件末尾,不会覆盖原有内容。- 注意这里用的是标准 Python
open(),涉及需要"等待"磁盘写入的 I/O 操作,但它本身不是async的。因此该事件处理函数声明为普通def而非async def——这样 FastAPI 会在专门的线程中运行它,避免阻塞事件循环。
startup 与 shutdown 为何难以配合
startup 和 shutdown 的逻辑通常是有联系的:启动某样东西再关闭它、获取资源再释放它等。把它们拆在两个互相不共享变量、不共享逻辑的函数里会更困难——你往往需要借助全局变量或类似技巧来传递状态。这正是官方现在推荐改用 lifespan 的原因:yield 前后的代码处在同一个函数作用域内,共享同一个 app 引用,天然适合成对管理资源。
从源码可以确认这套旧机制确实已被标记废弃:FastAPI.on_event 和 APIRouter.on_event 均带有 @deprecated 装饰器,提示"on_event is deprecated, use lifespan event handlers instead";同时 FastAPI 构造函数的 on_startup / on_shutdown 列表参数的文档注释也建议改用 lifespan 处理函数。对应的测试覆盖位于 tests/test_tutorial/test_events/(包含 test_tutorial001.py、test_tutorial002.py、test_tutorial003.py),验证了上述三种事件写法的端到端行为。
技术细节:ASGI Lifespan 协议
给好奇的技术细节:在 ASGI 技术规范层面,FastAPI 的这套机制对应其中的 Lifespan Protocol,该协议定义了 startup 与 shutdown 两类事件。FastAPI 的 lifespan 参数最终由底层 Starlette 应用驱动执行;如果你想了解 Starlette 的 lifespan 处理器细节,以及如何在代码其他位置使用 lifespan 期间保存的状态,可进一步阅读 Starlette 官方文档中关于 Lifespan 的章节。
注意事项:子应用(Mounts)不会执行这些事件
务必记住:lifespan 事件(startup 和 shutdown)只会在主应用上执行一次,而不会为通过 mount() 挂载的**子应用(Sub Applications - Mounts)**执行。如果你把带有 lifespan 逻辑的应用挂载到另一个应用下,子应用的启动/关闭逻辑将不会触发——这类子应用的机制见 Sub Applications - Mounts。此外,测试场景下如何验证 startup/shutdown 逻辑,可参考 Testing Events 文档。
小结
| 方案 | 写法 | 状态 | 适用场景 |
|---|---|---|---|
lifespan 参数 |
@asynccontextmanager + yield,FastAPI(lifespan=...) |
✅ 推荐 | startup/shutdown 逻辑需要共享变量(如获取并释放资源) |
@app.on_event("startup" / "shutdown") |
独立事件处理函数 | ⚠️ 已废弃 | 仅需简单、彼此独立的启动或关闭逻辑;提供了 lifespan 后它们不会再被调用 |
FastAPI(on_startup=[...], on_shutdown=[...]) |
构造函数传函数列表 | ⚠️ 源码注释中建议改用 lifespan |
遗留代码兼容 |
核心要点回顾:
- 用
@asynccontextmanager装饰的 async 函数中,yield之前是 startup 逻辑,之后是 shutdown 逻辑; - 把它通过
FastAPI(lifespan=lifespan)传入即可,应用只会在开始接收请求前执行一次 startup、在结束时执行一次 shutdown; - 避免在模块顶层加载昂贵资源,改用 lifespan 延迟到应用真正启动时加载;
- 挂载的子应用不触发 lifespan 事件,需特别注意资源管理的归属。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0627
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00