把所有的服务都放在一个网络分区,但是当网络出现故障时,整个系统都无法对外提供服务,那这还有什么意义呢?
分布式系统实现网络分区 必须实现分区容错性,剩下的就是 是选择一致性 还是可用性 也就是CP还是AP
为什么不能同时实现一致性和可用性?
网络分区AB网络通信失败的话
如果保持可用性 AB两个节点的数据无法同时更改数据,造成数据不一致
如果保持数据强一致性 AB网络不通,所以必须两个节点都不能更改数据,导致对外数据无法更改,节点不可用
优化
如果保证节点可用的情况下,先修改A的数据,网络恢复后更改B节点,达到数据最终一致性---->BASE理论
基本可用: 系统出现故障,但是还是能够对外提供服务,不能直接不可用
软状态: 允许各个节点数据不一致
最终一致性: 瞬时允许节点数据不一致,但是一定时间之后,各个节点数据一致
Base 是三个短语的简写,即基本可用(Basically Available)、软状态(Soft State)和最终一致性(Eventually Consistent)。
Base 理论的核心思想是最终一致性,即使无法做到强一致性(Strong Consistency),但每个应用都可以根据自身的业务特点,采用适当的方式来使系统达到最终一致性(Eventual Consistency)。
在系统设计中,最终一致性实现的时间取决于网络延时、系统负载、不同的存储选型、不同数据复制方案设计等因素。
Base 理论是在 CAP 上发展的,CAP 理论描述了分布式系统中数据一致性、可用性、分区容错性之间的制约关系,当你选择了其中的两个时,就不得不对剩下的一个做一定程度的牺牲。
Base 理论则是对 CAP 理论的实际应用,也就是在分区和副本存在的前提下,通过一定的系统设计方案,放弃强一致性,实现基本可用,这是大部分分布式系统的选择,比如 NoSQL 系统、微服务架构。在这个前提下,如何把基本可用做到最好,就是分布式工程师们追求的,在这个课程中,我们也会有专门的模块来讲解高可用。
除了 CAP 和 Base,上面还提到了 ACID 原理,ACID 是一种强一致性模型,强调原子性、一致性、隔离性和持久性,主要用于在数据库实现中。Base 理论面向的是高可用、可扩展的分布式系统,ACID 适合传统金融等业务,在实际场景中,不同业务对数据的一致性要求不一样,ACID 和 Base 理论往往会结合使用。
Nacos中目前是同时支持CP和AP的,具体看功能实现,不同的功能支持不同的理论
例如:
对于AP来说,Nacos使用的是阿里自研的Distro协议
在这个协议中,每个服务端节点是一个平等的状态,每个服务端节点正常情况下数据是一样的,每个服务端节点都可以接收来自客户端的读写请求
当某个节点刚启动时,他会向集群中的某个节点发送请求,拉取所有的服务实例数据到自己的服务注册表中
这样其它客户端就可以从这个服务节点中获取到服务实例数据了
当某个服务端节点接收到注册临时服务实例的请求,不仅仅会将这个服务实例存到自身的服务注册表,同时也会向其它所有服务节点发送请求,将这个服务数据同步到其它所有节点
所以此时从任意一个节点都是可以获取到所有的服务实例数据的。
即使数据同步的过程发生异常,服务实例也成功注册到一个Nacos服务中,对外部而言,整个Nacos集群是可用的,也就达到了AP的效果
同时为了满足BASE理论,Nacos也有下面两种机制保证最终节点间数据最终是一致的:
失败重试机制是指当数据同步给其它节点失败时,会每隔3s重试一次,直到成功
定时对比机制就是指,每个Nacos服务节点会定时向所有的其它服务节点发送一些认证的请求
这个请求会告诉每个服务节点自己负责的服务实例的对应的版本号,这个版本号随着服务实例的变动就会变动。
如果其它服务节点的数据的版本号跟自己的对不上,那就说明其它服务节点的数据不是最新的。此时这个Nacos服务节点就会将自己负责的服务实例数据发给不是最新数据的节点,这样就保证了每个节点的数据是一样的了。
Nacos的CP实现是基于Raft算法来实现的
在1.x版本早期,Nacos是自己手动实现Raft算法
在2.x版本,Nacos移除了手动实现Raft算法,转而拥抱基于蚂蚁开源的JRaft框架
在Raft算法,每个节点主要有三个状态
当有写请求时,如果请求的节点不是Leader节点时,会将请求转给Leader节点,由Leader节点处理写请求
比如,有个客户端连到的上图中的Nacos服务2节点,之后向Nacos服务2注册服务
Nacos服务2接收到请求之后,会判断自己是不是Leader节点,发现自己不是
此时Nacos服务2就会向Leader节点发送请求,Leader节点接收到请求之后,会处理服务注册的过程
为什么说Raft是保证CP的呢?
主要是因为Raft在处理写的时候有一个判断过程
不过这里还有一个小细节需要注意
Nacos在处理查询服务实例的请求直接时,并不会将请求转发给Leader节点处理,而是直接查当前Nacos服务实例的注册表
这其实就会引发一个问题
如果客户端查询的Follower节点没有及时处理Leader同步过来的写请求(过半响应的节点中不包括这个节点),此时在这个Follower其实是查不到最新的数据的,这就会导致数据的不一致
所以说,虽然Raft协议规定要求从Leader节点查最新的数据,但是Nacos至少在读服务实例数据时并没有遵守这个协议
当然对于其它的一些数据的读写请求有的还是遵守了这个协议。
JRaft对于读请求其实是做了很多优化的,其实从Follower节点通过一定的机制也是能够保证读到最新的数据