refactor(config): 重构 Etcd 配置监听机制#369
Conversation
- 替换 Viper 内置的 Etcd 监听器 - 使用 `go.etcd.io/etcd/client/v3` 客户端实现配置热更新 - 提高配置变更检测的可靠性和灵活性
ozline
left a comment
There was a problem hiding this comment.
单独开一个协程没有问题,但是如果我们需要关闭这个服务,如何通知协程关闭?
你可能需要引入一个 context
在什么情况下要单独关闭该服务,在etcd环境下运行时对配置的热重载应该没必要关闭,而当使用了k8s环境,该协程不会启动,也不会有关闭该协程的需求 |
这个是优雅退出的一部分,如果没有传递 ctx,在服务关闭的过程中这个 goroutine 仍然在运行,最后的结果类似于「强制掐断」,如果携带了 ctx,我们可以通过 ctx 传递服务已关闭(这个通常直接通过一个支持死亡的 ctx 来做就可以了),在服务优雅关闭过程中我们让上下文先关闭,之后再退出就行 因为这个 pr 不是紧急改动,你可以稍微改多一些 |
- 为非 k8s 环境的服务启动 Etcd 配置热更新监听 - 在服务关闭时通过 context 取消 Etcd 监听 goroutine - 优化 Etcd 客户端关闭逻辑,确保资源释放
|
因为要使用kitex和hertz框架下的优雅关闭,考虑到在init里实现较为困难,在这里采取在main函数中判断当前环境,非k8s状态下创建一个可取消的ctx并开启一个协程监听配置文件变化,并在 OnShutdown与RegisterShutdownHook 下注册ctx取消函数 |
可以问下为什么要在 非 k8s 场景在才有相关改动吗?k8s 场景会带来什么影响? |
修改前 Etcd出现配置文件更新后Viper获取不到配置文件更改的信息 导致无法进行热更新,修改的主要依据是Etcd会在配置文件变化时向客户端发出事件,从而通知Viper重新读取最新的配置文件并进行重映射。k8s下配置文件并不依赖Etcd,应该可以使用Viper的监听,不需要启动Etcd的监听 |
|
现在我来康康,哎开源就很容易一拖拖很久,工作了有 KPI 绩效考勤卡着你不会让你拖这么久 |
|
@2451965602 尝试一下 git merge 来解决文件冲突哈,这个会非常常用,尽量自己解决冲突 一般以 main 分支的代码为准,但不要一股脑都用 main 的代码 |
|
|
||
| func main() { | ||
| var watcherCancel context.CancelFunc | ||
| if os.Getenv("DEPLOY_ENV") != "k8s" { |
| } | ||
| case <-ctx.Done(): | ||
| logger.Infof("Stopping etcd config watcher.") | ||
| err = cli.Close() |
There was a problem hiding this comment.
这个部分改用 defer 实现,一般这种类似于 fs 操作的,我们会在创建后立刻写一个 defer,这样可读性也好,而且 cr 的时候可以一眼看到确实关闭了
# Conflicts: # cmd/academic/main.go # cmd/classroom/main.go # cmd/course/main.go # cmd/oa/main.go # cmd/paper/main.go # cmd/user/main.go # cmd/version/main.go
- 将硬编码的 "DEPLOY_ENV" 替换为 `constants.DeployEnv` 常量 - 确保 etcd 客户端在函数退出时正确关闭
| if os.Getenv(constants.DeployEnv) != "k8s" { | ||
| watcherCtx, cancel := context.WithCancel(context.Background()) | ||
| watcherCancel = cancel | ||
| go config.StartEtcdWatcher(watcherCtx, serviceName) |
There was a problem hiding this comment.
我重新看了一下整个链条,我尝试问一个设计的修改:
这里对于每一个模块(需要注意的是这个 pr 由于比较久了,缺少了新增的 captcha 模块),都使用了一模一样的代码,我们能否尝试把这些代码挪到 config.go 配置模块里呢?
这样在每一个模块的 main 函数(或者说更前面的init()函数,init()执行顺序是优先于 main()的)里只需要调用一个 config 的初始化函数就可以了?
因为我看现在的逻辑,会在init()执行时初始化配置,再一直到 main()时开始监听,这个我觉得可以合并一下
你可以看看方案是否有必要
ozline
left a comment
There was a problem hiding this comment.
还有一个额外的问题:我们实际场景是容器化部署的,从这个 env 的传参以及目前的修改来看,我好像没找到地方传入这个环境变量
这个环境变量是容器环境现在就有的吗?如果不是,你可能需要修改一下容器的启动配置
自查 PR 结构
PR 标题符合这个格式: <type>(optional scope): <description>
此 PR 标题的描述以用户为导向,足够清晰,其他人可以理解。
我已经对所有 commit 提供了签名(GPG 密钥签名、SSH 密钥签名)
这个 PR 属于强制变更/破坏性更改
这个 PR 的类型是什么?
refactor(config): 重构 Etcd 配置监听机制
这个 PR 做了什么 / 我们为什么需要这个 PR?
go.etcd.io/etcd/client/v3客户端实现配置热更新go.etcd.io/etcd/client/v3原生 Watch 接口,支持实时监听配置变更并立即生效,无需重启服务。(可选)这个 PR 解决了哪个/些 issue?
对 Reviewer 预留的一些提醒