一起学Consul(3)——Raft算法

现在市面上的很多分布式系统所用的分布式一致性算法基本上都是采用的Raft算法,比如Redis集群、Redis哨兵系统、Consul集群等。还有其他分布式一致性算法,比如Paxos、ZAB(zookeeper atomic Broadcast)等。

首先,什么是分布式一致性?是指在分布式系统中,各个节点的状态如何达到一致的问题。如果数据只存储一份,那么天然是一致的,因为一份数据永远只有一个状态。为了解决单点的性能瓶颈和故障不能转移等问题,引入的集群的架构,同时也带来了一致性的问题,即多个节点在某些时刻的状态不一致的问题。再往下就要提到CAP理论和BASE理论,就不展开了。

由于Paxos比较难以理解,所以出现了Raft。下面我们主要介绍Raft。

一言一概之,Raft是通过选举出一个领袖,负责将变更同步给其他跟随者,从而达到一致的系统。

Raft系统,含有多个节点,每个节点有三种状态:

跟随者状态(Follower state):完全被动,不主动发送请求。

候选人状态(Candidate state):成为领袖之前的状态。

领袖状态(Leader state):管理所有客户端请求,并同步给跟随者。

下文,我将领袖节点也称为主节点,跟随节点称为子节点。

一、领袖选举(leader election)

开始的时候所有节点都是跟随者,因为这时还没选出领袖。

在Raft中,有两个超时时间设置来控制选举,其中一个是选举超时(election timeout),是指一个跟随者在没有收到主节点的消息(心跳)多久之后才能成为候选人。这个值是介于150ms~300ms的随机数,随机数的目的是尽量防止多个节点同时变成候选人,增加选举的成功率。每一次选举周期用election term表示,是一个从0开始的整数,每一次选举会加一。

如果跟随者在election timeout时间之内,没有收到主节点的心跳信息,就会变成候选人,发起一轮选举,先将自己的election term +1,投给自己一票,并行地向其他节点发送请求投票的RPC(Request Vote RPC),选自己为领袖。

每一轮election term,每个节点只有一次投票权,只能投个一个节点,原则是先到先得,投完之后会立即重置election timeout,这也是为了减少候选人竞争,重置后就需要再等一段时间才有机会成为候选人。如果某个候选者收到的赞成票数超过总节点数的一半,那么此节点就会成为领袖节点。

领袖会发送Append Rntries消息给它的跟随者,这些消息每隔一段时间就会发送一次,这个间隔时间就叫做心跳超时(heartbeat timeout)。每个跟随者会响应这些消息,告诉领袖我还活着。当某个跟随者没有收到心跳请求的时间超过election timeout,成为候选人时,这一次选举周期(election term)就算是结束了。这个过程叫做领袖选举(leader election)。

选举的安全性:如果两个节点同时成为候选人,比如节点总数是4个,两个节点通知达到election timeout,这两个节点各自拿到自己给自己的一票,另外还有两个节点没有投票,如果两个候选人一人拿到一票,那么它们的总票数都是2票,2票没有超过总节点4的一半,所以投票失败,进行下一轮投票,直到选出leader为止。每个节点每轮只能投一次票和超过半数选票者当选,这两条规则保证了最多只有一个节点当选leader,另外也解释了总节点是奇数更容易选出leader。

二、日志复制(Log Replication)

选举出领袖后,所有的写操作都会经由领袖完成,也由领袖来将所有变更复制到其他节点。现在有个客户端发来一个变更请求,领袖会将这条变更(log entry)追加到日志中,但是状态是未提交状态,这时领袖会在发送下次心跳检测时将这个变更发送到所有跟随者,在超过半数的跟随者已经完成变更(追加log entry)之后,领袖才会把这条变更的状态变成已提交。并把执行结果返回给客户端。在下一次心跳的时候,领袖通知各个跟随者将这次变更的状态更新为已提交,到这时集群达到一致状态。如果跟随者崩溃或者运行缓慢,再或者网络丢包,领导人会不断的重复尝试附加日志条目 RPCs (尽管已经回复了客户端)直到所有的跟随者都最终存储了所有的日志条目。

