分布式一致性算法Raft

Raft基础

节点角色
一个Raft集群包含多个节点,通常是5个,可以容忍2个节点失效。在任何时刻,每一个服务器节点都处于三个状态之一:leader、follower、candidate。在正常情况下,集群中只有一个leader并且其他节点全都是follower。

  • follower都是被动的:他们不会发送任何请求,只是简单的响应来自leader和candidate的请求。
  • leader处理所以的客户端请求:如果一个客户端和follower通信,follower会将请求转给leader。
  • candidate是用来选举一个新leader的。

任期(Term)
Raft把时间分隔为任意长度的任期,任期在Raft中充当逻辑时钟的作用,这使得节点可以发现一些过期的信息比如过期的leader。
任期用连续的整数标记,每一段任期从一次选举开始,一个或多个candidate尝试成为leader。如果一个candidate赢得选举,然后它就在该任期剩下的时间里充当leader。在某些情况下,一次选举无法选出leader,在这种情况下,这一任期会以没有leader结束,然后一个新的任期会很快重新开始。Raft保证了在一个任期里最多只有一个leader。
不同的节点观察到的任期转化次数可能不同,在某些情况下,一个节点可能没有看到leader选举过程或者甚至整个任期全程。每个节点存储一个当前任期号,该编号随着时间单调递增。节点之间通信的时候会交换当前任期号,如果一个节点的当前任期号比其他的小,该节点会将自己的任期号更新为较大的那个值。如果一个candidate或者leader发现自己的任期号过期了,它会立即回到follower状态。如果一个节点接收到一个包含过期的任期号的请求,它会直接拒绝这个请求。

节点通信
Raft算法中节点之间使用RPC进行通信,并且基本的一致性算法只需要两种类型的RPC。请求投票RPC由candidate在选举期间发起,追加条目RPC由leader发起,用来复制日志和提供一种心跳机制。

Leader选举

触发leader选举
Raft使用心跳机制来触发leader选举。当系统中的服务器程序启动时,他们都是follower。一个节点只要能从leader或candidate处接收到有效的RPC就会一直保持follower状态。Leader周期性的向所有follower发送心跳(不包含日志条目的AppendEntires RPC)来维持自己的地位。如果一个follower在一段选举超时时间内没有接收到任何消息,它就假设系统中没有可用的leader,然后开始进行选举以选出新的leader。

Leader选举过程
1.要开始一次选举过程,follower先增加自己的当前任期号并且转换到candidate状态,然后投票给自己并且并行的向集群中的其他服务器节点发送RequestVote RPC让其他节点投票给它。Candidate会一直保持当前状态知道发生了以下三种事情之一:(a)它自己赢得了这次的选举;(b)其他的服务器节点成为leader;(3)一段时间后没有任何获胜者。
2.当一个candidate获得集群中过半服务器节点这对同一个任期的投票,它就赢得了这次选举并成为leader。(a)
算法限制
(1)对于同一个任期,每个服务器节点只会投给一个candidate,按照先来先服务(FCFS)的原则。
(2)要求或者过半投票的规则确保了只有一个candidate赢得此次选举(选举安全性)
3.一旦candidate赢得选举,就立即成为leader,然后它会向其他的服务器节点发送心跳消息来确定自己的地位并阻止新的选举。

在等待投票期间,candidate可能会收到另一个声称自己是leader的服务器节点发来的AppendEntries RPC。如果这个leader的任期号(包含在RPC中)不小于candidate当前的任期号,那么candidate会承认该leader的合法地位并回到follower状态。如果RPC中的任期号比自己小,那么candiate就会拒绝这次的RPC并继续保持candidate状态。(b)

第三种可能的结果是candidate即没有赢得选举也没有输:如果有多个follower同时成为candidate,那么选票可能会被瓜分以至于没有candidate赢得过半的投票。当这种情况发生时,每一个候选人都会超时,然后通过增加当前任期号来开始一轮新的选举,然而,如果没有其他机制的话,该情况可能会无限重复。Raft算法使用随机选举超时时间的方法来确保很少发生选票瓜分的情况,就算发生也能很快的解决。为了阻止选票一开始就被瓜分,选举超时时间是从一个固定的区间(如150-300毫秒)随机选择。这样可以把服务器都分散开以至于在大多数情况下只有一个服务器会选举超时,然后该服务器赢得选举(因为它超时后会发送一个更大的任期号进行投票,便会很快获得胜出)并在其他服务器超时前发送心跳。

日志复制

leader一旦被选举出来,就开始为客户端请求提供服务。客户端的每一个请求都包含一条被复制状态机执行的指令。Leader把该指令作为一个新的条目追加到日志中去,然后并行的发起AppendEntries RPC给其他的服务器,让他们复制该条目。当该条目被安全的复制(复制到过半的服务器上),leader会应用该条目到它的状态机中(状态机执行该指令)然后把执行的结果返回给客户端。如果follower奔溃或者运行缓慢,或者网络丢包,Leader会不断重试AppendEntries RPC(即使已经回复了客户端)直到所有的follower最终都存储了所有的日志条目。
Leader决定什么时候把日志条目应用到状态机中是安全的,这种日志条目被称为已提交的。Raft算法保证所有已提交的日志条目都是持久化的并且最终会被所有可用的状态机执行。一旦创建该日志条目的leader将它复制到过半的服务器上,该日志条目就会被提交。
我们设计了Raft日志机制来维持不同服务器之间日志高层次的一致性。这么做不仅简化了系统的行为也使得系统行为更加可预测,同时该机制也是保证安全性的重要组成部分。日志机制特性如下:

  • 如果不同日志中的两个条目拥有相同的索引和任期号,那么他们存储了相同的指令。
  • 如果不同日志中的两个条目拥有相同的索引和任期号,那么他们之间的所以日志条目也都相同。
    日志覆盖
    在Raft算法中,leader通过强制follower复制它的日志来解决不一致的问题。这意味这follower中根leaer冲突的日志条目会被leader的日志条目覆盖。

