应用生命周期
这一页解释从 vef.Run(...) 到 HTTP 服务真正开始监听,中间到底发生了什么。
启动顺序
这是 VEF 启动流水线的权威表述,来自 bootstrap.go(vef.Run)以及
internal/bootmodules/bootmodules.go 的 Assemble 函数(由 vef.Run 和
internal/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)——启动流程里
并不存在独立的 database 或 orm 步骤。tx_memory、outbox、redis_stream、
inbox 是事件传输子模块——即 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:
- 用
fx.WithLogger(newFxLogger)安装框架 FX logger - 添加 internal config module(内部配置模块)
- 添加 internal datasource module(内部数据源模块)
- 追加
bootmodules.Assemble返回的全部 option - 追加用户传入的
options... - 追加
fx.Invoke(cron.StartScheduler) - 追加
fx.Invoke(startApp) - 追加
fx.StartTimeout(defaultTimeout) - 追加
fx.StopTimeout(defaultTimeout*2) - 用
fx.New(opts...)创建 app - 用
app.Run()运行它
defaultTimeout 是 30 * 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.Hook;vef.StartHook、vef.StopHook 和 vef.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,然后按顺序:
- 应用“前置”中间件
- 挂载 API engine
- 应用“后置”中间件
所以 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 端点的。