RAFT algorithm

    分布式系统中的节点通信存在两种模型:共享内存(Shared memory)和消息传递(Messages passing)。基于消息传递通信模型的分布式系统,不可避免地会发生以下错误:进程可能会慢、垮、重启,消息可能会延迟、丢失、重复。为此,需要保持数据一致。

    通常来说,在分布式环境下,可以通过两种手段达成一致:

    1. Symmetric, leader-less

                  所有Server都是对等的,Client可以和任意Server进行交互

    2. Asymmetric, leader-based

                  任意时刻,有且仅有1台Server拥有决策权,Client仅和该Leader交互

“Designing for understandability”的Raft算法采用后者. 算法的原理其实很简单,《InSearch of an Understandable Consensus Algorithm》这个论文中写的比较详细。推荐大家看:   https://raft.github.io/

  

Consensus

    多个服务器在状态达成一致,但是在一个分布式系统中,因为各种意外可能,有的服务器可能会崩溃或变得不可靠,它就不能和其他服务器达成一致状态。这样就需要一种Consensus协议,一致性协议是为了确保容错性,也就是即使系统中有一两个服务器当机,也不会影响其处理过程。为了以容错方式达成一致,我们不可能要求所有服务器100%都达成一致状态,只要超过半数的大多数服务器达成一致就可以了,假设有N台服务器,N/2 +1 就超过半数,代表大多数了。Paxos和Raft都是为了实现Consensus一致性这个目标,这个过程如同选举一样,参选者需要说服大多数选民(服务器)投票给他,一旦选定后就跟随其操作。

    看本文前,建议先看动画PPT

Raft 算法

Raft中服务器端角色 

在Raft中,任何时候一个服务器可以扮演下面角色之一:

    Leader: 处理所有客户端交互,日志复制等,一般一次只有一个Leader.

    Follower: 类似选民,完全被动

    Candidate候选人: 类似Proposer律师,可以被选为一个新的领导人。

Raft算法

Raft阶段分为两个,首先是选举过程,然后在选举出来的领导人带领进行正常操作,比如日志复制等。

1. 任何一个服务器都可以成为一个候选者Candidate,它向其他服务器Follower发出要求选举自己的请求:


2. 其他服务器同意了,发出OK。


注意如果在这个过程中,有一个Follower当机,没有收到请求选举的要求,因此候选者可以自己选自己,只要达到N/2 + 1 的大多数票,候选人还是可以成为Leader的。

3. 这样这个候选者就成为了Leader领导人,它可以向选民也就是Follower们发出指令,比如进行日志复制。


4. 以后通过心跳进行日志复制的通知


5. 如果一旦这个Leader当机崩溃了,那么Follower中有一个成为候选者,发出邀票选举。


6. Follower同意后,其成为Leader,继续承担日志复制等指导工作:


 

值得注意的是,整个选举过程是有一个时间限制的,如下图:

  Splite Vote是因为如果同时有两个候选人向大家邀票,双方得到的票数是一样的。这时通过加时赛来解决,两个候选者在各自随机一段timeou(比如一个300ms,一个150ms)中,互相不服气的等待,之后首先发出邀票的的候选者得到了大多数同意,成为领导者Leader,而另外一个候选者后来发出邀票时,那些Follower选民已经投票给第一个候选者,不能再投票给它,它就成为落选者了,最后这个落选者也成为普通Follower一员了。

算法的一些细节

关于Term

    在分布式环境中,“时间同步”本身是一个很大的难题,但是为了识别“过期信息”,时间信息又是必不可少的。Raft为了解决这个问题,将时间切分为一个个的Term,可以认为是一种“逻辑时间”。需满足:

      1.每个Term至多存在1个Leader

      2.某些Term由于选举失败,不存在Leader

      3.每个Server本地维护currentTerm

      4.Candidate在等待投票结果的过程中,可能会接收到来自其它Leader的AppendEntries RPC。如果该Leader的Term不小于本地的currentTerm,则认可该Leader身份的合法性,主动降级为Follower;反之,则维持Candidate身份,继续等待投票结果

      5.Candidate既没有选举成功,也没有收到其它Leader的RPC,这种情况一般出现在多个节点同时发起选举(如图Split Vote),最终每个Candidate都将超时。为了减少冲突,这里采取“随机退让”策略,每个Candidate重启选举定时器(随机值),大大降低了冲突概率

    在Leader election,自增currentTerm,由Follower转换为Candidate,设置votedFor为自身,并行发起RequestVote RPC,不断重试,直至满足以下任一条件:

      1.获得超过半数Server的投票,转换为Leader,广播Heartbeat

      2.接收到合法Leader的AppendEntriesRPC,转换为Follower

      3.选举超时,没有Server选举成功,自增currentTerm,重新选举


举例-------日志复制

1.下面以日志复制为例子说明Raft算法,假设Leader领导人已经选出,这时客户端发出增加一个日志的要求,比如日志是"sally":

2. Leader要求Followe遵从他的指令,都将这个新的日志内容追加到他们各自日志中:


3.大多数follower服务器将日志写入磁盘文件后,确认追加成功,发出Commited Ok:


4. 在下一个心跳heartbeat中,Leader会通知所有Follwer更新commited 项目。

 

补充重要的问题

理解算法时候的注意点:

1.在leader出问题之前,可以保证大多数的server其term是相等的

2.在leader出问题后,leader如果还是leader,则其term不会变。剩余的server在选举leader的时候,大多数-1(减去leader的那台)的term是和leader挂的时候一样的,所以新选出的leader如果要获取大多数的vote,则其term必然大于老leader出问题的时候的term,所以之后如果老的leader活过来了那么也没关系,因为其小的term此时已经不能让其成为leader了。

3.某个一leader真正成为leader的时候,其只有唯一的term。因此一个term只可能有一个leader。也就是说如果同一时间存在两个term,那么就可能存在两个leader,但是其中的一个leader永远不可能成功的commit。另外,真正活着的leader是最大的term的那个。

4.当一个新的leader产生后,其会的将每个follower的next index设置为其自己nextindex,然后发送心跳包给各个follower。如果follower发现这两个index不一样,则拒绝。leader如果发现follower拒绝了,则发送其next index的前一个index作为next index,直到follower接受。这样的话要保证两个事情:

a.如果term和index一样,那么内容一样

b.如果term和index一样,那么之前的日志内容一样

对于a,只要leader是唯一的,那么就能保证(因为一个唯一的leader对应唯一的term,同时其自身可以保证index的唯一)。对于b,每次发送AppendEntries的时候提供前一个日志的term和id,follower保证其和当前的term、index一致才接受,通过数学归纳可以知道这是可以保证b的。

在a、b的前提下,如果leader落后太多,那么其可能会清理follower上已经commit的日志。因此还需要额外的条件:在选举leader的时候,一个candidate如果其自己的term、id比另一个candidate发来的请求要新,那么就拒绝。

5.在leader宕了的情况下,集群里至少有大多数-1的主机是拥有所有commit日志的。

6.对于figure 8的问题,要注意的是对于c图,term为4的leader是不会去commit老的term为2的日志的,对于index为2的日志,一种情况是和d一样被overwrite了(此时term 2的日志从来就没有被commit过),另一个情况是和e一样,由S1在Log MatchingProperty的前提下被commit(收到term 4的AppendEntries的时候,发现S1已经提交到了term 4的日志,所以其follower也提交到这一步)。这里的关键在于,commit指的是重做事物日志,当leader广播的日志内容到了大多数服务器的时候,这些服务器(包括leader自己)都可能只是有这个日志,但是没有commit过。老的日志如果要被commit,前提是follower收到AppendEntries的时候,其包含的leader的最新提交term+index大于这个老的日志的term+index,此时才会被commit。

7.follower收到candidate的请求,且candidate的term大于follower的term的时候,此时follower会立刻认新的leader。 

理解集群配置替换时候的注意点:

1.在集群配置变化的时候(主要是添加和减少主机),要避免的是新老两种配置同时生效(如果存在这种情况,按照figure 10可以看到可能存在同一个term中有两个leader的情况),换句话说就是要保证leader挂了的时候不会选出两个leader。为了解决这种问题,raft中采用配置同样依靠leader广播,且增加一种old-new的配置来解决这个问题。具体操作和原理如下(新加入的主机启动的时候对于集群的其他服务器信息一点配置都没有,因此其就是在等):

a.leader收到一个新的集群配置

b.leader广播一个old-new配置信息,这个信息被commit的前提是新集群和老集群中的多数follower都收到了这个信息

c.如果此时leader挂了,且b中的信息还没有被commit,此时集群中只有两种新的配置,一种是Cold,一种是Cold-new。Cold能正常commit或选举的前提是老集群中大多数服务器ok,Cold-new能正常commit或选举的前提是老集群中其大部分服务器ok,且新集群中其大部分服务器ok。如果说Cold-new中的某个服务器要成为leader,根据前提条件其需要老集群中的大部分服务器投票给他,这就保证了老集群中处于Cold的大部分服务器不会成为leader。如果老集群中的一个Cold服务器想成为leader,则Cold-new就没法投票成功(因为其得到不老集群中的多数投票)。当Cold中的一个服务器成为leader后,其再次发起投票即可(这次投票会的将得到上一轮Cold-new的日志overwrite掉)。

如果leader挂了的时候b中的信息已经被commit了,那么新的leader肯定是Cold-new的,这样就比较简单了。

d.当Cold-new已经被commit后,此时的leader就会发起包含新配置的Cnew信息。这个其实就是上面过程的翻转(Cold变成Cnew),成功的前提是老集群中的大部分和新集群的大部分都收到了Cnew消息。

2.总的来说,集群配置替换由于Cold-new的引入,保证了同一时刻Cold和Cnew不可能同时生效(因为Cold-new其实潜在要求了只有Cnew生效,Cold不生效。而c中另一种情况就是Cold,因此Cnew和Cold不能同时生效。),当Cnew和Cold不同时生效的时候,可能会处于中间的Cold-new或Cold或Cnew状态,其中处于Cold或Cold-new的时候,按照规定步骤继续向着Cnew前进即可。

3.在上面第一步的c中,如果Cold成了leader,其需要发起一次Cnew-old的广播,否则可能会由于收到新集群主机的选举信息而变成follower,并再次发起选举

4.Cold-new的比较简单的理解是,Ca-b要求a中的大多数、b中的大多数都投赞成票才算成功

理解快照时候的注意点:

1.快照最简单的理解就是按照figure 12的那样,把commit的当前内容及一些基本信息记录下。

2.在follower新加入集群的时候,由于其什么日志都没有,因此leader获取到期next index是一个已经由于快照而删除了的值。此时leader既要发送快照给这个follower。

 

参考:

http://www.jdon.com/artichect/raft.html

http://blog.csdn.net/cszhouwei/article/details/38374603

http://www.tuicool.com/articles/buem2yA

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值