etcd系列-----raft协议:理论基础篇(一)-------leader选举

常见的一致性算法有Paxos、 Raft等,Paxos协议是LeslieLamport于1990年提出的一种基于消息传递的、具有高度容错特性的一致性算法,Paxos 算法解决的主要问题是分布式系统内如何就某个值达成一致。 在相当长的一段时间内,Paxos 算法几乎成为一致性算法的代名词,但是Paxos 有两个明显的缺点:第一个也是最明显的缺点就是Paxos算法难以理解,Paxos算法的论文本身就比较晦涩难懂,要完全理解Paxos协议需要付出较大的努力,很多经验丰富的开发者在看完Paxos论文之后,无法将其有效地应用到具体工程实践中,这明显增加了工程化的门槛,也正因如此,才出现了几次用更简单的术语来解释Paxos 的尝试。 Paxos算法的第二个缺点就是它没有提供构建现实系统的良好基础,也有很多工程化Paxos算法的尝试,但是它们对Paxos算法本身做了比较大的改动,彼此之间的实现差距都比较大,实现的功能和目的都有所不同,同时与Paxos算法的描述有很多出入。

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

Raft 协议的工作模式是一个Leader 节点和l 多个Follower 节点的模式,也就是常说的Leader-Follower 模式。在Raft 协议中,每个节点都维护了一个状态机,该状态机有三种状态,分别是Leader 状态、Follower状态和Candidate状态,在任意时刻,集群中的任意一个节点都处于这三个状态之一。各个状态和转换条件如图:

 在多数情况下,集群中有一个Leader节点,其他节点都处于Follower状态,每个状态的节点负责的主要工作:

(1)Leader节点负责处理所有客户端的请求, 当接收到客户端的写入请求时,Leader节点会在本地追加一条相应的日志,然后将其封装成消息发送到集群中其他的Follower节点。当Follower节点收到该消息时会对其进行响应。如果集群中多数(超过半数)节点都己收到该请求对应的日志记录时,则Leader 节点认为该条日志记录己提交(commit),可以向客户端返回响应。Leader 还会处理客户端的只读请求, 其中涉及一个简单的优化,后面介绍具体实现时,再进行详细介绍。Leader节点的另一项工作是定期向集群中的Follower 节点发送心跳消息,这主要是为了防止集群中的其他Follower节点的选举计时器超时而触发新一轮选举。

(2)Follower节点不会发送任何请求,它们只是简单地响应来自Leader或者Candidate 的请求。 Follower节点也不处理Client的请求,而是将请求重定向给集群的Leader节点进行处理。

(3)Candidate节点是由Follower节点转换而来的,当Follower节点长时间没有收到Leader节点发送的心跳消息时,则该节点的选举计时器就会过期,同时会将自身状态转换成Candidate,发起新一轮选举。选举的具体过程在下面详细描述

为了方便描述,我们假设当前集群中有三个节点(A、B、 C),如图:

在Raft协议中有两个时间控制Leader选举发生,其中一个是选举超时时间(electiontimeout), 每个Follower节点在接收不到Leader节点的心跳消息之后,并不会立即发起新一轮选举,而是需要等待一段时间之后才切换成Candidate状态发起新一轮选举。这段等待时长就是这里所说的election timeout (后面介绍etcd的具体实现时会提到,Follower节点等待的时长并不完全等于该配置)。之所以这样设计,主要是Leader 节点发送的心跳消息可能因为瞬间的网络延迟或程序瞬间的卡顿而迟到(或是丢失),因此就触发新一轮选举是没有必要的。 electiontimeout一般设置为150ms~300ms之间的随机数。另一个超时时间是心跳超时时间(heartbeattimeout),也就是Leader节点向集群中其他Follower节点发送心跳消息的时间间隔。

当集群初始化时,所有节点都处于Follower 的状态,此时的集群中没有Leader 节点。当Follower 节点一段时间(选举计时器超时)内收不到Leader 节点的心跳消息,则认为Leader节点出现故障导致其任期(Term)过期,Follower节点会转换成Candidate状态,发起新一轮的选举。所谓 “任期(Term〕”,实际上就是一个全局的、连续递增的整数,在Raft协议中每进行一次选举,任期(Term)加一,在每个节点中都会记录当前的任期值(currentTerm)。每一个任期都是从一次选举开始的,在选举时,会出现一个或者多个Candidate 节点尝试成为Leader节点,如果其中一个Candidate节点赢得选举,则该节点就会切换为Leader状态并成为该任期的Leader节点,直到该任期结束。

回到前面的示例中,此时节点A由于长时间未收到Leader 的心跳消息,就会切换成为Candidate状态并发起选举(节点A的选举计时器(electiontimer)己被重置)。在选举过程中,节点A首先会将自己的选票投给自己,并会向集群中其他节点发送选举请求(RequestVote)以获取其选票,如图1所示;此时的节点B和节点C还都是处于Term=0的任期之中,且都是Follower状态,均未投出Term=l任期中的选票,所以节点B和节点C在接收到节点A的选举请求后会将选票投给节点A,另外,节点B、 C在收到节点A的选举请求的同时会将选举定时器重置,这是为了防止一个任期中同时出现多个Candidate节点,导致选举失败,如图2所示。注意,节点B和节点C也会递增自身记录的Term值。

在节点A收到节点B、 C的投票之后,其收到了集群中超过半数的选票,所以在Term=l这个任期中,该集群的Leader节点就是节点A,其他节点将切换成Follower状态,如图所示。另外需要读者了解的是,集群中的节点除了记录当期任期号(currentTerm),还会记录在该任期中当前节点的投票结果(VoteFor)。

继续前面的示例,成为Term=l任期的Leader节点之后,节点A会定期向集群中的其他节点发送心跳消息,如图1所示,这样就可以防止节点B和节点C中的选举计时器(electiontimer)超时而触发新一轮的选举:当节点B和节点C(Follower) 收到节点A的心跳消息之后会重置选举计时器,如图2所示, 由此可见,心跳超时时间(heartbeattimeout)需要远远小于选举超时时间(electiontimeout)。

如果有两个或两个以上节点的选举计时器同时过期,则这些节点会同时由Follower状态切换成Candidate状态,然后同时触发新一轮选举,在该轮选举中,每个Candidate节点获取的选票都不到半数,无法选举出Leader节点,那么Raft协议会如何处理呢?当任意节点的选举计时器到期之后,会再次发起新一轮的选举。前面提到过election timeout是在一个时间区间内取的随机数,所以在配置合理的时候上述情况多次出现的概率并不大。

发布了48 篇原创文章 · 获赞 9 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/cyq6239075/article/details/105310295