CAP 与 Raft 相关知识
一 . CAP
1.1 CAP的概念
CAP定理指的是在一个分布式系统中,一致性Consistency、可用性Availability、分区容错性Partition tolerance,三者不可兼得。
- 一致性(C):分布式系统中多个主机之间是否能够保持数据一致的特性。即,当系统数据发生更新操作后,各个主机中的数据仍然处于一致的状态。
- 可用性(A):系统提供的服务必须一直处于可用的状态,即对于用户的每一个请求,系统总是可以在有限的时间内对用户做出响应。
- 分区容错性(P):分布式系统在遇到任何网络分区故障时,仍能够保证对外提供满足一致性和可用性的服务。
CAP定理的内容是:对于分布式系统,网络环境相对是不可控的,出现网络分区是不可避免的,因此系统必须具备分区容错性。但系统不可能同时保证一致性与可用性。即要么CP,要么AP。
1.2 Base 理论
BASE 是 Basically Available(基本可用)、Soft state(软状态)和 Eventually consistent(最终一致性)三个短语的简写。Base是对CAP中一致性和可用性权衡的结果,其来源于对大规模互联网系统分布式实践的结论,是基于CAP定理逐步演化而来的。
BASE理论的核心思想是:即使无法做到强一致性,但每个系统都可以根据自身的业务特定,采用适当的方式来使系统达到最终一致性。
(1)基本可用
基本可用是指分布式系统在出现不可预知故障的时候,允许损失部分可用性。
(2)软状态
软状态,是指允许系统数据存在的中间状态,并认为该中间状态的存在不会影响系统的整体可用性,即允许系统主机间进行数据同步的过程存在一定延时。软状态,其实就是一种灰度状态,过渡状态。
(3)最终一致性
最终一致性强调的是系统中所有的数据副本,在经过一段时间的同步后,最总能够达到一个一致的状态。因此,最终一致性的本质是需要系统保证最终数据能够达到一致,而不需要保证系统数据的实时一致性。
1.3 一致性描述
(1)实时一致性:要求数据内容一旦发生更新,客户端立刻可以访问到最小的数据。所以,在集群环境下该特性是无法实现的,只存在于单机环境 中。
(2)最终一致性:数据内容发生更新后,经过一小段时间后,客户端可以访问到最新的数据。
实时一致性与最终一致性两个概念是从客户端访问到一致性内容的时间角度来说的,但从客户端访问到的内容角度来说(不说时间问题),还有下面两个概念(强/弱一致性)
(3)强一致性:也称为严格一致性。要求客户端访问到的一定是更新过的新数据。
(4)弱一致性:允许客户端从集群不同节点访问到的数据是不一致的。
1.4 生产案例
下面将生产中常见的一些中间件与服务器集群的CAP特性进行分析。
1. Zookeeper 与 CAP
Zookeeper 遵循的是CP模式,即保证了一致性,但牺牲了可用性。
当Leader节点中的数据发生了变化后,在follower还没有同步完成之前,整个Zookeeper集群是不对外提供服务的。如果此时有客户端来访问数据,则客户端会因访问超时而发生重试。不过,由于Leader的选举非常快,所以,这种重试对于用户来说几乎是感知不到的。所以说,Zookeeper保证了一致性,但牺牲了可用性。
2. Consul 与 CAP
Consul 遵循的是CP模式,即保证了一致性,但牺牲了可用性。
3. Redis 与 CAP
Redis 遵循的是AP模式,即保证了可用性,但牺牲了一致性。
4. Eureka 与 CAP
Eureka 遵循的是AP模式,即保证了可用性,但牺牲了一致性。
5.Nacos 与 CAP
Nocos 在做注册中心时,默认是AP的。但其也支持CP模式,但需要用户提交请求进行转换。
二. Raft 算法
2.1 Raft 算法概念
Raft 算法是一种通过对日志复制管理来达到集群节点一致性的算法。这个日志复制管理发生在集群节点中的leader 与 follower 之间。 raft通过选举出的leader节点负责管理日志复制过程,以实现各个节点之间数据的一致性。
2.2 角色、任期及角色转变
在raft中,节点有三种角色
Leader:唯一负责处理客户端写请求的节点;也可以处理客户端的读请求;同时负责日志复制工作。
Candidate:Leader选举的候选人,其可能会称为Leader。是一个选举中的过程角色。
Follower:可以处理客户端的读请求,负责同步来自于Leader的日志;当接收到其它Candidate的投票请求后可以进行投票;当发现Leader挂了,其会转变为Candidate发起Leader选举。
2.3 Leader 选举
通过raft算法首先要实现集群中leader的选举。
(1)我要选举
若follower在心跳超时范围内没有收到来自leader的心跳,则认为leader挂了。此时其首先会使其本地term增1。然后follower会完成以下步骤:
- 此时若收到了其它candidate的投票要求,则会将选票投给这个candidate。
- 如果没有收到其它candidate的投票要求,则由follower转变为candidate。
- 若之前尚未投票,则先投自己一票。
- 向其它节点发出投票请求,然后等待响应。
注意,每个follower 或者 candidate 只有一票。
(2)我要投票
follower在接收到投票请求后,会根据以下情况来判断是否投票:
- 发来投票请求的candidate的term不能小于我的term
- 在我当前term内,我的选票还没投出去
- 若就收到多个candidate的请求,我将采取first-come-first-served方式投票。
(3)等到响应
当一个candidate发出投票请求后,等待其它节点的响应结果。这个响应结果可能有三种情况:
- 收到过半选票,成为新的leader。然后会将消息广播给所有其它节点,以告诉大家我是新的leader了。
- 接收到别的candidate发来的新leader通知,比较了新leader的term并不比自己的term下,则自己转变为follower。
- 经过一段时间后,没有收到过半选票,也没有收到新leader通知,则重新发出选举。
(4)选举时机
在很大时候,当leader真的挂了,follower几乎同时会感知到,所以它们几乎同时会变为candidate发起新的选举。此时就可能会出现较多candidate票数相同的情况,即无效选举出leader。
为了防止这种情况的发生,raft算法其采用了randomized election timeouts 策略来解决这个问题。其会为这些follower随机分配一个选举发起时间 election timeout,这个timeout 在150-300ms范围内。只有到达了election timeout 时间的follower才能转变为candidate,否则等待。那么election timeout 较小的follower则会转变为candidate,然后先发起选举,一般情况下其会优先获取到过半选票成为新的leader。
2.4 数据同步
在leader选举出来的情况下,leader通过日志复制管理实现集群中各节点数据的同步。
(1)状态机
raft 算法一致性的实现,是基于日志复制状态机的。状态机的最大特征是,不同server中的状态机若当前状态相同,然后接收了相同的输入,则一定会得到相同的输出。
(2)处理流程
当leader 接收到client的写操作请求后,大体会经历以下流程:
leader 在接收到client的写操作请求后,就会将数据与自己的term封装为一个box,请随着下一次心跳发送给所有followers,以征求大家对该box的意见。同时在本地将数据封装为日志。
follower在接收到来自leader的box后,首先会比较该box的term与本地的term,只要不比自己的term下,就接收该box,并向leader回复同意。同时会将该box中的数据封装为日志。
当leader接收到过半同意响应后,会将日志commit到自己的状态机,状态机会输出一个结果,同时日志状态变为committed。
同时leader 还会通知所有follower将日志commit到他们呢本地的状态机,日志状态变为了committed。
在commit通知发出的同时,leader也会向client发出成功处理的响应。
(3)AP支持
Log由 term index、log index 及command 构成。为了保证可用性,各个节点中的日志可以不完全相同,但leader会不断给follower发送box,以使各个节点的log最终达到相同。即raft算法不是强一致的,而是最终一致的。
2.5 脑裂
raft 集群存在脑裂问题。在多机房部署中,由于网络连接问题,很容易形成多个分区。而多分区的形成,很容易产生脑裂,从而导致数据不一致。
下面以三机房部署为例进行分析,根据机房断网情况,可以分为五种情况;
(1)情况一 :不确定
经过选举,若新leader产生于B机房,则B与C机房可以正常对外提供服务,此时A机房也存在leader,但不能处理写操作。故此,出现了脑裂。
若新leader产生于C机房,则A、B、C机房均可正常对外提供服务,不存在脑裂。
(2)情况二:形成脑裂
无论新leader出现的B还是C机房,它们都可对外提供服务。但此时的A机房也存在leader,故此出现了脑裂。A机房只能提供读操作请求服务。
(3)情况三:无脑裂
A与C机房可以正常对外提供服务,但B机房由于会发起新的leader选举而全部变为candidate状态,此状态时无法对外提供任何服务的。不存在脑裂。
(4)情况四:无脑裂
该场景对A、B、C机房的服务没有影响。不存在脑裂。
(5)无脑裂
B、C机房不能提供任何服务,因为它们的主机都处于candidate状态。A 机房只能处理读操作请求。不存在脑裂。
2.6 Leader 宕机处理
(1)请求到达前,leader挂了
client 发送写操作请求到达leader之前,leader就挂了,因为请求还没有到达集群,所以这个请求对于集群来说就没有存在过,对集群数据的一致性没有任何影响。leader 挂了之后,会选举产生新的leader。
由于stale leader【失效的leader;老的leader;原leader】 并未向client 发送成功处理响应,所以client会重新发送该写操作请求。
(2)未开始向follower发送数据前,leader挂了
client 发送写操作请求给leader,请求到达leader后,leader还没有开始向followers发送数据,leader就挂了。这时集群会选举产生新的leader。stale leader 重启后会作为follower重新加入集群,并同步新leader中的数据以保证数据一致性。之前接收到client的数据被丢弃。
由于stale leader 并未向client 发送成功处理响应,所以client会重新发送该写操作请求。
(3)发送了部分后,leader挂了
client 发送写操作请求给leader,leader接收完数据后,向所有的followers发送数据。在部分follower接收到数据后,leader挂了。由于leader挂了,就会发起新的leader请求。
若leader产生于已完成数据接收的follower,其会继续将前面接收到的写操作请求转换为日志,并写入到本地状态机,并向所有follower发出询问。在获取过半同意响应后,会向所有followers发出commit指令。同时向client进行相应。
若leader产生于尚未完成数据接收的follower,那么原来已完成数据接收的follower则会放弃曾经接收到的数据。由于client没有接收到相应,所以client会重新发送该写操作请求。
(4)commit 通知发出后,leader挂了
client 发送写操作请求给leader,leader也成功向所有followers 发出commit指令,并向client 发出相应后,leader挂了。
由于stale leader 已经向client发送成功接收响应,且commit 通知已经发出,说明这个写操作请求已经被server成功处理。
2.7 Raft 算法动画演示
此网址通过动画演示了Raft算法的工作原理,网址为:http://thesecretlivesofdata.com/raft/
这个网址打开可能有点慢,要有耐心。
学习参阅特别声明
【Redis视频从入门到高级】
【https://www.bilibili.com/video/BV1U24y1y7jF?p=11&vd_source=0e347fbc6c2b049143afaa5a15abfc1c】