Nacos源码之AP一致性协议实现

CAP介绍

在了解Nacos一致性协议之前先了解一下CAP是什么:

C:一致性

A:可用性

P:分区容错性。针对多节点部署的系统,分区就代表了网络分区,由于网络原因节点之间无法通信进行数据同步。容错指在这种情况下,系统仍然可以对外提供服务。

在分布式系统中,首先需要保证P,然后在C和A之间做权衡:

在满足P的情况下,如果向node1写入一条数据,因为分区产生则数据无法同步给其他节点,此时就需要在C和A直接做出选择。

选择C,则需要保证所有节点数据的一致性,则整个分布式系统对外暂时不可用。

选择A,则会舍弃C,分布性系统仍然可以对外提供服务,但是因为数据没有同步,则通过不同节点查询返回的结果回不一致。

Nacos中的一致性协议

作为一个分布式系统,Nacos 的服务管理和配置管理都支持 AP、CP 协议。相比较而言Zookeeper作为服务发现常用的一种实现方式只支持CP协议。本篇还是以注册中心功能为基础进行介绍。

服务之间感知对方服务可正常提供服务的实例信息,必须从注册中心获取。因此对注册中心的可用性就会有着更高的要求,尽可能保证服务注册功能的可用性。

在Nacos中,注册中心分为非持久化和持久化两种服务:

非持久化对应了AP协议:保障服务的可用性,在一定时间内,各节点数据可以达成一致。

持久化则对应的CP协议:保障了各个节点数据的强一致性。

Nacos中AP协议的具体实现则是Distro 协议,是Nacos自研的一种协议。而CP协议的实现则是Raft协议。具体代码代码实现都在naming.consistency包中。

在Distro 协议的设计下

Nacos 每个节点是平等的都可以处理写请求,同时把新数据同步到其他节点。

每个节点只负责部分数据,定时发送自己负责数据的校验值到其他节点来保持数据⼀致性。

对于读请求每个节点可以独立处理,及时从本地发出响应。

对于写请求,Nacos中会员前置Filter根据请求中包含的 IP 和 port 信息计算其所属的 Distro 责任节点, 并将该请求转发到所属的 Distro 责任节点上。

Distro协议源码分析

在之前我们已经知道了,Nacos注册中心实现的大体流程,最终的数据存储在ServiceManager类中的双层Map中,之后就会通知客户端,推送最新的实例信息。

/**
 * Map(namespace, Map(group::serviceName, Service)).
 */
private final Map<String, Map<String, Service>> serviceMap = new ConcurrentHashMap<>();
复制代码

在往Service模型中添加实例(**ServiceManager.**addInstance)的时候调用了:

consistencyService.put(key, instances);
复制代码

这里的ConsistencyService就是我们需要关注的重点了。首先ConsistencyService接口具有多个实现类,那么在调用上面方法的时候到底是哪个实现类?通过注入的name可以发现其实是DelegateConsistencyServiceImpl:

@Resource(name = "consistencyDelegate")
private ConsistencyService consistencyService;

//实现类
@DependsOn("ProtocolManager")
@Service("consistencyDelegate")
public class DelegateConsistencyServiceImpl  ...

//在DelegateConsistencyServiceImpl中定义了两种实现类
private final PersistentConsistencyServiceDelegateImpl persistentConsistencyService;

private final EphemeralConsistencyService ephemeralConsistencyService;


@Override
public void put(String key, Record value) throws NacosException {
	mapConsistencyService(key).put(key, value);
}

private ConsistencyService mapConsistencyService(String key) {
	return KeyBuilder.matchEphemeralKey(key) ? ephemeralConsistencyService : persistentConsistencyService;
}
复制代码

通过上面的代码可以知道,在非持久化的情况下,真正的实现类是ephemeralConsistencyService。而ephemeralConsistencyService具有一个唯一的实现类就是DistroConsistencyServiceImpl。到此就对应了上面所说的非持久化对应了AP协议,AP协议的具体实现则是Distro 协议。

Distro协议下完整的的put方法如下:

