基础版raft算法

原文也发布在了 https://zoro09.gitbook.io/ideas/raft
嘻嘻嘻,欢迎关注。

强烈建议大家先移步 http://thesecretlivesofdata.com/raft/#home ,花5分钟左右时间简单了解一下raft。
本篇文章主要谈一谈我对raft的一些理解,如有异议,欢迎各路大佬指出~
(下面讨论的是论文版Raft,各种改进版在此不做讨论)

1 Raft都干了什么?

1.0 raft解决了什么问题

相信大家在接触新事物之前都会有此类疑问,一堆为什么要问。不过【raft解决了什么问题】这个问题可以去问问谷老师或者某度哈哈哈。

1.1 概述

raft基于paxos,是一种用来管理复制日志(replicated log)的共识算法,所以也可以理解为,raft的各种设计都是为了让集群中的server在log上达成共识。
raft的作者认为paxos有两个“缺点”:1)paxos非常难理解。2)paxos工业上不好实现。如果非要说难理解是缺点的话倒也算是吧,毕竟大道至简。
一个raft集群有多台server(废话,一台server也不需要共识),给定某个时间点,每个server的状态能且只能为下面三种状态之一:

  • leader
    leader诞生制度为选举制 (个人感觉raft准确来说是毛遂自荐+拉票)
    一个集群有且仅有一个leader 所有人都听它的
  • follower
    集群中机器初始化状态为follower
    每个follower都有个选举超时时间(election timeout),可先简单理解为follower在这个时间内没收到leader发来的心跳,则认为集群当前无leader,可以开始竞选leader了
  • candidate
    每个想要成为leader的follower,都会先从follower变为candidate状态
    candidate先给自己投一票,如果能够获得集群中大多数投票则成功成为leader
    raft中时间被分为多个term

集群中一个server的状态机如上图。简单起见,后面说的leader大多都是指【状态为leader的server】,follower/candidate同理。
正常情况下,集群只会有一个leader,多个follower/candidate。leader负责处理所有client的请求,而follower/candidate只和leader通信。

一个集群中能否有两个leader?是否可行?两个leader相比一个leader又有什么好处/坏处?

历史的车轮滚滚向前,朝代更替,永不停息。raft的世界亦如是,每个leader的诞生都伴随着一次或多次竞选(election),每一次竞选都会有一个或者多个follower变为candidate参加竞选,而每一次竞选的开始都意味着一个term的开始,每个term对应一个term number(此term number只增不减),因此,term number可称之为raft集群中的逻辑时钟(logical clock)。(为什么要专门搞个term number呢?用时间值不是更准确?万一有人有类似疑问,可以学习 分布式时钟 相关知识)
正如前面说的,一个term开始于一次竞选,而term以何种方式结束取决于竞选结果。如果竞选中没有一个candidate获得大多数的投票,则认为大家都竞选失败,此次term结束,开始下一轮竞选,如上图中t3。如果有candidate成功成为leader,则在该leader不再是leader之前称之为一个term,如上图term1 term2 term4…

集群中server之间通过RPC通信,基本的共识算法需要两种类型的RPC:
1)RequestVote RPC. 该类型RPC由candidate在竞选的过程中生成。
2)AppendEntries RPC. 该类型RPC由leader生成,主要用于日志同步和心跳。

为什么要这两种分开呢?一种RPC可以吗?

接下来从日志复制、领导者选举、成员变更三个方面进行介绍。

1.2 领导者选举 Leader Election

raft选主为多数原则,即超过半数的server赞成,则选主流程结束,此过程保证只有一个server赢得竞选。为了保证只有一个candidate竞选成功,在集群中节点投票时,每个节点按照first-come-first-served的原则,只能投出一票。
集群会发生选举的情况大致分为以下几类:

  • 集群为初始态,此时所有server均为follower,需要选主。
  • leader无法发出心跳,需要选主。
  • follower未收到leader发出的心跳,需要选主。

每个follower都有一个选举超时时间(election timeout),在一个follwer选举超时时间内如果没有收到来自leader/candidate的有效RPC,则该follower认为此时集群没有leader,状态变为candidate,发起一场竞选。
在candidate(记为x)发起竞选时,分别进行下面四步操作:

  1. 自增term
  2. 给自己投票
  3. 重置自己的election timeout
  4. 向集群中其他server发出RequestVote RPC

此时,candidate x的竞选结果可能有以下几种:

  • x竞选成功,成为leader
  • x竞选失败,其他candidate为leader
  • 没有任何candidate竞选成功

下面对candidate x的上述三种结果分别作出解释。

  • 第一种情况,x竞选成功,成为leader。当x获得了集群中大多数节点的投票之后,x成为leader,并开始向集群中其他server发出AppendEntries RPC,保持自己的leader地位。
  • 第二种情况,x竞选失败,其他candidate成为leader。在x等待集群其他server的投票结果时,收到了来自leader y的AppendEntries RPC,此时,
    • 如果y.term>=x.term,证明当前集群无需选主,leader y即为集群leader,x从candidate变为follower。
    • 如果y.term<x.term,则x拒绝该RPC,继续保持candidate状态。
  • 第三种情况,没有任何server竞选成功。原因可能为:
    • 如果 candidate y 和 candidate x 的 election timeout 差不多,而x和y各收到了集群少部分server的投票,没有任何一方满足多数原则,因此无人成功,坐等下一轮竞选。
    • 如果 candidate x 有机会获得 大多数 server 的投票,但因为资格不够被其他server拒绝 (后面会讲这种case)

举例如下,如下图,假设集群初始化时,所有server端配置为图中给定值。
集群初始化server状态

显然,S2的election timeout最小,因此,S2会成为candidate,重置自己的election timeout,并将term number +1,投自己一票后向其他server发出RequestVote RPC,如下图:
S2超时发起竞选

