关于raft:Raft算法学习

42次阅读

共计 3600 个字符,预计需要花费 9 分钟才能阅读完成。

Raft 角色

一个 Raft 集群蕴含若干节点,Raft 将这些节点分为三种状态:Leader、Follower、Candidate,每种状态负责的工作也不一样。失常状况下,集群中的节点只存在 Leader 与 follower 两种状态。

  • Leader(领导者):负责日志的同步治理,解决来自客户端的申请,与 follower 放弃心跳
  • Follower(追随者):响应 Leader 的日志同步申请,响应 Candidate 的邀票申请,以及将客户端申请到 Follower 的事务转发给 Leader
  • Candidate(侯选者):负责选举投票,集群刚启动或者 Leader 宕机时,状态为 Follower 的节点将转为 Candidate 并发动选举,选举胜出后,从 Candidate 转为 Leader 状态。
Raft 三个子问题

Raft 实现了和 Paxos 雷同的性能,它将一致性合成为多个子问题:Leader 选举、日志同步、安全性、日志压缩、成员变更。
这里重点看下选举、日志同步与安全性:

  • 选举:当 Leader 宕机或集群启动时,一个新的 Leader 须要被选举进去
  • 日志同步:Leader 接管来自客户端的申请并将其以日志条目标模式同步到集群中的其它节点,并且强制要求其它节点的日志和本人保持一致
  • 安全性:如果有任何的服务器节点曾经利用了一个确定的日志条目到它的状态机中,那么其它节点不能在同一个日志索引地位利用一个不同的指令。

1. 选举原理

依据 Raft 协定,一个利用 Raft 协定的集群在刚启动时,所有节点的状态都是 Follower。因为没有 Leader,Follower 无奈与 Leader 放弃心跳,因而,Follower 会认为 Leader 曾经下线,进而转为 Candidate 状态。而后,Candidate 将向集群中其它节点申请投票,批准本人降级为 Leader。如果 Candidate 收到超过半数节点的投票(N/2+1),它将成为 Leader。

第一阶段:所有节点都是 Follower
在筹备选举时,所有节点状态都是 Follower,并且初始任期为 0,同时启动选举定时器,每个节点的选举定时器超时工夫都在 100-500 毫秒之间且不统一,防止同时发动选举。

第二阶段:Follower 转为 Candidate 并发动投票
节点启动后在一个选举定时器周期内未收到心跳和投票申请,则状态转为候选者 Candidate 状态,且 Term 自增,并向集群中所有节点发送投票申请并且重置选举定时器。每个节点的选举定时器超时工夫都不统一,所以最先转为 Candidate 的节点最有可能成为 Leader。

第三阶段:投票阶段
节点收到投票申请后会依据以下状况决定是否承受投票申请:

  1. 申请节点的 Term 大于本人的 Term,且本人还未投票给其它节点,则承受申请,将票投给它
  2. 申请节点的 Term 小于本人的 Term,且本人琮未投票,则拒绝请求,将票投给本人

第四阶段:Candidate 转为 Leader
一轮选举过后,失常状况下会有一个 Candidate 收到超过半数节点(N/2+1)的投票,它将胜出并降级为 Leader。而后定时发送心跳给其它节点,其它节点会转为 Follower 并与 Leader 放弃同步,至此,选举完结;如果未过半数会进行下一轮选举。

2. Raft 日志同步

在一个 Raft 集群中,只有 Leader 节点可能解决客户端申请(如果申请到了 Follower,它会将申请转发到 Leader),客户端的每个申请都蕴含一条被复制状态机执行的指令。Leader 将这条指令作为一条新的日志条目(Entry)附加到日志中去,而后并行地将附加条目发送给各个 Followers,让它们复制这条上场条目。
当这条日志条目被各个 Followers 平安复制,Leader 会将这条上场条目利用到它的状态机中,而后将执行后果返回给客户端。如果 Follower 解体或者运行迟缓,或者网络丢包,Leader 会一直尝试附加日志条目直到所有的 Follower 都最终存储所有的日志条目,确保强一致性。

第一阶段:客户端申请提交到 Leader
Leader 在收到客户端申请后会将其作为日志条目(Entry)写入本地日志中,须要留神的是该 Entry 的状态是未提交(uncommitted),Leader 并不会更新本地数据,因而它是不可读的。

