Kratos App 生命周期
App 是 Kratos 的生命周期编排器。它不创建具体服务器,而是把 hooks、server、registrar 和 OS signal 排成可观察的启动与退出顺序。
先给答案:Kratos 用统一生命周期把“启动顺序”和“退出顺序”显式化
Section titled “先给答案:Kratos 用统一生命周期把“启动顺序”和“退出顺序”显式化”应用启动时依次准备依赖组件,再启动 HTTP/gRPC server;收到退出信号后先停止接收新请求,再等待在途请求、关闭连接、停止后台任务并注销注册信息。生命周期接口的价值是把这些动作从 main 函数里的手工顺序变成可组合流程。
如果启动顺序错误,server 可能先接收请求但配置或注册中心尚未就绪;如果退出顺序错误,实例会先从进程消失再从注册中心删除,调用方会得到一段时间的连接失败。优雅退出的本质是控制状态转换,而不是简单延迟几秒。
Run 顺序
Section titled “Run 顺序”buildInstance |beforeStart hooks |start all servers concurrently |register service |afterStart hooks |signal or Stop |deregister -> cancel -> server.Stop |afterStop hooksRun 在 app.go:82-150 完成全流程:先生成 instance,再执行 beforeStart,使用 errgroup 并发启动 server,等待启动 goroutine 进入运行态后注册,最后监听信号。注册超时由 options.go:86-99 的 registrar/stop timeout 控制。
为什么先启动再注册
Section titled “为什么先启动再注册”注册前必须确保 endpoint 已监听,否则服务发现可能拿到一个尚未可用的地址。buildInstance 会优先使用显式 endpoints,没有时从实现 transport.Endpointer 的 server 读取实际地址:app.go:176-198。
Stop 先执行 beforeStop,再调用 registrar.Deregister,最后取消 App context:app.go:153-173。服务器停止 goroutine 监听该 context,使用 context.WithoutCancel 保留停止阶段上下文,并可附加 stopTimeout:app.go:100-117。
替代方案:收到信号后直接强制退出进程。 问题:请求、注册信息和连接没有释放机会。 设计:先摘除注册,再广播取消,server 自己实现协议级优雅停止;超时后由具体 transport 强制收尾。
| 场景 | 风险 | 处理 |
|---|---|---|
| afterStart 失败 | 服务已注册但启动失败 | 业务 hook 要保证幂等并主动清理 |
| registrar 超时 | Run 直接返回错误 | 调整 RegistrarTimeout,不要无限等待 |
| 多 server | 部分启动成功 | errgroup 汇总错误,停止路径需可重复 |
| Stop 多次调用 | 重复注销 | registrar 实现应具备幂等性 |
服务生命周期的关键不是“启动函数”,而是注册可见性、流量接收和退出顺序之间的契约。
面试锚点
- 为什么注册发生在 server 启动之后?
errgroup和WaitGroup在 Run 中分别承担什么职责?- Kratos 如何保证优雅停止有超时边界?