1 Nacos ?致性协议
1.1 为什么 Nacos 需要?致性协议
Nacos尽可能减少用户部署以及运维成本,做到用户只需要?个程序包,就快速单机模式启动 Nacos 或集群模式启动 Nacos。而 Nacos 是?个需要存储数据的组件,为实现目标,就要在 Nacos 内部实现数据存储。单机问题不大,内嵌关系型数据库即可;但集群模式就要考虑保障各节点间的数据?致性及数据同步,就得引入共识算法,通过算法保障各节点间的数据?致性。
1.2 为什么 Nacos 选择了 Raft 以及 Distro
Nacos 在单个集群中同时运行 CP 协议及 AP 协议?要从 Nacos 场景出发:Nacos 集服务注册发现及配置管理于?体,集群下,各节点间的数据?致性保障问题,需拆分成两方面:
① 从服务注册发现来看
服务之间感知对方服务的当前可正常提供服务的实例信息,须从服务发现注册中心获取,因此对服务注册发现中心组件的可用性提出高要求,需在任何场景尽最大可能保证服务注册发现能力可以对外提供服务;同时 Nacos 的服务注册发现设计,采取了心跳可自动完成服务数据补偿的机制。如果数据丢失的话,是可以通过该机制快速弥补数据丢失。
为满足服务发现注册中心的可用性,强?致性共识算法不太合适,因为强?致性共识算法能否对外提供服务有要求,如当前集群可用节点数没过半,整个算法直接“罢工”,而最终?致共识算法更多保障服务可用性,并能保证在?定时间内各节点之间数据能达成?致。上述都是针对 Nacos 服务发现注册中的非持久化服务(即需客户端上报心跳进行服务实例续约)。
而对 Nacos 服务发现注册中的持久化服务,因为所有数据都是直接调用 Nacos服务端直接创建,因此需由 Nacos 保障数据在各节点间的强?致性,因此针对此类型的服务数据,选择强?致性共识算法来保障数据?致性。
② 配置管理
配置数据直接在 Nacos 服务端进行创建并管理,须保证大部分的节点都保存此配置数据,才能认为配置被成功保存,否则就会丢失配置的变更,问题很严重,如发布重要配置变更出现丢失变更,多半引起严重现网故障,因此对配置数据的管理,须集群中大部分节点强?致,这里只能使用强?致性共识算法。
③ 为啥是 Raft 和 Distro
强?致性共识算法,当前最多使用 Raft 协议,易让人理解,并有很多成熟工业算法实现,如蚂蚁金服 JRaft、Zookeeper ZAB、Consul Raft、百度 braft、Apache Ratis;因为 Nacos 是 Java 技术栈,因此只能在 JRaft、ZAB、ApacheRatis 中选择,但ZAB 和 Zookeeper 强绑定,再加上希望可以和 Raft 算法库支持团队随时沟通交流,因此选择 JRaft,也因为 JRaft 支持多 RaftGroup,为 Nacos 后面的多数据分片带来可能。
而 Distro 协议是阿里巴巴自研的?个最终?致性协议,最终?致性协议很多如 Gossip、Eureka 内的数据同步算法。Distro 算法集 Gossip 及 Eureka 协议优点并优化而出,原生 Gossip由于随机选取发送消息的节点,不可避免存在消息重复发给同?节点情况,增加网络传输的压力,也给消息节点带来额外的处理负载,而 Distro 算法引入权威 Server 概念,每个节点负责?部分数据及将自己的数据同步给其他节点,有效降低消息冗余问题。
2 早期的 Nacos ?致性协议
2.1 早期 Naocs 架构
特点
服务注册和配置管理?致性协议 分开,没下沉到 Nacos内核模块作为通用能力
- 服务发现模块?致性协议的实现和服务注册发现模块的逻辑强耦合
- 充斥服务注册发现的概念
缺点
- Nacos 服务注册发现模块逻辑复杂难维护,耦合了?致性协议层的数据状态
- 计算存储彻底难以分离
- 对计算层的无限水平扩容能力也有影响
因此必然要对 Nacos?致性协议做抽象及下沉,使其成为 Core 模块能力,彻底让服务注册发现模块只充当计算能力,同时为配置模块去外部数据库存储打下架构基础。
3 当前 Nacos 的?致性协议层
当前 Nacos 内核中,已将?致性协议的能力完全下沉到内核模块作为 Nacos 核心能力,很好的服务于服务注册发现模块及配置管理模块。
3.1 当前 Nacos 的架构
特点
新 Nacos 架构将?致性协议从原先服务注册发现模块下沉到内核模块,并尽可能提供统?抽象接口,使上层的服务注册发现模块及配置管理模块,不再耦合任何?致性语义。
优点
解耦抽象分层后,每个模块能快速演进,并且性能和可用性都大幅提升。
4 Nacos 如何做到?致性协议下沉
既然 Nacos 将 AP、CP 协议下沉到内核模块:
且尽可能保持了?样的使用体验。那这?致性协议下沉如何做到的?
?致性协议抽象
其实,?致性协议,就是用来保证数据?致的,而数据的产生,必然有?个写入的动作;同时还要能够读数据,并且保证读数据的动作以及得到的数据结果,并且能够得到?致性协议的保障。因此,?致性协议最最基础的两个方法,就是写动作和读动作
public interface ConsistencyProtocol<T extends Config, P extends RequestProcessor> extends CommandOperations {
/**
* Obtain data according to the request.
*/
Response getData(ReadRequest request) throws Exception;
/**
* 同步数据提交,在 Datum 中已携带相应的数据操作信息
*/
Response write(WriteRequest request) throws Exception;
}
任何使用?致性协议的,只需使用 getData 及 write 方法。
?致性协议抽象在 consistency 包,Nacos 对 AP、CP ?致性协议接口使用抽象都在,且实现具体?致性协议时,采用插件可插拔,进?步将?致性协议具体实现逻辑和服务注册发现、配置管理两个模块解耦。
package com.alibaba.nacos.core.distributed;
/**
* Conformance protocol management, responsible for managing the lifecycle of conformance protocols in Nacos.
*/
@SuppressWarnings("all")
@Component(value = "ProtocolManager")
public class ProtocolManager extends MemberChangeListener implements DisposableBean {
private void initAPProtocol() {
ApplicationUtils.getBeanIfExist(APProtocol.class, protocol -> {
Class configType = ClassUtils.resolveGenericType(protocol.getClass());
Config config = (Config) ApplicationUtils.getBean(configType);
injectMemb