RAFT协议介绍

RAFT协议介绍

RAFT协议是一种用于管理复制日志的一致性算法,其功能与Paxos相同类似,但其算法结构和Paxos算法不同,在设计Raft算法时设计者就将易于理解作为其目标之一,这使得Raft算法易于构建实际的系统,大幅度减少了工程化的工作量,也方便开发者此基础上进行扩展。

工作模式

RAFT工作模式采取的是Lead-Follower模式。在RAFT协议中,有一个Leader和多个Follower节点。每个节点都维护了一个状态机,该状态机有三种状态,分别是Leader状态、Follower状态和Candidate状态,在任意时刻,及群众的任意一个节点处于这三个状态之一。

在多数情况下,集群中有一个Leader节点,其他节点都处于Follower状态。

  • Leader节点负责处理所有客户端的请求。当接收到客户端的写入请求时,Leader节点会在本地追加一条相应的日志,然后将其封装成消息发送到集群中的其他Follower节点。当Follower节点收到该消息时会对其进行响应。如果集群中多数(超过半数)节点都已收到该请求对应的日志记录时,则Leader节点认为该条日志记录已提交,可以向客户端返回响应。Leader节点的另一项工作时定期向集群中的Follower节点发送心跳消息,这主要是为了放置集群中的其他Follower节点的选举计时器超时而触发新一轮选举。
  • Follower节点不会发送任何请求,它们只是简单地响应来自Leader或者Candidate的请求;Follower节点也不处理client的请求,而是将请求重定向给集群的Leader节点进行处理。
  • Candidate节点是由Follower节点转换而来的,当Follower节点长时间没有收到Leader节点发送的心跳消息时,则该节点的选举计时器就会过期,同时会将自身状态转换成为Candidate,发起新一轮的选举。

举例

名词解释

选举超时时间

每个Follower节点在接收不到Leader节点的心跳消息之火,并不会立即发起新一轮选举,而是需要等待一段时间之后才切换成Candidate状态发起新一轮选举。这段等待时长就是这里所说的选举超时时间。

之所以这样设计,是因为Leader节点发送的心跳消息可能因为瞬间的网络延迟或程序瞬间的卡顿而迟到(或者丢失),因此就触发新一轮的选举是没有必要的。选举超时时间一般设置为150ms-300ms之间的随机数。

心跳超时时间

心跳超时时间是Leader节点向集群中其他Follower节点发送心跳消息的时间间隔。

任期(term)

所谓“任期”,实际上就是一个全局的,连续递增的整数,在RAFT协议中每进行一次选举,任期加一,在每个节点中都会记录当前的任期值。每一个任期都是从一次选举开始的,在选举时,会出现一个或者多个Candidate节点尝试称为Leader节点,如果其中一个Candidate节点赢得选举,则该节点就会切换为Leader状态并称为该任期的Leader节点,直到该任期结束。

初始化选举(一个Candidate)

假设有一个3个节点的集群。分别有A、B、C三个节点。

当集群初始化时,所有节点都处于Follower状态,此时的集群中,诶呦Leader节点。当Follower节点一段时间内收不到Leader节点的心跳信息,则认为Leader节点出现故障导致其任期(term)过期,Follower节点会转换成Candidate状态,发起新一轮的选举。

在上述集群中,假设节点A率先超时,它切换为Candidate状态,并发起选举。在选举过程中,节点A首先会将自己的选票投给自己,并会向集群中其他节点发送选举请求获取其选票。如下图(1)所示;

而此时,节点B和节点C还是处于term=0的任期之中,都是Follower状态,均未投出term=1任期中的选票,所以节点B和C在接收到节点A的选举请求后,会将选票投给节点A,另外,节点B、C在收到节点A的选举请求的同时会将选举定时器重置,这时为了放置一个任期中同时出现多个Candidate节点,导致选举失败,节点B和C也会递增记录自己的term值,如上图(2)

在节点A收到节点B、C的投票之后,其收到了集群中超过半数的选票,所以在term-1这个任期中,该集群的Leader节点就是A节点,其他节点将切换成Follower状态。

另外,集群中节点除了记录当前任期号,还会记录在该任期中当前节点的投票结果。

在称为term=1任期的Leader节点之后,节点A会定期向集群中的其他节点发送心跳消息,如下图(1)所示,这样就额可以防止节点B和节点C中的选举计时器超时而触发新一轮的选举:当节点B和节点C受到节点A的心跳消息之后会重置选举计时器。

由此可见,心跳超时时间需要远远小于选举超时时间。

多个Candidate选举

上个章节说的是一个Candidate的情况,如果集群中两个或者两个以上的Follower同时超时的情况下怎么处理呢,看看下面这个例子。

假设集群中存在4个节点,分别为A、B、C、D。在某个时刻,节点A、B没有收到Leader的心跳信息,它们的超时时间又刚好相同,于是,节点A、B同时超时,状态切换为Candidate并向其他节点发出选举请求。如下图(1)所示。

再假设节点A的请求先到节点C,节点B的请求先到节点D,那么,节点A和B除了得到自身的投票之外,还分别获得了节点C和节点D的投票,得票数都是2,都没有超过半数。在这种情况下,当前这个term=4任期就选举失败,无法选举而告终。如上图(2)所示。

随着时间的流逝,当任意节点的选举计时器到期之后,会再次发起新一轮的选举。因为选举超时时间是一个随机值,所以,很大概率下,下一次只有一个节点会超时。

这里再假设节点A率先超时,它再次切换至Candidate状态,并发起新一轮选举,term加一。如下图(1)所示。节点B虽然处于Candidate状态,但是接收到的term值比本身记录的值大,节点B就切换为Follower状态,并更新自身的term为最新的值,所以节点B也会将选票投给节点A,如下图(2)所示:

Leader宕机之后重新选举

假设在上章节的集群稳定之后,Leader节点A突然间宕机了。B、C、D节点因为收不到心跳信息,会有一个节点率先超时,此处假设为D节点,它将切换为Candidate状态,并发起新一轮选举。如下图(1)所示:

当节点B和节点C受到节点D的选举请求后,会将其选票投给节点D,由于节点A宕机,没有参加此次厍,也就无法进行投票,但是在此轮选举中,节点D依然获得了半数以上的投票,故而成为新任期的Leader节点,并开始想其他Follower节点发送心跳消息。

当节点A恢复之后,会收到节点D发来的心跳消息,该消息中携带的任期号大于节点A当前记录的任期号,所以节点A会切换成Follower状态。在RAFT协议中,当某个节点接收到的消息锁携带的任期号大于当前节点本身记录的任期号,那么该节点会更新自身记录的任期号,同时会切换为Follower状态并重置选举计时器。

日志复制

Leader节点除了向Follower节点发送心跳消息,还会处理客户端的请求,并将客户端的更新操作以消息(Append Entries消息) 的形式发送到集群中所有的Follower节点。当Follower节点记录受到的这些消息之后,会向Leader节点返回相应的响应消息。当Leader节点在收到半数以上的Follower节点的响应消息之后,会对客户端的请求进行应答。最后,Leader会提交客户端的更新操作,该过程会发送Append Entries消息到Follower节点。通知Follower节点该操作已经提交,同时Leader节点和Follower节点也就可以将该操作应用到自己的状态机中。

示例

还是以一个由三个节点的集群为例,节点A、B、C,其中A为Leader。客户端发送一个更新操作到节点A,如下图:

此时,节点A会将该更新操作记录到本地的Log中。

之后,节点A会向其他节点发送Append Entries消息,其中记录了Leader节点最近接收的请求日志,如下图。集群中的其他Follower节点收到该Append Entries消息之后,会将该操作记录到本地的Logzhong,并返回相应的响应消息。

当Leader节点收到半数以上的响应消息之后,会认为集群中有半数以上的节点已经记录了该更新操作,Lean的节点会将该更新操作对应的日志记录设置为已提交,并应用到自身的状态几种,同时Leader节点还会对客户端的请求作出响应。如下图。

同时,Leader节点也会向集群中的其他Follower节点发送消息,通知它们该更新操作已经被提交,Follower节点收到该消息之后,才会将该更新操作应用到自己的状态机中。

参考资料

RAFT协议动图演示

0%