Skip to content

feat: LifespanManager.register 支持 priority 参数控制启动顺序#1153

Open
AH-Toby wants to merge 2 commits intofastapi-practices:masterfrom
AH-Toby:feat/lifespan-priority
Open

feat: LifespanManager.register 支持 priority 参数控制启动顺序#1153
AH-Toby wants to merge 2 commits intofastapi-practices:masterfrom
AH-Toby:feat/lifespan-priority

Conversation

@AH-Toby
Copy link
Copy Markdown

@AH-Toby AH-Toby commented Apr 13, 2026

当前 LifespanManager 按注册顺序执行 lifespan,顺序由文件 import 顺序隐式决定。在插件化场景下,插件模块各自注册自己的 lifespan 时,无法显式控制执行顺序,可能导致插件在依赖项(如数据库、Redis)初始化完成之前就开始启动。

改动

为 register 方法新增可选的 priority 参数:

传入 priority 的 lifespan 按数字从小到大优先执行
不传 priority 的 lifespan 保持原有注册顺序,追加在有优先级的之后
使用示例

# 优先级 1,最先启动
@lifespan_manager.register(priority=1)
@asynccontextmanager
async def db_lifespan(app: FastAPI): ...

# 优先级 2,数据库之后启动
@lifespan_manager.register(priority=2)
@asynccontextmanager
async def plugin_lifespan(app: FastAPI): ...

# 不传 priority,行为与改动前完全一致
@lifespan_manager.register
@asynccontextmanager
async def register_init(app: FastAPI): ...
向后兼容

不传 priority 时行为与改动前完全一致,现有代码无需任何修改。

@wu-clan
Copy link
Copy Markdown
Member

wu-clan commented Apr 13, 2026

我理解这个 PR 想解决的问题,而且这个问题本身我也认同:
在引入插件 lifespan hook 之后,如果仍然只依赖注册顺序来决定执行先后,后面确实容易出现初始化顺序不稳定的问题

不过我这边有个小建议:相比直接给 LifespanManager.register() 增加通用 priority 参数,也许可以考虑 stage + depends_on 这种拆分方式,语义上会更清晰一些

我的理解是,这里的顺序问题其实有两层:

  • 一层是“阶段顺序”,比如核心初始化应当先于插件初始化
  • 一层是“依赖顺序”,比如某个插件依赖另一个插件先完成初始化

如果这两类问题都统一交给 priority 处理,短期当然能工作,但后面可能会慢慢出现一些维护成本:

  • priority=10/20/100 这类值本身不太能表达语义
  • 新增插件时,大家不太容易判断应该取什么值
  • 后续如果顺序关系越来越多,可能会逐渐演变成靠数字约定执行顺序

所以我个人会更倾向于这样拆:

  • stage 解决粗粒度顺序
    例如只保留少量固定阶段:core -> plugin -> tail
    这样可以明确保证核心生命周期先执行,插件生命周期后执行

  • depends_on 解决插件之间的显式依赖
    比如可以考虑在 plugin.toml 中增加:

    [plugin]
    depends_on = ["dict", "config"]

    然后在插件注册阶段做一次拓扑排序

这样分层之后,职责会更清楚一些:

  • backend/common/lifespan.py 只负责阶段顺序
  • backend/plugin/core.py 负责插件依赖关系和注册顺序

我感觉这样做有几个好处:

  • stage 表达“属于哪一层”
  • depends_on 表达“依赖谁先完成”
  • 比单纯 priority 更容易理解
  • 后续也更方便做缺失依赖、循环依赖这类校验

如果这次只是想先解决“核心初始化必须先于插件 lifespan”这个问题,我觉得其实引入一个比较轻量的 stage 就已经足够了;
如果想顺手把插件生命周期机制做得更稳一点,那我会更推荐往 stage + depends_on 这个方向走,长期维护上应该会更舒服一些

当然,这只是我个人的一点想法,供参考

@AH-Toby
Copy link
Copy Markdown
Author

AH-Toby commented Apr 13, 2026 via email

@AH-Toby AH-Toby force-pushed the feat/lifespan-priority branch from a721d94 to 895ad79 Compare April 13, 2026 13:39
@AH-Toby AH-Toby force-pushed the feat/lifespan-priority branch from 895ad79 to 5277b73 Compare April 13, 2026 13:53
@AH-Toby
Copy link
Copy Markdown
Author

AH-Toby commented Apr 13, 2026

这次我根据你的建议,把顺序问题拆成了两层处理:
1.在 LifespanManager 中引入 stage,用于保证 core 生命周期先于 plugin 生命周期执行;
2.在插件加载阶段读取 plugin.toml 中的 depends_on,通过拓扑排序处理插件之间的显式依赖顺序。

另外这次也补充了几个配套内容:
1.depends_on 的配置校验
2.缺失依赖、未启用依赖、循环依赖的错误处理
3.plugin.schema.json 的同步更新
4.对阶段顺序、同阶段注册顺序、插件依赖排序的测试

其他:
对于没有声明 depends_on 的旧插件,当前仍然兼容,会按空依赖处理,不会影响现有注册流程。
如果你方便的话,麻烦再帮我看一下这版实现是否更符合你的预期,谢谢。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants