|
|
@ -1,16 +1,114 @@ |
|
|
|
# 事件:启动 - 关闭 |
|
|
|
# 生命周期事件 |
|
|
|
|
|
|
|
**FastAPI** 支持定义在应用启动前,或应用关闭后执行的事件处理器(函数)。 |
|
|
|
你可以定义在应用**启动**前执行的逻辑(代码)。这意味着在应用**开始接收请求**之前,这些代码只会被执行**一次**。 |
|
|
|
|
|
|
|
事件函数既可以声明为异步函数(`async def`),也可以声明为普通函数(`def`)。 |
|
|
|
同样地,你可以定义在应用**关闭**时应执行的逻辑。在这种情况下,这段代码将在**处理可能的多次请求后**执行**一次**。 |
|
|
|
|
|
|
|
因为这段代码在应用开始接收请求**之前**执行,也会在处理可能的若干请求**之后**执行,它覆盖了整个应用程序的**生命周期**("生命周期"这个词很重要😉)。 |
|
|
|
|
|
|
|
这对于设置你需要在整个应用中使用的**资源**非常有用,这些资源在请求之间**共享**,你可能需要在之后进行**释放**。例如,数据库连接池,或加载一个共享的机器学习模型。 |
|
|
|
|
|
|
|
## 用例 |
|
|
|
|
|
|
|
让我们从一个示例用例开始,看看如何解决它。 |
|
|
|
|
|
|
|
假设你有几个**机器学习的模型**,你想要用它们来处理请求。 |
|
|
|
|
|
|
|
相同的模型在请求之间是共享的,因此并非每个请求或每个用户各自拥有一个模型。 |
|
|
|
|
|
|
|
假设加载模型可能**需要相当长的时间**,因为它必须从**磁盘**读取大量数据。因此你不希望每个请求都加载它。 |
|
|
|
|
|
|
|
你可以在模块/文件的顶部加载它,但这也意味着即使你只是在运行一个简单的自动化测试,它也会**加载模型**,这样测试将**变慢**,因为它必须在能够独立运行代码的其他部分之前等待模型加载完成。 |
|
|
|
|
|
|
|
这就是我们要解决的问题——在处理请求前加载模型,但只是在应用开始接收请求前,而不是代码执行时。 |
|
|
|
|
|
|
|
## 生命周期 lifespan |
|
|
|
|
|
|
|
你可以使用`FastAPI()`应用的`lifespan`参数和一个上下文管理器(稍后我将为你展示)来定义**启动**和**关闭**的逻辑。 |
|
|
|
|
|
|
|
让我们从一个例子开始,然后详细介绍。 |
|
|
|
|
|
|
|
我们使用`yield`创建了一个异步函数`lifespan()`像这样: |
|
|
|
|
|
|
|
```Python hl_lines="16 19" |
|
|
|
{!../../docs_src/events/tutorial003.py!} |
|
|
|
``` |
|
|
|
|
|
|
|
在这里,我们在 `yield` 之前将(虚拟的)模型函数放入机器学习模型的字典中,以此模拟加载模型的耗时**启动**操作。这段代码将在应用程序**开始处理请求之前**执行,即**启动**期间。 |
|
|
|
|
|
|
|
然后,在 `yield` 之后,我们卸载模型。这段代码将会在应用程序**完成处理请求后**执行,即在**关闭**之前。这可以释放诸如内存或 GPU 之类的资源。 |
|
|
|
|
|
|
|
/// tip | 提示 |
|
|
|
|
|
|
|
**关闭**事件只会在你停止应用时触发。 |
|
|
|
|
|
|
|
可能你需要启动一个新版本,或者你只是你厌倦了运行它。 🤷 |
|
|
|
|
|
|
|
/// |
|
|
|
|
|
|
|
## 生命周期函数 |
|
|
|
|
|
|
|
首先要注意的是,我们定义了一个带有 `yield` 的异步函数。这与带有 `yield` 的依赖项非常相似。 |
|
|
|
|
|
|
|
```Python hl_lines="14-19" |
|
|
|
{!../../docs_src/events/tutorial003.py!} |
|
|
|
``` |
|
|
|
|
|
|
|
这个函数在 `yield`之前的部分,会在应用启动前执行。 |
|
|
|
|
|
|
|
剩下的部分在 `yield` 之后,会在应用完成后执行。 |
|
|
|
|
|
|
|
## 异步上下文管理器 |
|
|
|
|
|
|
|
如你所见,这个函数有一个装饰器 `@asynccontextmanager` 。 |
|
|
|
|
|
|
|
它将函数转化为所谓的“**异步上下文管理器**”。 |
|
|
|
|
|
|
|
```Python hl_lines="1 13" |
|
|
|
{!../../docs_src/events/tutorial003.py!} |
|
|
|
``` |
|
|
|
|
|
|
|
在 Python 中, **上下文管理器**是一个你可以在 `with` 语句中使用的东西,例如,`open()` 可以作为上下文管理器使用。 |
|
|
|
|
|
|
|
```Python |
|
|
|
with open("file.txt") as file: |
|
|
|
file.read() |
|
|
|
``` |
|
|
|
|
|
|
|
Python 的最近几个版本也有了一个**异步上下文管理器**,你可以通过 `async with` 来使用: |
|
|
|
|
|
|
|
```Python |
|
|
|
async with lifespan(app): |
|
|
|
await do_stuff() |
|
|
|
``` |
|
|
|
|
|
|
|
你可以像上面一样创建了一个上下文管理器或者异步上下文管理器,它的作用是在进入 `with` 块时,执行 `yield` 之前的代码,并且在离开 `with` 块时,执行 `yield` 后面的代码。 |
|
|
|
|
|
|
|
但在我们上面的例子里,我们并不是直接使用,而是传递给 FastAPI 来供其使用。 |
|
|
|
|
|
|
|
`FastAPI()` 的 `lifespan` 参数接受一个**异步上下文管理器**,所以我们可以把我们新定义的上下文管理器 `lifespan` 传给它。 |
|
|
|
|
|
|
|
```Python hl_lines="22" |
|
|
|
{!../../docs_src/events/tutorial003.py!} |
|
|
|
``` |
|
|
|
|
|
|
|
## 替代事件(弃用) |
|
|
|
|
|
|
|
/// warning | 警告 |
|
|
|
|
|
|
|
**FastAPI** 只执行主应用中的事件处理器,不执行[子应用 - 挂载](sub-applications.md){.internal-link target=_blank}中的事件处理器。 |
|
|
|
配置**启动**和**关闭**事件的推荐方法是使用 `FastAPI()` 应用的 `lifespan` 参数,如前所示。如果你提供了一个 `lifespan` 参数,启动(`startup`)和关闭(`shutdown`)事件处理器将不再生效。要么使用 `lifespan`,要么配置所有事件,两者不能共用。 |
|
|
|
|
|
|
|
你可以跳过这一部分。 |
|
|
|
|
|
|
|
/// |
|
|
|
|
|
|
|
## `startup` 事件 |
|
|
|
有一种替代方法可以定义在**启动**和**关闭**期间执行的逻辑。 |
|
|
|
|
|
|
|
**FastAPI** 支持定义在应用启动前,或应用关闭时执行的事件处理器(函数)。 |
|
|
|
|
|
|
|
事件函数既可以声明为异步函数(`async def`),也可以声明为普通函数(`def`)。 |
|
|
|
|
|
|
|
### `startup` 事件 |
|
|
|
|
|
|
|
使用 `startup` 事件声明 `app` 启动前运行的函数: |
|
|
|
|
|
|
@ -22,7 +120,7 @@ |
|
|
|
|
|
|
|
只有所有 `startup` 事件处理器运行完毕,**FastAPI** 应用才开始接收请求。 |
|
|
|
|
|
|
|
## `shutdown` 事件 |
|
|
|
### `shutdown` 事件 |
|
|
|
|
|
|
|
使用 `shutdown` 事件声明 `app` 关闭时运行的函数: |
|
|
|
|
|
|
@ -48,8 +146,28 @@ |
|
|
|
|
|
|
|
/// |
|
|
|
|
|
|
|
### `startup` 和 `shutdown` 一起使用 |
|
|
|
|
|
|
|
启动和关闭的逻辑很可能是连接在一起的,你可能希望启动某个东西然后结束它,获取一个资源然后释放它等等。 |
|
|
|
|
|
|
|
在不共享逻辑或变量的不同函数中处理这些逻辑比较困难,因为你需要在全局变量中存储值或使用类似的方式。 |
|
|
|
|
|
|
|
因此,推荐使用 `lifespan` 。 |
|
|
|
|
|
|
|
## 技术细节 |
|
|
|
|
|
|
|
只是为好奇者提供的技术细节。🤓 |
|
|
|
|
|
|
|
在底层,这部分是<a href="https://asgi.readthedocs.io/en/latest/specs/lifespan.html" class="external-link" target="_blank">生命周期协议</a>的一部分,参见 ASGI 技术规范,定义了称为启动(`startup`)和关闭(`shutdown`)的事件。 |
|
|
|
|
|
|
|
/// info | 说明 |
|
|
|
|
|
|
|
有关事件处理器的详情,请参阅 <a href="https://www.starlette.io/events/" class="external-link" target="_blank">Starlette 官档 - 事件</a>。 |
|
|
|
有关事件处理器的详情,请参阅 <a href="https://www.starlette.io/lifespan/" class="external-link" target="_blank">Starlette 官档 - 事件</a>。 |
|
|
|
|
|
|
|
包括如何处理生命周期状态,这可以用于程序的其他部分。 |
|
|
|
|
|
|
|
/// |
|
|
|
|
|
|
|
## 子应用 |
|
|
|
|
|
|
|
🚨 **FastAPI** 只会触发主应用中的生命周期事件,不包括[子应用 - 挂载](sub-applications.md){.internal-link target=_blank}中的。 |
|
|
|