事件与生命周期
应用生命周期管理的核心问题:资源什么时候创建、什么时候销毁(连接池、配置、后台任务)。FastAPI 用 lifespan 统一管理,事件模式则解决组件间的解耦通知。
FastAPI 的 lifespan
from contextlib import asynccontextmanager
@asynccontextmanager
async def lifespan(app: FastAPI):
# 启动时: 创建连接池、加载模型、启动后台任务
app.state.pool = await create_pool()
yield
# 关闭时: 释放连接池、停止任务
await app.state.pool.close()
app = FastAPI(lifespan=lifespan)- 启动钩子:
yield之前执行,应用开始接收请求前完成 - 关闭钩子:
yield之后执行,进程退出时清理 - 替代了旧的
@app.on_event("startup"/"shutdown")(已废弃),lifespan 是上下文管理器,异常处理更清晰 - 共享状态放
app.state,处理器和依赖通过request.app.state访问
资源生命周期全景
| 资源 | 创建时机 | 销毁时机 |
|---|---|---|
| 配置读取 | 模块加载 / lifespan | 进程结束 |
| 数据库连接池 | lifespan 启动 | lifespan 关闭 |
| 数据库会话 | 每次请求(依赖) | 请求结束(yield) |
| 后台任务(定时器) | lifespan 启动 | lifespan 关闭 |
分层记忆:进程级(lifespan)→ 请求级(依赖)→ 函数级(上下文管理器),每层的资源在对应层创建和释放。
事件模式(发布订阅)
事件驱动的本意:生产者和消费者解耦,不直接调用。
from collections import defaultdict
from dataclasses import dataclass, field
from typing import Callable
class EventBus:
def __init__(self):
self._handlers: dict[str, list[Callable]] = defaultdict(list)
def on(self, event: str, handler: Callable):
self._handlers[event].append(handler)
def emit(self, event: str, payload=None):
for handler in self._handlers[event]:
handler(payload)
bus = EventBus()
bus.on("order.created", send_notification) # 下单后通知
bus.on("order.created", update_inventory) # 同一事件多个订阅者价值:订单服务不用知道谁关心“下单”这件事,新增订阅者不改发布方代码(开闭原则)。Python 里事件总线也可以用回调、信号库(blinker)实现。
注意边界:进程内事件用 EventBus;跨服务事件用消息队列(Kafka/RabbitMQ),事件总线不是分布式方案。
与 Java 对比
| 维度 | Python/FastAPI | Spring |
|---|---|---|
| 生命周期 | lifespan 上下文管理器 | @PostConstruct/@PreDestroy、ApplicationListener |
| 事件 | EventBus / blinker | ApplicationEventPublisher |
| 异步 | asyncio 原生 | @Async + 线程池 |
面试追问
- lifespan 解决什么? 资源创建和销毁的时机管理:启动建连接池,关闭释放。上下文管理器保证异常时也执行清理
- startup 事件为什么废弃? on_event 生命周期语义弱(重复注册、无清理保证)。lifespan 是标准上下文管理器
- app.state 干什么? 存应用级共享状态(连接池、配置),处理器通过 request.app.state 访问
- 事件模式的价值? 发布者和订阅者解耦,新增订阅不改发布方。进程内用 EventBus,跨服务用 MQ
- 三层资源生命周期? 进程级 lifespan、请求级依赖、函数级上下文管理器,各层资源各层释放