跳到主要内容

应用生命周期

这一页解释从 vef.Run(...) 到 HTTP 服务真正开始监听,中间到底发生了什么。

启动顺序

这是 VEF 启动流水线的权威表述,来自 bootstrap.govef.Run)以及 internal/bootmodules/bootmodules.goAssemble 函数(由 vef.Runinternal/apptest 测试脚手架共用,保证两个 FX 图不会出现分叉):

config -> datasource -> middleware -> api -> security -> event -> expression -> js -> cqrs -> cron -> redis -> lock -> mold -> storage -> sequence -> tx_memory -> outbox -> redis_stream -> inbox -> schema -> monitor -> mcp -> push -> app

datasource 是单独的一步:它在同一个模块里把 *sql.DB 连接起来(通过 internal/database)并包装成 orm.DB(通过 internal/orm)——启动流程里 并不存在独立的 databaseorm 步骤。tx_memoryoutboxredis_streaminbox 是事件传输子模块——即 tx_memory 传输模块、outbox 传输模块、redis_stream 传输模块和 inbox 模块——排在 sequence 之后、schema 之前注册。js 是共享 JS 引擎模块,push 是 WebSocket 推送模块。

注意清单顺序只是为了可读性:FX 按声明的依赖关系解析实际构建顺序,真正 重要的是依赖形状——

  • 配置供给一切
  • datasource 供给需要 orm.DB 的 API 处理器
  • 安全模块供给受认证请求
  • 事件传输子模块(tx_memory、outbox、redis_stream、inbox)构建在核心 event 模块之上
  • storage、monitor、schema、MCP、push 都在 app 开始监听前完成装配

vef.Run(...) 实际做的事情

vef.Run(...) 按下面的顺序组装 FX app:

  1. fx.WithLogger(newFxLogger) 安装框架 FX logger
  2. 添加 internal config module(内部配置模块)
  3. 添加 internal datasource module(内部数据源模块)
  4. 追加 bootmodules.Assemble 返回的全部 option
  5. 追加用户传入的 options...
  6. 追加 fx.Invoke(cron.StartScheduler)
  7. 追加 fx.Invoke(startApp)
  8. 追加 fx.StartTimeout(defaultTimeout)
  9. 追加 fx.StopTimeout(defaultTimeout*2)
  10. fx.New(opts...) 创建 app
  11. app.Run() 运行它

defaultTimeout30 * time.Second,所以默认启动超时是 30s, 默认停止超时是 60s

因为用户 option 是追加在 bootmodules.Core() 之后的,所以应用模块可以通过 vef.ProvideAPIResource(...) 这类 helper 继续追加 group 成员。如果要替换 core 已经提供的单例,通常要用 vef.Decorate(...)vef.Replace(...), 或者 vef.SupplyFileACL(...) 这类框架 replacement helper;为同一个 service 再注册一个普通 vef.Provide(...) 并不会形成 override。

高级模块可以接收 vef.Lifecycle 并调用 Lifecycle.Append(...) 直接注册 fx.Hookvef.StartHookvef.StopHookvef.StartStopHook 是这些 hook 的便利构造函数。 Lifecycle.Append 的精确签名记录在 public API index 中。

内部的 startApp invoke 会在模块图构建完成后追加 HTTP server 的 lifecycle hook。它的 OnStart 等待 application.Start() 或启动 context 超时;OnStop 调用 application.Stop()

最小启动示例:

func main() {
vef.Run(
ivef.Module,
auth.Module,
sys.Module,
web.Module,
)
}

App 启动阶段

应用模块会先创建 Fiber app,然后按顺序:

  1. 应用“前置”中间件
  2. 挂载 API engine
  3. 应用“后置”中间件

所以 VEF 的中间件顺序有两层:

  • 包裹整个 Fiber app 的 app-level middleware
  • 进入 API engine 后才运行的 api-level middleware

App 级中间件顺序

根据当前实现,常见顺序大致是:

  • compression(-1000
  • headers(-900
  • CORS(-800
  • body-encoding(-750,将客户端可选的 X-Body-Encoding 请求体解码回原始 JSON,作用域 /api
  • content type(-700,JSON/multipart 检查,作用域 /api
  • request ID(-650
  • request logger 绑定(-600
  • panic recovery(-500
  • request record logging(-100
  • API 路由
  • push WebSocket 端点(order 450;启用集成模块时其入站网关位于 400
  • MCP 端点中间件(order 500
  • storage 文件代理路由(order 900
  • SPA fallback middleware(order 1000

这意味着哪怕请求根本没有进入 API engine,某些 app 级中间件也一样会执行。

API 级中间件顺序

进入 API engine 后,请求链当前是:

  • auth
  • contextual
  • data permission
  • rate limit
  • audit
  • handler

这个顺序决定了 handler 里能直接拿到:

  • 已认证的 principal
  • request-scoped orm.DB
  • request-scoped logger
  • 已解析好的数据权限 applier

内置模块自己的启动 hook

一些模块还会在生命周期里做额外事情:

  • datasource 会先 ping 主数据源连接并输出数据库版本
  • event bus 会启动内存事件分发器
  • storage 会初始化需要启动动作的 provider
  • app 会真正启动 HTTP server 并注册 stop hook

所以“应用成功启动”不只是 Fiber 起了,而是整条运行时依赖链都准备好了。

为什么这对排错很重要

如果 VEF 应用启动失败,常见原因通常在这些层级里:

  • 配置文件没找到
  • 数据库配置无效
  • provider 配置不支持
  • FX 构造函数注册错误
  • handler factory 解析失败

理解启动顺序以后,能更快判断故障最可能出在哪一层。

下一步

接下来建议看 路由,理解资源是如何真正变成 HTTP 端点的。