@Override
public void put(String key, Record value) throws NacosException {
	onPut(key, value);
	// If upgrade to 2.0.X, do not sync for v1.
	if (ApplicationUtils.getBean(UpgradeJudgement.class).isUseGrpcFeatures()) {
		return;
	}
	distroProtocol.sync(new DistroKey(key, KeyBuilder.INSTANCE_LIST_KEY_PREFIX), DataOperation.CHANGE,
			DistroConfig.getInstance().getSyncDelayMillis());
}
复制代码

可以看到在onPut之后,调用了distroProtocol.sync方法,这个方法就是用来像其他节点同步当前节点注册实例数据用的,可以保证每个节点都有全量的注册实例信息,具体实现:

/**
 * Start to sync data to all remote server.
 *
 * @param distroKey distro key of sync data
 * @param action    the action of data operation
 * @param delay     delay time for sync
 */
public void sync(DistroKey distroKey, DataOperation action, long delay) {
        //遍历除当前节点之外所有的集群节点
	for (Member each : memberManager.allMembersWithoutSelf()) {
		syncToTarget(distroKey, action, each.getAddress(), delay);
	}
}
复制代码

到这里我们发现执行sync方法的是DistroProtocol,它是个什么东西呢?它其实就是整个Distro协议的入口。其中实现了集群多节点之间同步数据所需要的各种操作:比如初始化时向其他节点加载数据、同步数据到指定节点、获取当前节点的快照数据。

这里简单看一下新加入节点的的初始化操作:

@Component
public class DistroProtocol {

public DistroProtocol(ServerMemberManager memberManager, DistroComponentHolder distroComponentHolder,
		DistroTaskEngineHolder distroTaskEngineHolder) {
	this.memberManager = memberManager;
	this.distroComponentHolder = distroComponentHolder;
	this.distroTaskEngineHolder = distroTaskEngineHolder;
	startDistroTask();
}

   ....

)
复制代码

通过注解可以知道在服务启动之后就会调用startDistroTask,从集群的其他节点同步注册实例数据到当前节点,来保障集群的的每台机器上都维护了当前的所有注册上来的非持久化实例数 据:

private void load() throws Exception {
	while (memberManager.allMembersWithoutSelf().isEmpty()) {
		Loggers.DISTRO.info("[DISTRO-INIT] waiting server list init...");
		TimeUnit.SECONDS.sleep(1);
	}
	while (distroComponentHolder.getDataStorageTypes().isEmpty()) {
		Loggers.DISTRO.info("[DISTRO-INIT] waiting distro data storage register...");
		TimeUnit.SECONDS.sleep(1);
	}
	for (String each : distroComponentHolder.getDataStorageTypes()) {
		if (!loadCompletedMap.containsKey(each) || !loadCompletedMap.get(each)) {
			loadCompletedMap.put(each, loadAllDataSnapshotFromRemote(each));
		}
	}
}

private boolean loadAllDataSnapshotFromRemote(String resourceType) {
	DistroTransportAgent transportAgent = distroComponentHolder.findTransportAgent(resourceType);
	DistroDataProcessor dataProcessor = distroComponentHolder.findDataProcessor(resourceType);
	if (null == transportAgent || null == dataProcessor) {
		Loggers.DISTRO.warn("[DISTRO-INIT] Can't find component for type {}, transportAgent: {}, dataProcessor: {}",
				resourceType, transportAgent, dataProcessor);
		return false;
	}
	for (Member each : memberManager.allMembersWithoutSelf()) {
		long startTime = System.currentTimeMillis();
		try {
			Loggers.DISTRO.info("[DISTRO-INIT] load snapshot {} from {}", resourceType, each.getAddress());
			DistroData distroData = transportAgent.getDatumSnapshot(each.getAddress());
			Loggers.DISTRO.info("[DISTRO-INIT] it took {} ms to load snapshot {} from {} and snapshot size is {}.",
					System.currentTimeMillis() - startTime, resourceType, each.getAddress(),
					getDistroDataLength(distroData));
			boolean result = dataProcessor.processSnapshot(distroData);
			Loggers.DISTRO
					.info("[DISTRO-INIT] load snapshot {} from {} result: {}", resourceType, each.getAddress(),
							result);
			if (result) {
				distroComponentHolder.findDataStorage(resourceType).finishInitial();
				return true;
			}
		} catch (Exception e) {
			Loggers.DISTRO.error("[DISTRO-INIT] load snapshot {} from {} failed.", resourceType, each.getAddress(), e);
		}
	}
	return false;
}
复制代码