上图中S1 S3 S4 S5的tern number都比S2小,S2大概率会得到大多数server的同意票,成为leader;

上面我们说过,raft是一个为了让集群中的server在log上达成共识的算法,那么,server在给一个candidate投票时,是如何判断投同意还是拒绝呢?除了判断
term number 的大小之外,是否需要满足其他的条件?

1.3 日志复制

在开始讲述日志复制之前,暂时假设集群当前已选好leader。
leader负责响应client发出的command的请求,同时,leader会将这些command写入log,并负责把log复制到各个follower,复制log到follower的请求称之为AppendEntries RPC,当leader认为该条log已被成功复制后,给client返回response,大致流程如下图。
client发起x=1 command
如果leader收到集群中大多数机器【写入log成功】的response,则该条log被认为是committed,此时leader可以返回client相应的response。Raft保证已经committed的log肯定已持久化且集群中每个机器最终都会成功写入该log。如上图,leader(S1)返回给client response时,集群中大多数机器(S1 S2 S3)都已经成功写入x=1,S4和S5可能因为机器已经crash/运行太慢没及时响应或者网络中数据包丢失等其他原因没有写入x=1的log,此时,leader会一直发送AppendEntries RPC, 直到log写入成功为止。
上面描述了集群写入log的大致流程,下面对每一步进行详细说明raft如何保证集群中每个server都成功写入该log。
分布式系统中,由于机器crash/网络等原因会引起各种各样的问题。raft通过制定一系列约束来保证日志复制的准确性,履行它【已经committed的log肯定已持久化且集群中每个机器最终都会成功写入该log】的承诺。
logs

  1. log由多个entry组成,每条 log entry 记录 log index、term number、command
    如上图,一个小方框代表一个entry,方框内第一行数字代表term number,第二行数字代表command;log index线性增长;
  2. leader 的 log 只会 append
    一旦server被选中作为leader,则所有的follower日志和leader保持完全一致,leader不会覆写/删除任何log entry。
  3. leader发出复制日志的AppendEntries RPC时,会带上上一条log的term number和log index
    为保证所有server log的顺序一致性
  4. follower中接收到新的 log entry 写入请求后,会比对当前最新log和AppendEntries RPC请求中的上一条log的term number和log index是否一致
    同第3条,为保证所有server log的顺序一致性

正如上述第2条所述,集群所有follower会以leader的日志为参考,与其保持完全一致,而raft又承诺已经committed的log肯定已持久化且集群中每个机器最终都会成功写入该log。如此一来,leader的选举显得尤为重要。

已经记录了log index,为啥还要记录term number呢?

1.4 选主还需哪些约束?

集群初始化时,选举leader相对比较简单,每台server都没有log,无论哪台server被选为leader都可以。然而实际上,分布式系统中,机器crash并不罕见,网络的延迟等因素同样不可忽视,集群运行一段时间后,重新选举leader的情况更为常见。
⚠️ 发起选举情况可能原因有两种:leader宕机,未发出RPC;leader没宕机,网络原因follower未在election timeout时间内收到S1发出的RPC。总之,发起选举的follower认为当前集群处于无leader状态,需要重新选主。
1.1小节讲述了大致的选举流程,更多侧重集群初始化选主,并没有提及发起选举的candidate 机器log的状态对于选举结果会有何影响。依然用下图举例,由上到下我们对server一次编号S1-S5。
集群当前各server log
由1.2小节可知,log成功被follower复制的条件之一就是上一条log复制成功,因此上图中S2 S4 S5状态是可能会存在的。下面假设一下几种可能情况,分别分析。

  • S2 election timeout
    log index 为6和7的已经成功写入大多数机器,状态为committed。假设S2可以成功被选举为leader,集群中follower需要和leader日志保持一致,因此如下图所示,原本已提交的log index=6,7的日志会被覆写为(term=4, x<-1),原本已提交的log丢失,raft无法履行【已经committed的log肯定已持久化且集群中每个机器最终都会成功写入该log】的承诺。
    所以,S2不能够称为leader。为了让S2不能成为leader,需要每个server在投票时对比本机的log index和发起投票的candidate的log index。如果发起投票的candidate log index落后于本机,则反对。有了上述约束,
    • S1(如未宕机) S3 S5反对票
    • S2 S4同意票
      S2 election timeout

S2未能获得集群中大多数server投票,竞选失败。

  • S3 election timeout
    毋庸置疑,S3会获得所有server的同意票(若S1未宕机 S1也是同意票),顺利成为集群leader,成为leader之后,log index为8的entry仍会被一直重试写入其他server,直到成功。
  • S4 election timeout (情况同S2)
  • S5 election timeout
    log index为8的entry,在S5发起选举时还未成功写入集群大多数机器,这种情况raft不做出承诺仍会持久化,S5得票情况如下
    • S1(若未宕机) S3 的反对票
    • S2 S4和S5 的同意票
      S5 election timeout
      所以,S5会被成功选为新leader,如果client发出x<-1的请求,集群中所有server中log index=8的entry会被写为S5中的值。

因此,要成为leader,需要具备以下条件:
term number不小于集群中半数以上server的log index
log index不小于集群中半数以上server的log index

1.5 成员变更

略略略,有空再填坑叭…

2 raft有什么不足吗?

对于log的写入顺序控制是否过于严格?
写入log流程是否可以优化?
还有哪些性能优化点?
上面的问题先埋个坑,下次再说叭…(如果有下次

3 参考链接

http://thesecretlivesofdata.com/raft/#home
https://raft.github.io/

raft论文中详细介绍了每种RPC携带的信息,每种状态的机器记录了哪些信息。希望大家有空都看看,本文如有不对之处,欢迎各位大佬指出。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值