安全性

到目前为止,描述的机制并不能充分的保证每一个状态机会按照相同的顺序执行相同的指令。例如,一个follower可能会进入不可用状态,在此期间,leader可能提交了若干的日志条目,然后这个follower可能会被选举为leader并且用新的日志条目覆盖这些日志条目,结果不通的状态机可能会执行不同的指令序列。

选举限制
在任何基于leader的一致性算法中,leader最终都必须存储所有已经提交的日志条目。Raft使用一种简单的方法,可以保证新leader在当选时就包含了之前所有任期号中已经提交的日志条目。
Raft使用投票的方式来阻止candidate赢得选举除非该candidate包含了所有已经提交的日志条目。候选人为了赢得选举必须与集群中的过半节点通信,这意味着至少其中一个服务器节点包含了所有已提交的日志条目。如果candidate的日志至少和过半的服务器节点一样新,那么它一定包含了所有已经提交的日志条目(这是正常情况下的情形,如果Leader的某条日志条目没有完成过半节点的复制,那么就可能造成非最新的节点当选新Leader,下一小节会重点讨论)。RequestVote RPC执行了这样的限制:RPC中包含了candidate的日志信息,如果投票者自己的日志比candidate的还新,它会拒绝该投票请求。(Raft通过比较两份日志中最后一条日志条目的索引值和任期号来定义谁的日志比较新:如果两份日志最后条目的任期号不同,那么任期号大的日志更新;如果两份日志最后条目的任期号相同,那么日志较长的那个更新)
提交之前任期内的日志条目
一旦当前任期内的某个日志条目已经存储到过半的服务器节点上,leader就只知道该日志条目已经被提交了。如果某个leader在提交某个日志条目之前崩溃了,以后的leader会试图完成该日志条目的复制。

集群成员变更

配置变更自动化被纳入到了Raft一致性算法中。
为了使配置变更机制能够安全,在转换的过程中不能存在任何时间点使得同一个任期里可能选出两个leader。但是,任何服务器直接从就配置转换到新配置的方案都是不安全的,因为各个机器会在不同的时候进行转换。为了保证安全性,配置变更必须采用一种两阶段方法。**在Raft中,集群线切换到一个过度配置称为联合一致;一旦联合一致被提交了,那么系统就切换到新的配置上。**联合一致结合了老配置和新配置:

  • 一旦日志条目被复制给集群中新、老配置的所有服务器,新、老配置的服务器都可以成为leader
  • 达成一致需要分别在两种配置上获得过半的支持。

变更的具体流程:
(1)集群配置在复制日志中以特殊的日志条目来存储和通信,当一个leader接收到一个改变配置从C-old到C-new的请求,它就为联合一致将该配置(C-old,new)存储为一个日志条目,并以前面描述的方式复制该条目。
(2)如果leader崩溃了,新leader可能是在C-old配置也可能是在C-old,new配置下选出来的,这取决与赢得选举的candidate是否已经接收到了C-old,new配置。
(3)一旦C-old,new被提交了,那么 C-old和C-new都不能在没有得到认可的情况下做出决定,并且Leader完整性特性保证了只有拥有C-old,new日志条目的服务器才能被选举为leader。
(4)现在leader创建一个描述 C-new配置的日志条目并复制到集群吉他节点就是安全的了,此外,新的配置为服务器收到后会立即生效。
(5)当新的配置在 C-new的规则下被提交,旧的配置就变得无关紧要,同时不是使用新配置的服务器就可以被关闭了。

配置变更另外三个问题:
(1)新服务器开始时可能没有存储任何的日志条目,当这些服务器以这种状态加入到集群中,他们需要一段时间来赶上其他服务器,这段时间他们无法提交新的日志条目。为了避免因此而造成的系统短时间不可用,Raft在配置变更前引入一个额外的阶段,在该阶段,新的服务器以没有投票权身份加入到集群中来(leader也复制日志给他们,但是考虑过半的时候不用考虑他们)。一旦该新服务器追赶上了其他机器,配置变更就可以按上面描述的方式进行。
(2)集群的leader可能不是新配置中的一员,在这种情况下,leader一旦提交了C-new日志条目就会退位(回到follower状态)。这意味这有这样的一段时间(leader提交C-new期间),leader管理着一个不包括自己的集群,它复制着日志但不把自己算在过半里面。leader转换发生在C-new被提交的时候,因为这是新配置可以独立运转的最早时刻(将总是能够在C-new配置下选出)。在这之前,可能只能从C-old中选出领导人。
(3)那些被一处的服务器可能会干扰集群。这些服务器将不会收到leader的心跳,所以当选举超时他们就会进行新的选举过程。他们会发送带有新任期号的ReqeustVote RPCs,这样会导致当前的leader回到Follower状态。新的leader最终会被选出来,但是被移除的服务器将会再次超时,然后这个过程会再次重复,导致系统可用性很差。为防止此问题,当服务器认为当前leader存在时,服务器会忽略RequestVote RPCs。特别的,当服务器在最小选举时间内收到一个RequestVote RPCs,它不会更新任期号或者投票。这不会影响正常的选举,因为每个服务器在开始一次选举之前,至少等待最小选举时间。相反,这有利于避免被移除的服务器的扰乱:如果leader能够发送心跳给集群,那它就不会被更大的任期号罢免。

日志压缩

猜你喜欢

转载自blog.csdn.net/weixin_44630798/article/details/88432209