Nacos 注册中心和配置管理的数据一致性

Nacos 官方 描述了其命名的由来 Nacos: Dynamic Naming and Configuration Service;按照目前主流的使用场景,Nacos 有两个核心能力:注册中心(Naming)和配置管理(Configuration);而这里我们讨论注册中心和配置管理的数据一致性。

本文将会涉及以下内容:

注册中心的数据一致性剖析,服务端对请求的处理、客户端的行为配置管理的数据一致性剖析,服务端对请求的处理、客户端的行为附加1: Nacos 2.x 对比 1.4.x 最主要有了哪些提升?

gRPC 长链接全双工交互 附加2:建议的 Nacos 部署架构

配置管理集群 + 注册中心集群

关键词:

关键词含义DistroNacos 实现的数据最终一致性协议,用在注册中心模块JRaftSOFAJRaft,蚂蚁 SOFA 实现 Java 版的 Raft 分布式一致性组件List-Watchlist-watch 机制,watch 实时通知,list 兜底轮询;既兼顾了实时性也达到最终一致gRPCA high performance, open source universal RPC framework, gRPC on HTTP/2. 长链接全双工,grpc-java 基于 netty 实现。

注册中心的数据一致性剖析

注册中心-请求剖析

图(1)中 NamingRequest(服务注册、反注册、心跳保活) 这类自带更新的请求,均会经过 DistroFilter ,其会决策是否本节点负责该数据的更新(有些哈希责任分片的韵味)。 非本节点负责则会转发到目标节点,走的路是 No responsible --> dispatch to targetServer。 本节点的流程会先判断是否为 ephemeral 短暂数据(默认的服务注册均是短暂实例,依靠客户端发起的心跳保活不断续命;应用正常下线会主动反注册,异常宕机则由超时机制剔除它,都会通知订阅者),路由到 Distro 内存存储(一般的使用都不用看 Raft 持久化存储… ),节点之间异步执行数据同步达到最终一致性,保障了最终一致的读请求(例如服务订阅、服务列表)。

Distro Protocol 总结下来就以下逻辑:

数据更新后:主动落异步任务发送请求给其他节点(99.99% 情况实现了数据同步)异步检测任务(兜底):每个节点均有异步检测任务,遍历每个服务,向它的责任节点请求 checksum(数据摘要),若不一致则拉取最新数据。

多多想想,其实上面的逻辑就保证了数据的最终一致性。

图(1)中 NamingRequest(服务订阅、服务列表) 这类读请求,任何一个节点收到请求都会直接处理,是 ephemeral 则从 Distro 内存存储里获取并返回; not ephemeral, is persistent 会走向 Raft,请施主自行了解啦,JRaft 的读写是哪样的。

最后总结下,默认服务注册均是 ephemeral 短暂实例,由 Distro 最终一致性保障,而 Raft 就当作趣味看看好了,若你想深究的话再讨论讨论(ServiceName 等服务元信息是 persistent 持久化存储的)。

注册中心-客户端剖析

图(2) 分了左右两部分,左侧是 nacos-client 1.x 版本的逻辑,右侧是 nacos-client 2.x 版本的逻辑。你将看到全世界通用的 List-Watch 机制,watch 实时通知,list 兜底轮询。

nacos-client-naming 1.x,客户端初始化会主动启用一个 udp 端口(用于被服务端推送数据),同时也有异步任务(兜底)定时轮询服务列表; nacos-client-naming 2.x,客户端初始化会与服务端建立 gRPC 长链接,启动异步任务(兜底)定时轮询服务列表,由于长链接服务端可通过 gRPC 主动推送数据到客户端;

无论 1.x 还是 2.x 都由 list-watch 机制保障客户端侧数据的最终一致性,也即可以通俗地说:订阅者必定会获取到最终一致的提供者列表。

注意:服务变更通知在 1.x 采用 upd,而 upd 推送不可靠,有低概率丢失;更严峻的是网络联通性问题: 若 Nacos 与应用客户端在不同网络域,例如应用客户端能够访问 Nacos 这条路打通了,但 Nacos 访问应用客户端的网络道路能通吗?无论什么原因不允许都会让服务变更 upd 主动推送失败。

配置管理的数据一致性剖析