日志(log entry)由有序序号标记的条目组成。每个条目都包含领袖创建这条日志时的任期号(election term num)。日志中的任期号用来检查是否出现不一致的情况。每一条日志条目同时也都有一个整数索引值来表明它在日志中的位置。请求投票的RPC 实现了这样的限制: RPC 中包含了候选人的日志信息,然后投票人会拒绝掉那些日志没有自己新的投票请求,这是为了保证在更换leader的时候,不会丢失日志。

复制过程中,系统是不一致的,但是最终能达到一致状态,这在分布式系统中被称为达到最终一致性。上面整个过程叫做日志复制(Log Replication)。

三、RPCs与Timeout

Raft 算法中服务器节点之间通信使用远程过程调用(RPCs),并且基本的一致性算法只需要两种类型的 RPCs。请求投票(RequestVote) RPCs 由候选人在选举期间发起,然后附加条目(AppendEntries)RPCs 由领导人发起,用来复制日志和提供一种心跳机制。为了在服务器之间传输快照增加了第三种 RPC。当服务器没有及时的收到 RPC 的响应时,会进行重试, 并且他们能够并行的发起 RPCs 来获得最佳的性能。

RPC有三种:

RequestVote RPC:候选人在选举期间发起

AppendEntries RPC:领导人发起的一种心跳机制,复制日志也在该命令中完成

InstallSnapshot RPC: 领导者使用该RPC来发送快照给太落后的追随者。

超时设置:

Heartbeat Timeout: 领导者的心跳超时时间

Election Timeout: 追随者设置的候选超时时间

MTBT :指的是单个服务器发生故障的间隔时间的平均数

BroadcastTime << ElectionTimeout << MTBF

两个原则:

BroadcastTime应该比ElectionTimeout小一个数量级,为的是使领导人能够持续发送心跳信息(heartbeat)来阻止追随者们开始选举;

ElectionTimeout也要比MTBF小几个数量级,为的是使得系统稳定运行。

一般BroadcastTime大约为0.5毫秒到20毫秒,ElectionTimeout一般在10ms到500ms之间。大多数服务器的MTBF都在几个月甚至更长。

四、与Client交互

Client只向领导者发送请求;

Client开始会向追随者发送请求,追随者拒绝Client的请求,并重定向到领导者;

Client请求失败,会超时重新发送请求;

Raft算法要求Client的请求线性化。有两个解决方案:

Raft算法提出要求每个请求有个唯一标识;

Raft的请求保持幂等性;

五、网络分区

Raft能够在网络分区时(俗称脑裂)保持一致性。如果有个集群有5个节点。由于网络问题,被分成两个子集群(子集群A有2个节点,子集群B有3个节点),如果原领袖在A中,B集群可以选出了自己的新领袖。此时如果有两个客户端分别对两个子集群进行操作,但是其中节点数较少的A集群的更新操作只能复制到一个节点,加上主节点也没有超过半数,所以无法完成commit操作。而另一个较大的集群则可以将更新操作复制给大多数,能够commit。现在网络恢复了,election term 小的领袖会自动下台,A集群中所有节点会回滚它们的未提交的记录,并且同步新领袖的日志记录。现在集群重新达到一致性状态。

引用资料:

raft论文https://raft.github.io/raft.pdf

https://raft.github.io

有个疑问,希望有知道的同学可以留言解答:某个子节点B由于与主节点A断开连接,但是与其他子节点连接正常,这个子节点会不会替换掉原来的主节点?原来的主节点A也只与节点B断连,与其他节点是正常的,原来的主节点A会不会重新夺回leader宝座?这样会不会存在A与B,轮流当选的情况?如下图所示,红色的连接断开。

喜欢请关注,更多原创
一起学Consul(3)——Raft算法_第1张图片

你可能感兴趣的:(架构相关)