可以看到这里会遍历集群其他节点进行数据的同步,具体操作是轮询所有的 Distro 节点,通过向其他的机器发送请求拉取全量数据。

DistroProtocol中还有很多其他同步数据相关的方法实现,大家可以自己结合某个点来去查看阅读。

总结

到此我们已经基本了解了Naocs中临时节点所使用的一致性协议。作为注册中心功能来说需要保证其可用性,一般会选用AP协议,也就是Distro。在Distro 协议的设计思想下,每个 Distro 节点都可以接收到读写请求,并且会全量或者定期的进行数据同步,保障集群中所有节点数据的最终一致性。

关于相关的代码,在源码中还有更多实现,大家可以自行去阅读。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
摘要 本规范定义无线AP控制和配置(Control And Provisioning of Wireless Access Points, CAPWAP)协议,满足在RFC4564中由CAPWAP Working Group定义的目标。CAPWAP协议在设计上颇具灵活性,这使其能够用于各种无线技术。本文档介绍基础CAPWAP协议,而独立的绑定扩展使其可与其他无线技术一起使用。 目录 第1章 序言 1-1 目标 1-2 本文档中的约定 1-3 特约作者 1-4 术语 第2章 协议综述 2-1 无线绑定定义 2-2 CAPWAP会话建立综述 2-3 CAPWAP状态机定义 2-3-1 CAPWAP协议状态转换 2-3-2 CAPWAP/DTLS接口 2-4 在CAPWAP协议中使用DTLS 2-4-1 DTLS握手处理流程 2-4-2 DTLS会话建立 2-4-3 DTLS错误处理 2-4-4 DTLS端点认证和授权 第3章 CAPWAP传送 3-1 UDP传送 3-2 UDP-Lite传送 3-3 AC发现 3-4 分段/重组 3-5 MTU发现 第4章 CAPWAP分组格式 4-1 CAPWAP前导 4-2 CAPWAP DTLS首部 4-3 CAPWAP首部 4-4 CAPWAP数据消息 4-4-1 CAPWAP数据通道保持激活 4-4-2 数据净荷 4-4-3 建立DTLS数据通道 4-5 CAPWAP控制消息 4-5-1 控制消息格式 4-5-2 服务质量 4-5-3 重传 4-6 CAPWAP协议消息要素 4-6-1 AC描述符 4-6-2 AC IPv4列表 4-6-3 AC IPv6列表 4-6-4 AC名称 4-6-5 带优先权的AC名称 4-6-6 AC时间戳 4-6-7 添加MAC ACL条目 4-6-8 添加站 4-6-9 CAPWAP控制IPv4地址 4-6-10 CAPWAP控制IPv6地址 4-6-11 CAPWAP本地IPv4地址 4-6-12 CAPWAP本地IPv6地址 4-6-13 CAPWAP计时器 4-6-14 CAPWAP传输协议 4-6-15 数据传输数据 4-6-16 数据传输模式 4-6-17 解密错误报告 4-6-18 解密错误报告周期 4-6-19 删除MAC ACL条目 4-6-20 删除站 4-6-21 发现类型 4-6-22 重复的IPv4地址 4-6-23 重复的IPv6地址 4-6-24 空闲超时 4-6-25 ECN支持 4-6-26 映像数据 4-6-27 映像标识符 4-6-28 映像信息 4-6-29 启动下载 4-6-30 位置数据 4-6-31 最大消息长度 4-6-32 MTU发现填充 4-6-33 无线电设备管理状态 4-6-34 无线电设备运行状态 4-6-35 结果代码 4-6-36 返回的消息要素 4-6-37 会话ID 4-6-38 统计量计时器 4-6-39 特定供应商净荷 4-6-40 WTP 主板(Board)数据 4-6-41 WTP描述符 4-6-42 WTP回退 4-6-43 WTP帧隧道模式 4-6-44 WTP MAC类型 4-6-45 WTP名称 4-6-46 WTP无线电设备统计量 4-6-47 WTP重启统计量 4-6-48 WTP静态IP地址信息 4-7 CAPWAP协议计时器 4-7-1 ChangeStatePendingTimer 4-7-2 DataChannelKeepAlive 4-7-3 DataChannelDeadInterval 4-7-4 DataCheckTimer 4-7-5 DiscoveryInterval 4-7-6 DTLSSessionDelete 4-7-7 EchoInterval 4-7-8 IdleTimeout 4-7-9 ImageDataStartTimer 4-7-10 MaxDiscoveryInterval 4-7-11 ReportInterval 4-7-12 RetransmitInterval 4-7-13 SilentInterval 4-7-14 StatisticsTimer 4-7-15 WaitDTLS 4-7-16 WaitJoin 4-8 CAPWAP协议变量 4-8-1 AdminState 4-8-2 DiscoveryCount 4-8-3 FailedDTLSAuthFailCount 4-8-4 FailedDTLSSessionCount 4-8-5 MaxDiscoveries 4-8-6 MaxFailedDTLSSessionRetry 4-8-7 MaxRetransmit 4-8-8 RetransmitCount 4-8-9 WTPFallBack 4-9 WTP保存的变量 4-9-1 AdminRebootCount 4-9-2 FrameEncapType 4-9-3 LastRebootReason 4-9-4 MacType 4-9-5 PreferredACs 4-9-6 RebootCount 4-9-7 Static IP Address 4-9-8 WTPLinkFailureCount 4-9-9 WTPLocation 4-9-1 . WTPName 第5章 CAPWAP发现操作 5-1 发现请求消息 5-2 发现响应消息 5-3 主发现请求消息 5-4 主发现响应消息 第6章 CAPWAP加入操作 6-1 加入请求 6-2 加入响应 第7章 控制通道管理 7-1 回显请求 7-2 回显响应 第8章 WTP配置管理 8-1 配置一致性 8-1-1 配置灵活性 8-2 配置状态请求 8-3 配置状态响应 8-4 配置更新请求 8-5 配置更新响应 8-6 改变状态事件请求 8-7 改变状态事件响应 8-8 清除配置请求 8-9 清除配置响应 第9章 设备管理操作 9-1 固件管理 9-1-1 映像数据请求 9-1-2 映像数据响应 9-2 复位请求 9-3 复位响应 9-4 WTP事件请求 9-5 WTP事件响应 9-6 数据传送 9-6-1 数据传送请求 9-6-2 数据传送响应 第10章 站点会话管理 10-1 站点配置请求 10-2 站点配置响应 第11章 NAT考虑 第12章 安全考虑 12-1 CAPWAP安全 12-1-1 转换受保护数据为不受保护数据 12-1-2 转换不受保护数据为受保护数据(插入) 12-1-3 删除受保护记录 12-1-4 插入不受保护记录 12-1-5 应用MD5 12-1-6 CAPWAP分段 12-2 会话ID安全 12-3 发现或DTLS设置攻击 12-4 伴随DTLS会话的干扰 12-5 CAPWAP预配置 12-6 在CAPWAP中使用预共享密钥 12-7 在CAPWAP中使用证书 12-8 在CN字段中使用MAC地址 12-9 AAA安全 12-10 WTP固件 第13章 运行考虑 第14章 传输考虑 第15章 IANA考虑 15-1 IPv4多播地址 15-2 IPv6多播地址 15-3 UDP端口 15-4 CAPWAP消息类型 15-5 CAPWAP首部标记 15-6 CAPWAP控制消息标记 15-7 CAPWAP消息要素类型 15-8 CAPWAP无线绑定标识符 15-9 AC安全类型 15-10 AC DTLS策略 15-11 AC信息类型 15-12 CAPWAP传输协议类型 15-13 数据传送类型 15-14 数据传送模式 15-15 发现类型 15-16 ECN支持 15-17 无线电设备管理状态 15-18 无线电设备运行状态 15-19 无线电设备故障原因 15-20 结果代码 15-21 返回的消息要素原因 15-22 WTP主板数据类型 15-23 WTP描述符类型 15-24 WTP回退模式 15-25 WTP帧隧道模式 15-26 WTP MAC类型 15-27 WTP无线电设备统计量故障类型 15-28 WTP重启统计量故障类型 第16章 致谢 第17章 参考文献 17-1 标准类参考文献 17-2 信息类参考文献 编辑通讯录

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值