Raft 算法之领导人选举

Raft 算法之领导人选举

Raft 论文地址:https://ramcloud.atlassian.ne... 算法

Raft论文中分为三块:安全

  • 领导选举
  • 日志复制
  • 安全性

本文中主要介绍领导人选举服务器

Raft中的节点状态

Raft中的节点有三种状态:网络

  • 领导人状态:Leader
  • 跟随者状态:Follower
  • 候选人状态:Candidate

每个节点都是一个状态机,Raft会根据当前的心跳,任期等状态来进行状态的迁移转化,就以下图所示:并发

raft-role.png

首先,在Raft节点启动的时候,全部任务都是Follower状态, 由于此时没有Leader,全部Follower都在固定的超时时间内都收不到来自Leader的心跳,从而变成了Candidate状态,开始选举Leaderspa

当节点处于Candidate状态的时候,会并发的向全部的节点发出请求投票请求RequestVote(后面章节会向详细介绍),在Candidate状态下,节点可能会发生三种状态的迁移变化:.net

  • 开始下一轮新的选举:发出的投票请求在固定时间内没有收到其余节点的响应,或者是收到响应(或者赞成投票)的节点数量没有达到 N/2+1,那么选取超时,进入下一轮选举
  • 选举成功,成为新的Leader:若是选举过程当中收到大于N/2+1数量的节点的投票,那么选举成功,当前的节点成为新的Leader
  • 成为Follower:若是选举过程当中收到来及其余节点的Leader心跳,或者是请求投票响应的Term大于当前的节点Term,那么说明有新任期的Leader

若是节点选举成功,成为了Leader,那么Leader将会在固定周期内发送心跳到全部的节点,可是若是心跳请求收到的响应的Term大于当前节点的Term,那么当前节点的就会成为Follower。好比Leader节点的网络不稳定,掉线了一段时间,网络恢复的时候,确定有其余节点被选为了新的Leader,可是当前节点在掉线的时候并无发现其余节点选为Leader,仍然发送心跳给其余节点,其余节点就会把当前的新的Term响应给已通过时的Leader,从而转变成Follower日志

领导人选举

整个集群必需要在丢包,乱序,延时等诸多不稳定因素的状况下,可以选举出惟一一个Leadercode

请求投票RPC

就如上文中提到的,若是Follower在必定时间内没有收到心跳请求,那么将会切换到Candidate状态,开始一轮新的选取,选举过程当中会向集群中的全部节点发送请求投票的RPCblog

RPC请求参数:

  • term:当前候选人的任期号
  • candidateId:候选人的Id
  • lastLogIndex:候选人的最后日志条目索引值
  • lastLogTerm:候选人的最后日志条目的任期号

其中lastLogIndexlastLogTerm用来判断候选人的日志是否和服务器的日志同样新(后文中会解释),必需要至少同样新才能投票。

RPC响应值:

  • term:被请求节点的任期号
  • voteGranted:是否赞成投票给候选人

Candidate发送请求投票RPC

Candidate什么时候发送请求投票RPC?

若是Leader发生异常,那么基本上全部的Follower在同一时间切换为Candidate,并同时发出请求投票的RPC,那么就有可能致使选票被均衡的瓜分,从而须要从新发起新一轮的投票。为了不选票被瓜分的问题,选举超时的是能够能够从一个固定的区间(例如150-300毫秒)随机选择。

Candidate如何发送投票RPC?
  1. 自增当前节点的任期号
  2. 给本身投票
  3. 重置选举超时计时器
  4. 发送请求投票的RPC给其余服务器
节点收到请求投票的RPC该如何处理?
  1. 判断当前的Term的和请求投票参数中的Term

    • 若是当前的Term > 请求投票参数中的Term,那么拒绝投票(设置voteGrantedfalse),并返回当前的Term
    • 不然就更新当前Term为请求投票参数中的Term, 并将自身状态切换成Follower
  2. 检测当前节点的投票状态:

    • 若是当前的节点没有给任何其余节点投过票,或者是已经投过票给当前节点,那么继续检测日志的匹配状态(步骤3)
    • 不然,那么拒绝投票(设置voteGrantedfalse), 由于一个节点在一个任期内不能同时投票给多个节点
  3. 检测候选人的日志是否至少比当前节点的日志新,经过比较候选人的lastLogIndexlastLogTerm和当前节点的日志,确保新选举出来的Leader不会丢失已经提交的日志:

    • 若是日志匹配,即当前的任期和候选人的任期相同,且候选人的日志长度比当前的日志长度 或者 候选人的任期比比当前节点的任期高,那么就为候选人投票(设置votedGrantedtrue),并成为Follower
    • 不然,那么就拒绝投票(设置voteGrantedfalse
Candidate收到请求投票的响应该如何处理?

每个候选人在每个任期内都会发出一轮投票请求,若是在指定时间内,收到大于N/2+1个节点的赞成投票的响应,那么说明投票成功,晋升为Leader

由于在整个投票过程当中,假设网络是不稳定的,那么就有可能致使投票请求和请求的响应丢失,乱序,延时等,从而致使收到和当前任期不相匹配的响应,因此若是收到和当前任期不匹配的响应,那么就直接丢弃不处理。

完整的处理流程以下:

  1. 检查响应的Term是否大于当前候选人的Term

    • 若是是,那么说明有其余节点开始了新一轮的选举或者是有新的Leader被选举出来,那么就把当前节点从Candidate切换为Follower状态,并更新当前节点的Term
    • 不然,进行步骤2
  2. 检查响应的Term是否和当前的节点的Term是否相等:

    • 若是相等,那么说明在指定时间内收到了投票请求的响应,那么就进行步骤3
    • 不然说明这是一个过时的投票请求响应,直接丢弃
  3. 检查响应是否赞成投票:

    • 若是赞成,那么增长当前任期的赞成投票节点数量,并检查赞成投票的节点数量大于 N/2+1,那么就切换为Leader
    • 若是不一样意,多是日志不匹配,由于Leader的日志至少要比Follower的日志新

领导人选举的安全性

从上文的请求投票RPC的处理流程中得知,Leader不是随便选一个节点均可以成为的,若是候选人不知足要求,那么其余节点就不会给候选人投票。

若是集群中的任何一个节点不通过判断就能成为Leader,那么将会发生什么?这种状况可能致使已经被提交的日志被覆盖,若是状态机已经Apply了被覆盖的日志,将会致使不一致的结果。因此为了选举的安全性,Raft添加了如下的限制:

  1. Leader不会覆盖本身的任何日志,Follower严格按照Leader的日志进行复制(必要时强行覆盖)
  2. 选举Leader的时候,Candidate的日志至少要比当前节点新(这个“新”稍后解释),不然就拒绝投票;由于已经提交的日志确定是存在大于等于N/2+1个节点上的,而投票至少也须要N/2+1个节点赞成,因此整个投票过程当中确定会有包含有全部已经提交日志的节点存在的。

上文中的“新”就是:即当前的任期和候选人的任期相同,且候选人的日志长度比当前的日志长度 或者 候选人的任期比比当前节点的任期高

相关文章
相关标签/搜索