CP模式下集群支持单写多读,即只有集群中的leader节点才能处理更新服务实例(即注册或删除)的请求,而所有的机器都可以处理查询请求。当集群中的非leader节点接收到更新服务实例的请求时,会转发给leader节点进行处理。leader节点更新完本地的服务实例数据之后,会把数据同步到集群中的其它节点。在这个过程当中,集群中的所有节点都是能够提供查询请求的,在数据没有同步完全之前,向不同的服务器发送查询请求,可能会得到不同的服务实例列表。因此,Nacos的CP模式并不严格。另外,在leader节点宕机导致重新选主时,集群不能处理更新服务实例的请求,这是CP模式的特点,即舍弃了A,尽力保证CP(如前所述,Nacos的CP模式不严格)。
一、数据模型
Nacos中的服务注册数据被设计为五层结构,包括Namespace、Group、Service、Cluster、Instance。
一)隔离层
把Namespace、Group理解为隔离数据,例如,设定Namespace的取值为dev(开发环境)、test(测试环境),用于隔离不同环境;Group的取值设定为payment(支付)和nonpayment(非支付),用于对服务进行分组隔离。在服务实例启动的时候,从配置文件读取固定的Namespace、Group取值,达到不同环境,不同分组之间服务被隔离,不能相互发现并调用的目的。
二)核心层
把Service、Cluster、Instance理解为核心数据,Service是某个具体的服务,Cluster是能够提供该服务的集群,Instance是Cluster下的服务器实例。
二、数据存储
Nacos服务器在两个地方存储核心数据,这里称之为服务器数据存储和一致性数据存储。
其中,服务器数据存储用于响应API请求,一致性数据存储是Nacos实例注册数据实质存放的地方。
Nacos服务器在处理更新服务实例的请求时,会优先更新一致性数据存储的核心数据,然后再通过对一致性数据存储的监听机制,把核心数据同步到本机的服务器数据存储,同时通知订阅相关服务的客户端服务注册数据的变化。
Nacos服务端在更新本机的一致性数据存储之后,会通过API把核心数据同步到集群中的其它节点(CP模式使用接口Post /nacos/v1/ns/raft/datum/commit),其它节点通过对本身一致性数据存储的监听机制,更新本机的服务器数据存储,同时通知订阅相关服务的客户端服务注册数据的变化,达到最终一致。
一)服务器数据存储
存放于类ServiceManager的内部对象Map<String, Map<String, Service>> serviceMap,这是一个两层Map的嵌套结构,第一层的Key是Namesapce名称,第二层的Key是Group名称和Service名称的组合,第二层的Value是Service的对象。Service内部对象Map<String, Cluster> clusterMap,记录了提供该Service的集群;Cluster内部包含对象Set<Instance> persistentInstances,为该集群下CP模式的服务实例。
二)一致性数据存储
存放于类RaftConsistencyServiceImpl的内部对象RaftCore raftCore;RaftCore 中包含对象ConcurrentMap<String, Datum> datums,该Map的Key是Namcesapce、Group、Service的组合,Datum包含服务实例列表,即该Map存放一个Service的所有实例。
三、线程模型
一)Tomcat工作线程
处理客户端请求,完成API调用
二)服务变更通知控制线程
当一致性数据存储中的数据发生变化(例如新增或删除实例)时,把数据变化同步到服务器数据存储,并启动服务变更通知执行线程
三)服务变更通知执行线程
把Service下Cluster或Instance的增减数据,循环发送给各个订阅者
四) 服务实例健康检测线程
Nacos的CP模式中,服务器对每个Cluster启动一个健康检查线程,主动对集群中的每个Instance发送健康检查请求,根据相应结果记录实例的健康状态,记录实例的健康状态,通知给订阅方(注意这里和AP模式不同,不会摘除服务实例),并把健康检查的结果放入HealthCheckCommon类的对象LinkedBlockingDeque<HealthCheckResult> healthCheckResults中
五) 健康检测结果通知线程
从healthCheckResults中拉取检查结果,通过API接口通知给集群中的其他节点
四、典型场景
一)服务实例注册(POST /nacos/v1/ns/instance)
1. 启动对于Cluster的服务实例健康检测线程(Tomcat工作线程)
2. 如本机不是leader节点,通过Post /nacos/v1/ns/raft/datum把请求发到leader节点,完成返回(Tomcat工作线程)
3. 如果本机是leader节点,则把实例数据写入一致性数据存储,启动服务变更通知控制线程(Tomcat工作线程)
4. 调用集群中其它服务器的Post /nacos/v1/ns/raft/datum/commit接口,同步实例数据(Tomcat工作线程)
5. 同步实例数据到服务器数据存储,并启动服务变更通知执行线程(服务变更通知控制线程)
6. 通过UDP数据包,循环把服务provider变更数据通知各个consumer(服务变更通知执行线程)
二)服务实例摘除(DELETE /nacos/v1/ns/instance)
1. 如本机不是leader节点,通过Post /nacos/v1/ns/raft/datum把请求发到leader节点,完成返回(Tomcat工作线程)
2. 如果本机是leader节点,则从一致性数据存储中删除实例,启动服务变更通知控制线程(Tomcat工作线程)
3. 调用集群中其它服务器的Post /nacos/v1/ns/raft/datum/commit接口,同步实例数据(Tomcat工作线程)
4. 同步实例数据到服务器数据存储,并启动服务变更通知执行线程(服务变更通知控制线程)
5. 通过UDP数据包,循环把服务provider变更数据通知各个consumer(服务变更通知执行线程)
三)CP模式实例变更转发请求处理(POST /nacos/v1/ns/raft/datum)
1. 在一致性数据存储中增加或删除实例(取决于转发过来的是注册还是摘除请求,数据会在转发方准备好,按照转发过来的实例列表更新即可),启动服务变更通知控制线程(Tomcat工作线程)
2. 调用集群中其它服务器的Post /nacos/v1/ns/raft/datum/commit接口,同步实例数据(Tomcat工作线程)
3. 同步实例数据到服务器数据存储,并启动服务变更通知执行线程(服务变更通知控制线程)
4. 通过UDP数据包,循环把服务provider变更数据通知各个consumer(服务变更通知执行线程)
四)CP模式实例变更同步请求处理(POST /nacos/v1/ns/raft/datum/commit)
1. 在一致性数据存储中增加或删除实例(取决于转发过来的是注册还是摘除请求,数据会在转发方准备好,按照转发过来的实例列表更新即可),启动服务变更通知控制线程(Tomcat工作线程)
2. 同步实例数据到服务器数据存储,并启动服务变更通知执行线程(服务变更通知控制线程)
3. 通过UDP数据包,循环把服务provider变更数据通知各个consumer(服务变更通知执行线程)
五、版本
本文基于Nacos的1.2.0版本