package svccfg import ( "context" "os" "yunyan/comm" "yunyan/lego/core" "yunyan/lego/core/cbase" "yunyan/lego/sys/log" ) // configComp 注册集群内的配置下发处理器。args/reply 用普通 struct——rpcx 走 MsgPack,无需 pb 类型。 type configComp struct { cbase.ModuleCompBase module *SvcCfg service comm.IService } func (this *configComp) Init(service core.IService, module core.IModule, comp core.IModuleComp, opt core.IModuleOptions) (err error) { if err = this.ModuleCompBase.Init(service, module, comp, opt); err != nil { return } this.module = module.(*SvcCfg) this.service = service.(comm.IService) return } func (this *configComp) Start() (err error) { if err = this.ModuleCompBase.Start(); err != nil { return } if err = this.service.Register(string(comm.Rpc_ReloadModuleConfig), this.Rpc_ReloadModuleConfig); err != nil { return } // reset 处理器各服务都注册,但只有属主会真的执行(见下)——注册在非属主上是为了给出明确的拒绝理由, // 而不是让调用方收到"方法不存在"这种含糊的 rpc 错误。 if err = this.service.Register(string(comm.Rpc_ResetModuleConfig), this.Rpc_ResetModuleConfig); err != nil { return } log.Infof("[SvcCfg] 业务配置下发处理器已注册 service=%s owner=%v reloaders=%v", this.service.GetType(), this.service.GetType() == comm.ModuleConfigOwner, comm.HasModuleReloaders()) return } func (this *configComp) instance() string { host, _ := os.Hostname() if host == "" { host = "unknown" } return this.service.GetType() + "@" + host } // Rpc_ReloadModuleConfig 重读业务库、热替换本服务持有的 sys 客户端。 // 失败信息放在 reply.Err 而非 rpc error:这样 api 汇总时能区分「某个服务重载失败」与「整条 rpc 调不通」。 func (this *configComp) Rpc_ReloadModuleConfig(ctx context.Context, args *comm.ReloadModuleConfigReq, reply *comm.ModuleConfigResp) (err error) { reply.Service, reply.Instance = this.service.GetType(), this.instance() if !comm.HasModuleReloaders() { reply.Skipped = true // 本服务不持有任何业务凭据客户端(如 gateway/timer),什么也不用做 return } results, e := comm.DoReloadModuleConfig() if e != nil { reply.Err = e.Error() return } reply.Results = results log.Infof("[SvcCfg] 已重载业务配置 service=%s reason=%s modules=%d", reply.Service, args.Reason, len(results)) return } // Rpc_ResetModuleConfig 把某模块还原为**本服务配置文件**里的初始值。 // // 只有配置属主(comm.ModuleConfigOwner=home)会执行:yaml 每服务一份,非属主的 yaml 不是权威初始值, // 让它们也写库会互相覆盖。还原后仍需由 api 扇出 reload,让其余服务重读库。 func (this *configComp) Rpc_ResetModuleConfig(ctx context.Context, args *comm.ResetModuleConfigReq, reply *comm.ModuleConfigResp) (err error) { reply.Service, reply.Instance = this.service.GetType(), this.instance() if reply.Service != comm.ModuleConfigOwner { reply.Err = "本服务(" + reply.Service + ")不是业务配置属主(" + comm.ModuleConfigOwner + "),不能从配置文件还原" return } if args.Module == "" { reply.Err = "缺少要还原的模块名" return } // "*" = 整库还原(升级/迁移把库写脏后的整体恢复);否则只还原单个模块。 var results []comm.ModuleApplyResult var e error if args.Module == comm.ModuleResetAll { results, e = comm.DoResetAllModuleConfigToFile() } else { results, e = comm.DoResetModuleConfigToFile(args.Module) } if e != nil { reply.Err = e.Error() return } reply.Results = results log.Infof("[SvcCfg] 已还原为配置文件初始值 service=%s module=%s", reply.Service, args.Module) return }