Python asgi 的 lifespan 协议使用

2次阅读

asgi lifespan 是服务器启动/关闭时强制调用的生命周期钩子,必须实现为异步生成器,仅 yield 两次以分别表示 startup 和 shutdown 完成,否则将报错或导致资源泄漏。

Python asgi 的 lifespan 协议使用

asgi lifespan 是什么,什么时候必须实现

它不是可选装饰,而是 ASGI 服务器(如 Uvicorn、Hypercorn)启动/关闭时强制调用的生命周期钩子。如果你的应用需要在服务启动时初始化数据库连接池、加载配置或预热缓存,又或者要在退出前优雅关闭连接、刷写日志,lifespan 就是唯一标准入口。没实现它,Uvicorn 启动时会报 TypeError: lifespan handler not implemented;强行忽略,资源泄漏几乎必然发生。

怎么写一个合规的 lifespan handler

必须是异步可调用对象,接收一个 scope 字典,返回一个异步生成器(async def + yield),且只 yield 两次:第一次表示 startup 完成,第二次表示 shutdown 完成。任何偏离这个协议的行为都会导致服务器卡死或直接崩溃。

常见错误现象: 写成普通函数、漏掉 yield、yield 超过两次、startup 中抛异常没捕获、shutdown 里 await 了未完成的协程

  • 使用 async def lifespan(app),不是 deflambda
  • scope["type"] 一定是 "lifespan",别和 httpwebsocket 混淆
  • startup 阶段失败必须抛出异常(让服务器 abort),不能静默吞掉;shutdown 阶段异常会被忽略,但应尽量避免
  • 不要在 lifespan 里做耗时同步操作(如 time.sleep(5)),会阻塞整个事件循环

示例片段:

立即学习Python免费学习笔记(深入)”;

async def lifespan(app):     # startup     await database.connect()     yield     # shutdown     await database.disconnect()

Uvicorn 启动时 lifespan 不触发?检查这三处

不是代码写错,而是运行环境没对齐。最常踩的坑是:你以为在跑 ASGI,其实启动的是 WSGI 模式,或者用了错误的入口参数。

  • 确保启动命令显式指定 --lifespan on(Uvicorn 0.20+ 默认是 on,但老版本或某些 CI 环境可能关着)
  • 确认 ASGI 应用对象暴露的是 app(或你命名的变量),而不是 app.appapplication —— 错的变量名会让 Uvicorn 找不到 lifespan 协议
  • 检查是否误用了 uvicorn.run(..., loop="asyncio") 之类的老参数,新版本已弃用;正确方式是直接传 apphost/port

验证方法:加一行 print("lifespan started") 在 yield 前,启动时没输出,基本就是上述某处断开了。

多个 lifespan handler 怎么组合?别手动链式调用

ASGI 规范不支持多个并列的 lifespan handler。如果你用了 Starlette、fastapi 或自定义中间件,它们内部可能已注册了自己的 lifespan 逻辑。硬写两个 async def lifespan 并试图 yield 来回切换,结果是服务器拒绝启动或行为不可预测。

正确做法是:只保留一个顶层 handler,在里面显式 await 其他组件的初始化/清理逻辑。

  • Starlette 的 on_event("startup")on_event("shutdown") 已被标记为 deprecated,必须迁移到 lifespan
  • FastAPI 的 @app.on_event("startup") 底层仍是封装 lifespan,但仅限于 FastAPI 实例自身;若你还套了一层 middleware 或自定义 app 类,得自己合并逻辑
  • 数据库连接池(如 asyncpg、Tortoise)通常提供 .init() / .close_connections() 方法,直接 await 它们即可,别再包一层 asyncio.create_task

复杂点在于:有些库的 cleanup 是“尽力而为”型(比如不保证 flush 日志),而 ASGI lifespan shutdown 阶段一旦 yield,服务器就认为你已完成——没做完的,就真做不完了。

text=ZqhQzanResources