第二阶段:Leader 将 Entry 发送到其它 Follower
Leader 与 Follower 之间放弃着心跳分割,随心跳 Leader 将追加的 Entry(AppendEntries)并行地发送给其它的 Follower,并让它们复制这条上场条目,这一过程称为复制(Replicate)。

  1. Leader 向 Follower 发送的 Entry 是 AppendEntries. 在一个心跳周期内,Leader 可能会接管到多条客户端的申请,因而,Leader 发送给 Follower 也会是多个 Entry 也就是 AppendEntries
  2. Leader 在向 Follower 发送追加的 Entry 外,还会将上一条日志条目标索引地位(prevLogIndex),Leader 的任期号(Term)也一并发送。如果 Follower 在它的日志中找不到蕴含雷同索引地位和任期号的条目,那么它就会回绝接管新的日志条目,呈现这样的状况阐明 Follower 与 Leader 数据不统一了。
  3. 解决 Leader 与 Follower 数据不统一的问题。因为某些起因 Leader 上数据可能比 Follower 上的数据多也可能少。要使二者数据放弃雷同,Leader 会找到最初两者达成统一的中央,而后将那个点之后的日志条目删除并发送本人的日志给 Follower,所有这些操作都在进行附加日志的一致性查看时实现。
    Leader 还会为每个 Follower 保护一个 nextIndex,它示意下一个须要发送给 Follower 日志条目标索引地址。当一个 Leader 刚被选举进去的时候,它会初始化所有的 nextIndex 值,并给本人最初一条日志的 index 加 1。如果一个 Follower 的日志和 Leader 不统一,那么在下一次附加日志一致性查看会失败。在被 Follower 回绝之后,Leader 会减小该 Follower 对应的 nextIndex 值并进行重试。最终 nextIndex 会在某个地位使得 Leader 和 Follower 的日志达成统一,这时会将 Follower 抵触的日志条目全副删除并再加上 Leader 的日志,这样 Follower 的日志就会和 Leader 保持一致。

第三阶段:Leader 期待 Follower 回应
Follower 接管到 Leader 发来的复制申请后,可能会有两种回应:

  1. 写入本地日志中,返回 Success;
  2. 一致性查看失败,回绝写入,返回 False(解决形式见第二阶段)

须要留神的是,此时该 Entry 的状态在 Leader 中仍是未提交状态,当 Leader 收到大多数 Follower Success 回应后,会将第一阶段写入的 Entry 标记为提交状态,并此日志条目利用到它的状态机中。

第四阶段:Leader 回应客户端
实现前三个阶段后,Leader 会向客户端回应 OK,示意写操作胜利。

第五阶段:Leader 告诉 Followers Entry 已提交
Leader 回应客户端后,将随着下一个心跳告诉各 Follower,各 Folloer 收到告诉后会将 Entry 标记为提交状态,至此,Raft 集群超过半数节点曾经达到统一状态,能够确保强一致性。因为网络等起因可能会有局部 Follower 在某个工夫点内与 Leader 不统一,但最终还是会统一。

3. Raft 算法之安全性
Raft 如何保障对于给定的任期号(Term),Leader 都领有之前任期的所有被提交的日志条目(也就是 Leader 的完整性)呢?
1) 为了保障所有之前的任期号中曾经提交的日志条目在选举的时候都会呈现在新的 Leader 中,Raft 采纳的是日志条目单向传递,只从 Leader 传给 Follower,并且 Leader 从不会笼罩本身本地日志中曾经存在的条目。
2) 一个 Candidate 只有蕴含了最新的已提交的 log entry,并且取得了集群中大部分节点的认可才有可能博得选举,这也意味着每一个曾经提交的日志条目必定存在于至多一个服务器节点上。
Candidate 在发送 RequestVote RPC 时,要带上本人的最初一条日志的 term 和 log index,其余节点收到音讯时,如果发现自己的日志比申请中携带的更新,则回绝投票。日志比拟的准则是,如果本地的最初一条 log entry 的 term 更大,则 term 大的更新,如果 term 一样大,则 log index 更大的更新。
3) Leader 能够复制后面任期的日志,然而不会被动提交后面任期的日志,而是通过提交以后任期的日志来间接地提交后面任期内的日志(这一点有些不太了解)

参考的文章:
分布式算法 – Raft 算法
Raft 协定安全性保障

正文完
 0