如何在使用lifespan的FastAPI中添加shutdown事件?
解决FastAPI中Lifespan与Profiler Shutdown事件冲突的问题
当FastAPI使用lifespan管理数据库等资源时,Starlette会忽略通过add_event_handler添加的shutdown事件,导致PyInstrumentProfiler无法正常导出结果。以下是几种可行的解决方案:
方案一:将Profiler关闭逻辑集成到自定义Lifespan函数
这是最直接且符合Starlette设计规范的方式,把Profiler的结果导出逻辑嵌入你已有的lifespan关闭阶段:
from fastapi import FastAPI from contextlib import asynccontextmanager @asynccontextmanager async def lifespan(app: FastAPI): # 原有启动逻辑:初始化数据库连接等 yield # 新增Profiler结果导出逻辑 for middleware in app.user_middleware: if middleware.cls.__name__ == "PyInstrumentProfilerMiddleware": # 获取Profiler中间件实例并调用导出方法 await middleware.cls.get_profiler_result() # 同步方法则去掉await break app = FastAPI(lifespan=lifespan) app.add_middleware(PyInstrumentProfilerMiddleware, profiler_output_type="html", open_in_browser=True)
注意:需确认
get_profiler_result是同步还是异步方法,同步方法直接调用即可,异步方法需添加await。
方案二:修改Profiler中间件以支持Lifespan事件
如果不想改动原有lifespan函数,可以修改PyInstrumentProfilerMiddleware的__call__方法,使其正确捕获ASGI Lifespan的shutdown事件:
class PyInstrumentProfilerMiddleware: # 保留原有初始化等代码... async def __call__(self, scope, receive, send): if scope["type"] == "lifespan": while True: message = await receive() if message["type"] == "lifespan.shutdown": # 触发Profiler结果导出 await self.get_profiler_result() # 同步方法去掉await await send({"type": "lifespan.shutdown.complete"}) return elif message["type"] == "lifespan.startup": await send({"type": "lifespan.startup.complete"}) else: # 保留原有请求处理逻辑 await self.app(scope, receive, send)
使用修改后的中间件,即可在应用关闭时自动触发Profiler结果导出,且不影响原有lifespan逻辑。
方案三:全局依赖项钩子(备选)
如果以上方案不适用,可通过全局依赖项添加shutdown逻辑(注:此方式针对每个请求结束触发,非全局应用关闭):
from fastapi import Depends async def profiler_shutdown_hook(): yield # 导出Profiler结果 await app.state.profiler.get_profiler_result() app = FastAPI(lifespan=lifespan) app.add_middleware(PyInstrumentProfilerMiddleware, profiler_output_type="html", open_in_browser=True) # 注册全局依赖 app.router.dependencies.append(Depends(profiler_shutdown_hook))
内容的提问来源于stack exchange,提问作者serendipty01
相关产品推荐
相关产品推荐

