[alibaba/nacos]1.4.5版本的集群节点数量发生变更时,distro checkSum时会删除节点中的临时服务实例缓存,导致服务发现请求返回空

2025-10-31 591 views
1
nacos版本: 1.4.5 运行环境:jre8/x86/3节点集群+nginx统一代理 现象描述: 将集群中某个节点关闭或启动时,偶尔会出现调用服务发现接口返回的hosts为空的情况。

排查过程: 在出现服务发现返回的hosts为空的时间点,nacos节点的naming-distro.log日志中出现以下记录,服务s4的数据被清理:

2023-04-10 10:14:25,949 INFO to remove keys: [com.alibaba.nacos.naming.iplist.ephemeral.project-1##env-1@@s4], to update keys: [], source: 10.0.0.2:8848

追踪定位: 日志出现在com.alibaba.nacos.naming.consistency.ephemeral.distro.DistroConsistencyServiceImpl#onReceiveChecksums中,该方法处理来自集群内其他节点的distro checkSum请求(/v1/ns/distro/checksum)。

查看该方法处理逻辑,发现在集群节点数量发生变更时,有可能会因各个节点更新healthyList速度不一致,导致处理checkSum请求时错误地移除了部分临时服务实例数据。

整理了一张个人猜测的状态流转图(“负责”指该nacos管理的服务(distro responsible),“缓存”指该nacos持有的服务实例数据,“节点”指该nacos持有的healthyList)。在下图【Step5】阶段,查询s4健康实例的服务发现请求发往Nacos3时,返回空数据列表。

问题分析 在DistroConsistencyServiceImpl#onReceiveChecksums处理中,会准备两个List:更新key(toUpdateKeys)、删除key(toRemoveKeys)。在上图的【Step5】阶段中,Nacos2和Nacos3的healthList不一致,两边都认为服务s4应该由自己负责管理,当Nacos3处理Nacos2发送的checkSums请求时,会将服务s4有关的key放入到toRemoveKeys中,同时并不会放入toUpdateKeys中,导致Nacos3上的服务s4实例缓存在一段时间内不存在: 建议 是否可以在DistroConsistencyServiceImpl#onReceiveChecksums方法的删除toRemoveKeys中key之前增加一段二次检查:“若该key的数据版本号与source server的版本号一致,将该key从toRemoveKeys中移除”?

回答

9

虽然在1.x版本中,nacos client有提供服务发现时防推空功能,但还是想了解下在不升级至2.x版本前提下,nacos server有无更好的解决办法。

0

nacos1挂掉, nacos2和3会重新计算责任节点,s1会被另外一个节点所负责,这个过程一般会小于心跳过期时间,除非你设置了过期时间为几个毫秒。

远离上 在客户端和服务端都是2.0以上的话, 使用长连接, 数据连接到那个节点,哪个节点就是责任节点,停止节点后长连接会飘到其他节点上,这个时间一般在1s内就会完成, 其他节点上旧数据会在3分钟之后才过期移除,期间这部分数据会重复一份。

4

nacos1挂掉, nacos2和3会重新计算责任节点,s1会被另外一个节点所负责,这个过程一般会小于心跳过期时间,除非你设置了过期时间为几个毫秒。

远离上 在客户端和服务端都是2.0以上的话, 使用长连接, 数据连接到那个节点,哪个节点就是责任节点,停止节点后长连接会飘到其他节点上,这个时间一般在1s内就会完成, 其他节点上旧数据会在3分钟之后才过期移除,期间这部分数据会重复一份。

@KomachiSion

nacos1关闭后s1会由其他节点负责,而且在各节点变更healthList完成前,不会在checkSums时删掉自己的s1缓存(nacos2和nacos3之前都不负责s1)。

但是,剩余节点(nacos2和nacos3)本来负责的服务,在集群变动后可能由其他节点负责(比如s4从原来由nacos3负责,改为由nacos2负责),因节点healthyList变更时间不一致,导致nacos3过早删除了s4实例缓存(上面图里的Step 4)。

我能理解这是nacos 1.x集群一致性设计的一部分,目的是让节点发现自己负责服务与集群内其他节点产生冲突时,优先放弃自己的管理权(并清理缓存),来规避集群扩缩容时产生脏数据的可能, 但是该设计也会导致集群扩缩容后请求服务发现接口返回为空的异常现象。

上文第7项的调整建议,可否改善此类问题在1.x版本出现的可能?

1

nacos1挂掉, nacos2和3会重新计算责任节点,s1会被另外一个节点所负责,这个过程一般会小于心跳过期时间,除非你设置了过期时间为几个毫秒。 远离上 在客户端和服务端都是2.0以上的话, 使用长连接, 数据连接到那个节点,哪个节点就是责任节点,停止节点后长连接会飘到其他节点上,这个时间一般在1s内就会完成, 其他节点上旧数据会在3分钟之后才过期移除,期间这部分数据会重复一份。

@KomachiSion

nacos1关闭后s1会由其他节点负责,而且在各节点变更healthList完成前,不会在checkSums时删掉自己的s1缓存(nacos2和nacos3之前都不负责s1)。

但是,剩余节点(nacos2和nacos3)本来负责的服务,在集群变动后可能由其他节点负责(比如s4从原来由nacos3负责,改为由nacos2负责),因节点healthyList变更时间不一致,导致nacos3过早删除了s4实例缓存(上面图里的Step 4)。

我能理解这是nacos 1.x集群一致性设计的一部分,目的是让节点发现自己负责服务与集群内其他节点产生冲突时,优先放弃自己的管理权(并清理缓存),来规避集群扩缩容时产生脏数据的可能, 但是该设计也会导致集群扩缩容后请求服务发现接口返回为空的异常现象。

上文第7项的调整建议,可否改善此类问题在1.x版本出现的可能?

不太合理, 出现冲突时,其实并不知道谁才是正确的,肯定以自身计算为准比较好。 同时删除数据(缓存)对业务损耗会更大。 毕竟一时的脏数据可能只是流量不均, 但是删除数据后一定会导致找不到提供者报错。

建议是赶紧找机会升级到2.0的版本,长连接的机制能比较好的解决这个问题。

7

nacos1挂掉, nacos2和3会重新计算责任节点,s1会被另外一个节点所负责,这个过程一般会小于心跳过期时间,除非你设置了过期时间为几个毫秒。 远离上 在客户端和服务端都是2.0以上的话, 使用长连接, 数据连接到那个节点,哪个节点就是责任节点,停止节点后长连接会飘到其他节点上,这个时间一般在1s内就会完成, 其他节点上旧数据会在3分钟之后才过期移除,期间这部分数据会重复一份。

@KomachiSion nacos1关闭后s1会由其他节点负责,而且在各节点变更healthList完成前,不会在checkSums时删掉自己的s1缓存(nacos2和nacos3之前都不负责s1)。 但是,剩余节点(nacos2和nacos3)本来负责的服务,在集群变动后可能由其他节点负责(比如s4从原来由nacos3负责,改为由nacos2负责),因节点healthyList变更时间不一致,导致nacos3过早删除了s4实例缓存(上面图里的Step 4)。 我能理解这是nacos 1.x集群一致性设计的一部分,目的是让节点发现自己负责服务与集群内其他节点产生冲突时,优先放弃自己的管理权(并清理缓存),来规避集群扩缩容时产生脏数据的可能, 但是该设计也会导致集群扩缩容后请求服务发现接口返回为空的异常现象。 上文第7项的调整建议,可否改善此类问题在1.x版本出现的可能?

不太合理, 出现冲突时,其实并不知道谁才是正确的,肯定以自身计算为准比较好。 同时删除数据(缓存)对业务损耗会更大。 毕竟一时的脏数据可能只是流量不均, 但是删除数据后一定会导致找不到提供者报错。

建议是赶紧找机会升级到2.0的版本,长连接的机制能比较好的解决这个问题。

@KomachiSion 但是当前1.x版本处理方式,并不是以自身计算为准,而是“直接删除了自身数据,却也并未采纳对方数据”,导致在下一轮数据校验同步完成前,找不到提供者报错:

2.x长连接设计的确能提供更好的使用体验,这个我也了解,但社区当前也并未彻底遗弃1.x版本(陆续发布了1.4.4、1.4.5等fix版本)。 现实情况是1.x版本就是存在一种集群正常扩缩容场景下几乎必现的实例缓存丢失问题,想咨询下社区团队对于问题的看法,是真实的缺陷?还是设计如此?

1

Save problem ! 1.x 版本还能不能正常线上使用的?

8

就是因为都是以自身计算为准, 所以服务端认为这个服务并非自己负责的节点,来源给的checksum是移除了,因此移除数据是正常的。这里的以自身计算为准,指的是计算的责任节点,而不是实际的数据内容,你大概是误解了代码的意思,认为是服务数据。

Nacos的非持久化实例作为AP协议,保证的是最终一致性, 尽量做到数据过程中的一致性,但会有一定延迟存在。

如果不能升级2.0版本, 可以升级到1.4.2以上的客户端, 开启推空保护功能,防止AP协议过程中短暂数据不一致的场景。

但社区当前也并未彻底遗弃1.x版本(陆续发布了1.4.4、1.4.5等fix版本)。

1.X 已经进入生命周期末期的维护阶段, 发版节奏很慢且不会有新功能添加。机制和整个1.X阶段也是一致的。估计很快会结束生命周期。还是建议尝试升级到2.X

3

@KomachiSion 如果采用升级nacos-server集群至2.x,客户端保持使用1.x版本,可以规避这类问题吗? (之所以不使用2.x客户端的原因:服务实例和nacos-server中间会存在多层网络代理,不便于使用grpc长连接)

9
新版本2.x服务端会同步心跳时间,切换的时候不容易删除旧数据 发布时可以先不开启端口映射,等待一段时间,或者判断数据一致了再开启流量进入。