Nacos 官方 描述了其命名的由来 Nacos: Dynamic Naming and Configuration Service
;按照目前主流的使用场景,Nacos 有两个核心能力:注册中心(Naming)和配置管理(Configuration);而这里我们讨论注册中心和配置管理的数据一致性。
本文将会涉及以下内容:
关键词:
关键词 | 含义 |
---|---|
Distro | Nacos 实现的数据最终一致性协议,用在注册中心模块 |
JRaft | SOFAJRaft,蚂蚁 SOFA 实现 Java 版的 Raft 分布式一致性组件 |
List-Watch | list-watch 机制,watch 实时通知,list 兜底轮询;既兼顾了实时性也达到最终一致 |
gRPC | A 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 总结下来就以下逻辑:
多多想想,其实上面的逻辑就保证了数据的最终一致性。
图(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 主动地通知。
nacos-client-config 1.x 客户端初始异步任务,不断地发起 listen(check-config-md5)
请求到服务端,
get-config
获取最新配置;notify back LP
返回客户端。update-config
会通过 Notify 机制通知其他节点,也发送配置变更事件 notify back LP
让 long-polling 返回。
nacos-client-config 2.x 客户端初始异步任务,长间隔地发起 listen(check-config-md5)
请求到服务端(不再 LP 所以间隔调大到了 5min 保证 list-watch 的 list);
与 1.x 的主要区别是 notify config change
配置变更事件由服务端主动通过 gRPC 请求到客户端。其他的逻辑变化不大。
无疑是将所有的网络交互(客户端-服务端,服务端集群之间)采用 gRPC(Http2) 长链接全双工,至于网络协议的优缺点请阅:gRPC 长连接在微服务业务系统中的实践,本文作者不够专业不敢吹水,唯有讨论下此举在 Nacos 上哪些方面有提升表现。
总体上:(客户端-服务端,服务端集群之间)减少了不断地进行 Http 短链接的建立与断开,TCP Time Wait
数目。
注册中心部分:
配置管理部分:
Nacos 注册中心/配置管理是核心基础设施,变更牵涉面比较广;日常 Nacos 迭代过程中,集群的升级/重启 对依赖其的应用服务影响,主要影响在注册中心模块,因为注册中心会有责任分片,上下线的短暂过程会有很多 Distro 转发异常。为了更好地更缩小变更影响面,有以下措施:按照功能分离,把配置管理与注册中心分离成两个集群。
如图所示,Nacos 配置管理与注册中心已然两个独立的集群,互不干扰,定制版配置管理集群日常迭代不影响注册中心。
本文主要介绍了 Nacos 注册中心/配置管理的数据一致性实现,对比了 1.x 与 2.x 的主要差异,gRPC 长链接全双工交互确实是 2.x 的最大卖点,提升了稳定性和吞吐量。
Nacos 注册中心/配置管理是核心基础设施,变更牵涉面比较广,日常 Nacos 迭代为了更好地更缩小变更影响面,建议按照功能分离,把配置管理与注册中心分离成两个集群。