Raft算法论文翻译

论文原文
raft算法的基础部分,基于自己的理解翻译了比较主要的部分。

5.1 Raft 基础

一个Raft集群通常有5个结点,可以容许两个结点失效。任一时刻某个结点只能有leader、follower和candidate三种角色中的一种。正常情况下只有一个leader,其他的都是follower。followers是被动的,他们不能主动发出请求,但是可以响应leader和candidates的请求。leader处理所有的client请求(如果一个client联系一个follower,这个follower把这个请求转发给leader)。cadidate角色是用来选主的。

Raft把时间分成随机长度的term,term是连续的整数编号。每一个term都从选主动作开始,如果一个candidate赢得了选主,他就成为集群的leader,有时候选主会得到一个分裂的结果,这时这个term结束于无主状态,紧跟着会再次进入下一个term进行选主,Raft保证某一时刻只有一个leader。

不同的结点在不同时点观察状态过渡期的不同term,term的作用就像一个逻辑时钟,它使得结点可以探测到过期的信息比如一个旧的leader。每个结点存储一个当前的term号,这个term号随着时间单调增长。节点之间在通信的时候交换各自存储的term号;如果一个结点当前的term号比其他结点的小,它就把自己的term号更新为更大的那个值。如果candidate或者leader发现自己的term废弃了(比其他结点的term小),它立刻返回follower状态。如果一个结点收到一个过期的term号的请求,它就拒绝这个请求。

Raft结点使用rpc通信,并且只需要两类rpc。RequestVoteRPCs(请求投票rpc)由candidates结点在选主期间发起,AppendEntriesRPCs由leader发起用于复制日志项和提供心跳。如果没有及时收到rpc的响应,节点会重发,它们并行发布rpc以获得最高的性能。

5.2 选主

Raft使用一个心跳机制来触发选主。结点启动以后进入follower状态。结点只要能收到一个leader或者candidate的有效rpc就会保持在follower状态。leader周期性发送心跳(不带日志项的AppendEntriesRPCs)给followers来维持leader的权威。如果一个follower超过一个选主时长(election timeout)没有收到任何通信,它就假定没有生存着的leader了并且开始选主来选择新的leader。

为了开始一次选主,一个follower把它当前的term号加1并且变为candidate状态。然后它投自己一票并且向集群的其他结点并发发布RequestVote RPCs。一个candidate保持它的候选状态直到发生以下三件事中的一件:

  1. 它赢得选主
  2. 其他的结点确立了leader的地位
  3. 一段时间没有选举胜出者
    下面分别讨论这三种结果

如果一个candidate获得了集群中大多数结点的相同term的投票它就胜出了。基于先到先处理原则,每个结点在一个term只能给一个candidate投票。大多数原则保证了一个term最多有一个candidate能赢得选主。一旦一个candidate赢得了选主它就称为leader。然后它就开始向集群其他结点发送心跳来建立权威并阻止新的选举。

等待投票的时间里,一个candidate可能会收到另一个声称是leader的结点发出的AppendEntries RPC。如果那个leader的term(包含在它发的rpc中)比这个candidate当前的大,那么这个candidate就认同这个leader的合法性并且回到follower状态。如果它收到的rpc里面的term比自己的小,那么这个candidate拒绝这个rpc并维持在候选状态。

第三种可能的结果是这个candidate既没有赢得也没有输掉选举:如果同时有多个followers变为candidates,投票会被分裂这样没有candidate能获得大多数投票。发生这种情况时,每个dandidate会超时并且发起一次新的选主:把自己的term加1并发布一轮新的RequestVote RPCs。然而,如果没有额外的措施,分裂投票会无限重复下去。

Raft使用随机选主时限来保证分裂投票很少发生,即便发生也会被很快解决。首先,选主时限在150-300ms间随机选择。所有节点都是这样的,这么一来大多数情况只有一个结点会首先到达这个时限,它会在其他结点的选主时限到达前赢得选举并发送心跳。相同的机制也用来处理分裂投票。每个candidate在选主开始的时候都重新计算出一个随机选主时限,过了这个时限它就发起下一轮选主;这样减少了下次选主再出现分裂投票的可能性。

5.3 日志副本

leader被选出来以后就开始服务client的请求。每一个客户端请求包含一条要被副本状态机执行的命令。leader把命令作为一条新的日志项追加到它的日志里,然后并发发布AppendEntries RPCs给其他的结点来复制这一条日志项。当这个日志项被安全地复制以后,leader将这个日志项的内容应用到它的状态机并把结果返回给client。如果某些followers崩溃了或者处理慢了,又或者网络丢包了,leader就无限期重发AppendEntries RPCs(即使在它回复了client以后)直到全部的followers最终存储了所有的日志项。

每条日志项存储一条状态机命令和从leader收到这条日志项的term号。日志项里的term号用来侦测发现日志间的不一致性和保证一些特性。每个日志项有一个整数索引来索引它在日志中的位置。

leader决定什么时间可以安全地让状态机执行一条日志项;然后这条日志项就称为commited。Raft保证commited的日志项是持久化的并最终将会被所有的可用的状态机执行。当leader将它创建的一条日志项复制到大多数结点以后,这条日志项就是commited的了。这也commit了leader日志中所有正在处理的日志项,包括被之前的leader创建的日志项。leader持续跟踪它要被commite的最高的index号,并把这个index号包在未来要发送的AppendEntries RPCs里面,这样其他结点最终都会得到这个号。一旦一个follower知道了一条日志是commited的了,它就让本地状态机应用这个日志项(按照日志的顺序)。

Raft维护以下特性来一起组成日志匹配属性:

  1. 如果不同日志的两个日志项有相同的index和term,那么它们存储着相同的命令
  2. 如果不同日志的两个日志项有相同的index和term,那么日志中前面所有的日志项都是相同的。

为了让f的日志和l的保持一致,l要找到两个人都一样的最后一条日志,删除f日志中这一条之后的所有日志,把自己从这一条之后的所有日志都发给f。这些操作是响应由AppendEntries
RPCs执行的一致性检查。l为每一个f维护一个“下一条索引” ,就是这个l要发给f的下一条日志的索引。当一个l掌权以后,它初始化所有的下一条索引的值为它自己日志的下一条的索引。如果f和l的日志不一致,下一次 AppendEntries RPC的AppendEntries一致性检查就会失败。被拒绝以后,l将下一条索引减1再重发 AppendEntries RPC。最终下一条索引会到达l和f的日志匹配的点。此时AppendEntries就成功了,f日志中所有的冲突日志项都被删除,如果l的日志有日志项就会再追加到f中。一致性达成。

l永远不覆写或者删除自己日志中的条目(“leader 只追加”属性)

发布了66 篇原创文章 · 获赞 21 · 访问量 8万+

猜你喜欢

转载自blog.csdn.net/qq_35753140/article/details/86537352
今日推荐