之前写过一篇陋文:Nacos 1.4.x 配置管理交互与源码简解,2.x 在此逻辑上变化不大,其主要变化部分是 Long-polling 由 gRPC 长链接替代。 LP 相对来说,可以看作为服务端实时通知客户端的渠道,配置变更时即可让客户端实时感知到;而 2.x 采用 gRPC 长链接全双工后,服务端可用 gRPC 主动地通知。

配置管理- 1.x 大体逻辑

nacos-client-config 1.x 客户端初始异步任务,不断地发起 listen(check-config-md5) 请求到服务端,

若 md5 值不一致,服务端会立刻返回,让客户端调用 get-config 获取最新配置;若 md5 值一致,服务端会把这个请求 hold 住(也即一种说法叫 long-polling),等待超时或者 notify back LP 返回客户端。

update-config 会通过 Notify 机制通知其他节点,也发送配置变更事件 notify back LP 让 long-polling 返回。

配置管理- 2.x 大体逻辑

nacos-client-config 2.x 客户端初始异步任务,长间隔地发起 listen(check-config-md5) 请求到服务端(不再 LP 所以间隔调大到了 5min 保证 list-watch 的 list); 与 1.x 的主要区别是 notify config change 配置变更事件由服务端主动通过 gRPC 请求到客户端。其他的逻辑变化不大。

附加1: Nacos 2.x 对比 1.4.x 的最主要提升

无疑是将所有的网络交互(客户端-服务端,服务端集群之间)采用 gRPC(Http2) 长链接全双工,至于网络协议的优缺点请阅:gRPC 长连接在微服务业务系统中的实践,本文作者不够专业不敢吹水,唯有讨论下此举在 Nacos 上哪些方面有提升表现。

总体上:(客户端-服务端,服务端集群之间)减少了不断地进行 Http 短链接的建立与断开,TCP Time Wait 数目。

注册中心部分:

Distro 转发的连接数减少。 在 1.x 的服务间转发是内部做了一次 Http 请求,大致如此:client -> server1 -> server2(target)。这样白白浪费了一个 Tomcat 线程,若你知道 Tomcat 默认线程数是 200 你便会知道这个转发多昂贵了。而且根据责任分片,这个转发的概率是 (N-1)/N,N 是 nacos-server 节点数;总结下来就是平均一个请求需要耗费 1+(N-1)/N 个 Tomcat 线程。而服务端集群之间采用 gRPC(Http2) 长链接全双工,免了转发的链接建立,复用已有的长链接便可。

配置管理部分:

配置变更实时监听 Long-polling 由 gRPC 长链接替代,提升不会非常明显,符合总体描述。

附加2:建议 Nacos 部署架构

配置管理集群 + 注册中心集群

Nacos 注册中心/配置管理是核心基础设施,变更牵涉面比较广;日常 Nacos 迭代过程中,集群的升级/重启 对依赖其的应用服务影响,主要影响在注册中心模块,因为注册中心会有责任分片,上下线的短暂过程会有很多 Distro 转发异常。为了更好地更缩小变更影响面,有以下措施:按照功能分离,把配置管理与注册中心分离成两个集群。

如图所示,Nacos 配置管理与注册中心已然两个独立的集群,互不干扰,定制版配置管理集群日常迭代不影响注册中心。

总结

本文主要介绍了 Nacos 注册中心/配置管理的数据一致性实现,对比了 1.x 与 2.x 的主要差异,gRPC 长链接全双工交互确实是 2.x 的最大卖点,提升了稳定性和吞吐量。 Nacos 注册中心/配置管理是核心基础设施,变更牵涉面比较广,日常 Nacos 迭代为了更好地更缩小变更影响面,建议按照功能分离,把配置管理与注册中心分离成两个集群。

附录

参考及建议阅读:支持 gRPC 长链接,深度解读 Nacos 2.0 架构设计及新模型Http/2 及 gRPC 的一些文章

gRPC 长连接在微服务业务系统中的实践 Raft 分布式一致性:分布式一致性 Raft 与 JRaftList-Watch 机制于 K8s:K8S之list-watch机制+节点以及亲和性调度,请阅第一段

相关阅读

评论可见,请评论后查看内容,谢谢!!!
 您阅读本篇文章共花了: