Raft算法的Leader选举和日志复制过程

Raft 简介

Raft是一种为了管理复制日志的一致性算法。它提供了和 Paxos算法相同的功能和性能,但是它的算法结构和 Paxos不同,使得Raft算法更加容易理解并且更容易构建实际的系统。为了提升可理解性,Raft将一致性算法分解成了几个关键模块,例如Leader选举、日志复制和安全性。同时它通过实施一个更强的一致性来减少需要考虑的状态的数量。从一个用户研究的结果可以证明,对于学生而言,Raft算法比 Paxos算法更加容易学习。Raft算法还包括一个新的机制来允许集群成员的动态改变,它利用重叠的大多数来保证安全性。

 

分布式一致性算法

一致性算法允许一组机器像一个整体一样工作,即使其中一些机器出现故障也能够继续工作下去。正因如此,一致性算法在构建可信赖的大规模软件系统中扮演着重要的角色。在过去的 10 年里,Paxos算法统治着一致性算法这一领域:绝大多数的实现都是基于 Paxos或者受其影响。同时 Paxos也成为了教学领域里讲解一致性问题时的示例。

但是不幸的是,尽管有很多工作都在尝试降低它的复杂性,但是 Paxos算法依然十分难以理解。并且,Paxos自身的算法结构需要进行大幅的修改才能够应用到实际的系统中。这些都导致了工业界和学术界都对 Paxos算法感到十分头疼。

和 Paxos算法进行过努力之后,研究人员开始寻找一种新的一致性算法,可以为构建实际的系统和教学提供更好的基础。作者的做法是不寻常的,首要目标是可理解性:是否可以在实际系统中定义一个一致性算法,并且能够比 Paxos算法以一种更加容易的方式来学习。此外,Raft作者希望该算法方便系统构建者的直觉的发展。一个算法能够工作是很重要的,而且能够知道为什么能工作也很重要。

Raft一致性算法就是这些工作的结果。在设计Raft算法的时候,使用一些特别的技巧来提升它的可理解性,包括算法分解(Raft主要被分成了Leader选举,日志复制和安全三个模块)和减少状态机的状态(相对于 Paxos,Raft减少了非确定性和服务器互相处于非一致性的方式)。

强领导者(Strong leader):和其它一致性算法相比,Raft使用一种更强的领导能力形式。比如,日志条目只从领导者发送给其它的服务器。这种方式简化了对复制日志的管理并且使得Raft算法更加易于理解。

领导选举(Leader election):Raft算法使用一个随机计时器来选举领导者。这种方式只是在任何一致性算法都必须实现的心跳机制上增加了一点机制。在解决冲突的时候会更加简单快捷。

成员关系调整(Membership changes):Raft使用一种共同一致的方法来处理集群成员变换的问题,在这种方法下,处于调整过程中的两种不同的配置集群中大多数机器会有重叠,这就使得集群在成员变换的时候依然可以继续工作。

 

Leader 选举和日志复制

Raft的角色

Client:系统外部客户端,以下gif使用绿色节点代表client。

Leader:负责接受Client的写请求,并将写请求同步到所有的Follower,使用带有黑色实线的节点表示。

Follower:同步于Leader的数据

Candidate:当一定时间内不存在Leader时,会有相应的Follower成为候选者进行Leader选举,使用带有虚线的节点表示。

 

单一节点

假设有一个单节点系统。以例,可以将这个节点视为存储单个值的数据库服务器。客户端只需要将数据发送到这个服务器上就能完成存储,读取数据是也是从这个服务器获取数据。一个节点只需要完好的保持数据即可,不需要共识机制,因为只有这么一个数据。但是,如果有多个节点,每个节点都需要存储同一个值,那么就需要共识机制保证数据的一致性,即分布式共识问题。

Raft算法的Leader选举和日志复制过程

 

单一 Leader 选举

Raft所有的节点都以Follower状态开始。如果Follower没有收到Leader的信息,那么们可以成为Candidate。然后,Candidate从其他节点请求投票节点将投票表决。如果Candidate从多数节点中获得选票,它将成为Leader。这个过程称为Leader选举。在Raft中,有两个超时设置可控制选举。首先是选举超时选举超时是指Follower成为Candidate之前所等待的时间。选举超时被随机分配在150毫秒至300毫秒之间。选举超时后,Follower成为Candidate并开始新的选举任期(Term),它先为自己投票,然后向其他节点发送请求投票消息。如果接收节点在这个期间还没有投票,那么它将投票给Candidate。并且节点重置其选举超时。

Raft算法的Leader选举和日志复制过程

若Leader宕机了,观察选举连任的情况。节点 A 经过重新选举后成为任期2的Leader。在这一个过程中获得多数票,可以确保每个任期只能选出一位Leader。Leader选举需要半数以上的Follower同意才行,一般节点数量应该选择为奇数。宕机选举如下:

Raft算法的Leader选举和日志复制过程

Leader开始向其Follower发送"添加条目"消息。这些消息以心跳超时指定的时间间隔发送。Follower然后响应每个追加条目消息。此选举任期将持续到Follower停止接收心跳并成为Candidate为止。Leader向其他Follower发送Log同步请求时,实质上也是一个心跳包,这里心跳包的作用一是携带请求,二是刷新Follower的选举超时时间。

Raft算法的Leader选举和日志复制过程

 

多个 Candidate 竞争选举

如果两个节点同时成为候选节点,则可能会发生拆分表决。两个节点都开始以相同的任期进行选举,而每个Candidate都先到达一个Follower节点,并且得到投票。现在,每位Candidate都有2票,并且在这个任期中将无法获得更多选票,此时需要超时时间过后再进行选举。节点 C 在第5个任期中获得了大多数选票,因此成为Leader。双候选者选举情况如下:

Raft算法的Leader选举和日志复制过程

 

日志复制

系统的所有更改现在都通过Leader。每次更改都将添加到节点日志中的条目。该日志条目当前未提交,因此不会更新节点的值。要提交条目,节点首先将其复制到Follower节点,然后Leader等待,直到大多数节点都写了该条目。如下所示,该条目已提交到Follower节点上,并且节点状态为"5"。Leader然后通知Follower该条目已提交。现在,集群已就系统状态达成共识。此过程称为日志复制

Raft算法的Leader选举和日志复制过程

 

当选出一位Leader后,需要将系统的所有更改复制到所有节点(刚才是Term 0,现在是Term 1)。通过使用与心跳相同的"添加条目"消息来完成此操作。首先,客户将更改发送给Leader。更改将添加到Leader的日志中然后将更改在下一个心跳发送给Follower。一旦大多数Follower认可,便提交该条目。然后将响应发送给客户端。现在,让发送一条命令,将值增加"2"最终系统值更新为"7"

Raft算法的Leader选举和日志复制过程Raft算法的Leader选举和日志复制过程

 

网络分区

面对网络分区,Raft仍然可以保持一致。假如添加一个分区以将[ A, B ],[ C, D, E ]分开。现在有两位Leader使用不同的选举。我们添加另一个客户端,并尝试更新两个Leader。通过添加客户端对不同分区的访问来学习Raft在网络分区发生时的处理策略。

分区产生前后:

Raft算法的Leader选举和日志复制过程Raft算法的Leader选举和日志复制过程

分区产生后,由于[ C, D, E ]的选举超时时间流逝,其中 C 成为候选人,向 D、E 节点发送投票请求,C 成为所在分区的Leader。

 

分别添加客户端访问两个分区:

Raft算法的Leader选举和日志复制过程

此时客户端 1向Leader B发送请求,Leader B会发送心跳包给A,确认其是否可以同步,但是却发现当前只有 2 个节点同意同步(包括Leader B自身的一票),达不到节点总数的一半以上(不到3个),所以暂时不会进行数据的更新。此时客户端 2向Leader C发送了一个请求,Leader C把心跳包发送给 D 和 E ,收获到半数以上节点的投票,即表明可以同步数据,所以提交日志,C、D、E节点被设置为8

 

恢复分区后:

Raft算法的Leader选举和日志复制过程

现在让网络分区恢复。节点 A、B 看到更高的选举期限(Term)并退出。节点 A 和 B 都将回滚其未提交的条目并匹配新Leader的日志。现在,我们的日志在整个集群中是一致的。

 

总结

算法的设计通常会把正确性效率或者简洁作为主要的目标。尽管这些都是很有意义的目标,但是对于Raft而言可理解性也是样的重要。一个广为接受但是十分令人费解的算法 Paxos已经困扰了无数学生和开发者很多年了。在开发者把算法应用到实际的系统中之前,这些目标没有被实现,必然偏离发表算法时的初衷。除非开发人员对这个算法有着很深的理解并且有着直观的感觉,否则将会对它们而言很难在实现的时候保持原有期望。Raft比 Paxos要容易理解。Raft也可以为实际的系统实现提供坚实的基础。