灰度发布在k8s上丢失的问题
我这边在实际场景使用时发现在k8s场景下多次灰度发布会有部分收不到最新配置,只能靠5min一次的兜底刷新获取最新配置,查看代码发现是灰度版本发布主要靠GrayReleaseRulesHolder来确定获取哪条release 我注意到这个GrayReleaseRulesHolder是靠ReleaseMessage扫描来获得通知的,这个通知靠定时任务导致各pod的进度不一,在config缓存的类ConfigServiceWithCache中我有看到靠client端传来的notificationId对比来确定是否当前pod的配置是最新的,不是则传统缓存去读库 为何GrayReleaseRulesHolder同样会影响到获取哪个配置,没使用类似的设计来保证获取的是最新的配置呢?这样不是会导致灰度发布后有部分节点无法获取最新配置么
你说的现象确实存在,根本原因在于 GrayReleaseRulesHolder 的更新机制和 ConfigServiceWithCache 不一样,导致在 K8s 多副本场景下灰度规则的同步存在延迟和不一致。
GrayReleaseRulesHolder 依赖 ReleaseMessage 通知和定时任务(比如每 5 分钟全量扫描)来更新灰度规则缓存。由于 ReleaseMessage 的消费和定时任务在每个 pod 上是异步的,所以不同 pod 上的 GrayReleaseRulesHolder 状态可能不同步,部分节点就会短时间内拿不到最新的灰度规则,只有等到下次定时扫描或消息消费后才会更新,这就是你看到的“只能靠5min一次的兜底刷新获取最新配置”的原因相关代码实现。
而 ConfigServiceWithCache 是通过客户端带来的 notificationId 判断本地缓存是否过期,如果发现客户端的 notificationId 比本地新,会主动失效缓存并从数据库拉取最新配置,这样可以保证只要客户端拿到新 notificationId,服务端就能及时返回最新配置机制细节。
GrayReleaseRulesHolder 没有用 notificationId 机制,主要是因为灰度规则的变更频率远低于配置发布,且规则本身是服务端行为,客户端无法感知规则的 notificationId。现有设计下,确实会导致灰度发布后部分节点短时间内无法获取最新规则,属于 Apollo 的已知一致性弱点官方说明。
如果你希望缩短延迟,可以调小灰度规则扫描间隔(grayReleaseRuleScanInterval),但这会增加数据库压力。或者关注 Apollo 2.5.0+ 的增量同步能力(config-service.incremental.change.enabled),虽然它主要优化配置同步,但也能一定程度提升多副本环境下的同步及时性增量同步 PR 说明。
总之,灰度规则同步目前没有像配置缓存那样的强一致性机制,K8s 多副本下短暂不一致是设计上的 trade-off。如果业务对一致性要求极高,建议灰度发布窗口适当拉长,或结合实例重启等手段规避极端情况。
To reply, just mention @dosu.
How did I do? Good | Irrelevant | Incorrect | Verbose | Hallucination | Report 🐛 | Other
但是灰度发布和灰度规则变更都会引发这个问题,我复现下来看触发几率不小
为何不能在GrayReleaseRulesHolder中引入类似的机制,如果client端传过来的notificationid比缓存中的更大则穿透缓存
GrayReleaseRulesHolder 目前没有采用类似 ConfigServiceWithCache 的 notificationId 机制,主要有以下几个技术原因:
-
灰度规则(GrayReleaseRule)本身没有像配置发布那样的 notificationId 体系。ConfigServiceWithCache 能用 notificationId 判断缓存是否过期,是因为每次配置发布都会生成唯一的 ReleaseMessage(带有递增 id),客户端和服务端都能感知和对比。而灰度规则的变更(比如新建、修改、删除规则)并不会生成独立的 notificationId,也没有和 ReleaseMessage 一一对应的机制,导致客户端无法感知灰度规则的“最新状态”编号,服务端也无法用 client 传来的 notificationId 做有效对比和穿透缓存代码实现。
-
GrayReleaseRulesHolder 的缓存更新完全依赖于服务端的 ReleaseMessage 通知和定时全量扫描,和客户端请求无关。即使客户端传递 notificationId,GrayReleaseRulesHolder 也无法判断这个 id 是否对应最新的灰度规则,因为灰度规则的变更没有 notificationId 追踪实现细节。
-
要实现类似机制,需要对灰度规则的每次变更都生成唯一的“规则变更 id”并同步到客户端,客户端再带回这个 id,服务端才能判断是否需要穿透缓存。这涉及 Apollo 架构的较大改动,目前灰度规则的发布和配置发布是两套独立流程,缺乏统一的变更追踪体系。
-
GrayReleaseRulesHolder 内部虽然有 loadVersion 字段用于本地缓存版本管理,但这个版本号并不对外暴露,也没有和客户端同步的机制GrayReleaseRuleCache 结构。
综上,GrayReleaseRulesHolder 不能直接引入 notificationId 穿透缓存,主要是因为灰度规则没有 notificationId 体系,客户端也无法感知规则的变更。要实现类似机制,需要对灰度规则的变更、发布、通知链路做较大改造。
To reply, just mention @dosu.
How did I do? Good | Irrelevant | Incorrect | Verbose | Hallucination | Report 🐛 | Other
为何不能在GrayReleaseRulesHolder中引入类似的机制,如果client端传过来的notificationid比缓存中的更大则穿透缓存
其实是可以这么做的,因为 GrayReleaseRulesHolder 也是通过 ReleaseMessage 触发更新的
尝试实现了下,这个merge操作有并发问题,需要加锁,另外为了防止穿透缓存过于频繁,需要加个双重检查锁,5个service pod ,600个client的情况下能节省20倍
This issue has been automatically marked as stale because it has not had activity in the last 30 days. It will be closed in 7 days unless it is tagged "help wanted" or other activity occurs. Thank you for your contributions.