Kratos 热更新需显式启动 Watcher 并循环调用 Next(),conf.Load() 必须在 kratos.New() 前执行且置于 main() 首行,变更后须手动 c.Scan(&bc) 才更新结构体字段。

热更新在 Kratos 里不是“开了就自动生效”的功能,它依赖你显式启动 Watcher 并正确绑定路径;conf.Load() 必须在 kratos.New() 之前执行,否则配置加载阶段已错过,后续 Watch 无效。
conf.Load() 必须放在 main() 第一行
Kratos 的 kratos.New() 内部会立即调用 conf.Load()。如果你把 conf.Load() 放在 logger 初始化、flag 解析或 service 初始化之后,框架已经用空配置启动了——这时再 Watch 也改不动运行时的结构体字段。
- 正确顺序:
conf.Load(...)→conf.Scan(&bc)→kratos.New(...) - 错误示例:在
wireApp()或某个Init()函数里才调c.Load(),此时应用早已初始化完毕 -
conf.Load()返回 error 时必须检查并退出,否则静默失败,后续 Watch 不会触发
Watch() 调用后必须手动循环 Next()
Kratos 的所有 Watcher(包括 file、nacos、polaris)都是单次触发模型:调一次 Next(),只等一次变更。不循环,就不会持续监听。
- 文件源需自己启 goroutine:
go func() { for { if _, err := watcher.Next(); err != nil { log.Warnf("watch config failed: %v", err) return } // reload logic here } }() - Nacos/Consul/Polaris 等远程源同样适用该模式;
contrib/config/polaris/v3的Watcher也是这个语义 - 别指望
c.Watch("key", fn)自动长期运行——它只是注册回调,真正拉变更靠你主动调Next()
配置变更只更新 raw 字节流,不触发热解析
Kratos 的热更新机制只负责把新 YAML/JSON 内容推给 config.Provider,不会自动重新 Scan() 到结构体,也不会感知 Protobuf 字段增删。
- 修改
bootstrap.Server.Port的值 → 会生效(字段存在,类型匹配) - 新增
bootstrap.Etc.FeatureFlag字段但没在 YAML 里加 → 运行时仍是 zero value,无报错 - 删掉 YAML 中已有字段 → 结构体内对应字段值不变,不会被清空
- 必须自己在
Next()回调里显式调c.Scan(&bc),否则结构体永远是旧值
Polaris 配置源 Watcher 启动前要确认 polaris.yaml 存在
contrib/config/polaris/v3 默认使用 polaris.NewConfigAPI(),它会读取当前工作目录下的 polaris.yaml。如果该文件缺失或格式错误,NewConfigAPI() 就会 panic,导致整个 source 构造失败。
-
polaris.yaml至少需包含global.serverAddress和认证信息(如token) - 若想绕过文件读取,可传入自定义
polaris.ConfigAPI实例,用WithServerAddress()等选项显式配置 -
WithNamespace("default")、WithFileGroup("default")、WithFileName("app.yaml")三者必须与北极星控制台中实际配置的三元组完全一致,否则Load()返回空,Watch()也收不到任何变更
最常被忽略的是:热更新不是“改完配置就立刻反映到业务逻辑”,它是一条需要你亲手串起的链路——从 conf.Load() 时机、Watcher.Next() 循环、到手动 Scan() 覆盖结构体。任何一个环节断开,都会表现为“配置明明改了,服务却没反应”。











