JAVA相关分布式分布式系统

分布式系统学习2-Raft算法分析与实现

2017-10-19  本文已影响337人  __七把刀__

Raft是一个分布式系统的一致性算法,它不像Paxos那么难懂,实现比Paxos简单许多,性能与Paxos相当,在Etcd,Consul里面等都有广泛运用。之前在容器服务化的时候用到Consul,顺带看了Raft算法的论文,然后为了练手Go语言做了mit6.824分布式系统课程的lab2。由于实验里面随机选举时间和模拟的节点crash导致的异常可能在你运行上百次才会出现,实现后要测试多次以保证测试通过。我的Raft算法的实现代码在这里 6.824-2017-raft,多有参考其他代码,见README。6.824课程的lab1是完成一个简化版的MapReduce,实现比较简单,代码见 6.824-2017-mapreduce。如有错误,恳请指正。

1 概述

分布式系统的一致性算法就是指一组机器协同工作,即便其中有某些机器宕机了,系统还能正常对外提供服务。以前通常都喜欢用Paxos来讲解一致性算法,但是Paxos本身很复杂,代码实现也很难,于是催生了Raft这个更加简单易懂的一致性算法,难得的是,它的效果跟Paxos差不多。

为了易于理解,Raft采用了算法分解(分为leader选举,日志复制以及安全性)和减少状态的方式。与以往一致性算法不同的是,Raft有一些特别的地方:

2 复制状态机

一致性算法是在复制状态机的背景下提出来的。在复制状态机中,集群中的服务器从相同的状态中生成同样的副本,即使其中有些服务器宕机了,客户端还是可以继续执行操作,复制状态机可以用来解决分布式系统中许多容错问题。大型分布式系统中通常拥有一个集群leader,比如GFS,HDFS等通常使用一个单独的复制状态机管理leader选举和配置信息以应对leader的崩溃。此外,使用复制状态机的还有Chubby以及ZooKeeper等。

复制状态机通过复制日志实现,如下图所示。每个服务器都会存储一份日志,日志存储的是一系列命令,而服务器的状态机会按顺序执行这些日志中的命令。每份日志中以同样的顺序存储了同样的命令,而状态机以同样的顺序执行这些相同的命令。每台服务器的状态机都是确定的,它们以同样的顺序执行同样的命令,最终的状态和输出也必然是一样的。

复制状态机图示

保持复制日志的一致性就是一致性算法的工作了。服务器上的一致性模块接收客户端命令并添加命令到它的日志中。它与其他服务器通信以保证每个日志最终都以相同的顺序包含相同的命令,即便过程中有服务器宕机了。一旦客户端命令正确的复制了,每个服务器的状态机按照日志中顺序处理这些命令,并将输出返回给客户端。最终,这些服务器看起来就像是一台高可用的状态机。

应用到实际系统中的一致性算法通常具备下面几个特性:

3 Raft算法

Raft算法分为两部分,领导选举(Leader Election)和日志复制(Log Replication)。这里有个很形象的动画说明Raft算法的实现,关于成员变更和日志快照这里有篇解释很好的文章,见 深入浅出 Raft - Membership Change

3.1 领导选举

首先了解下Raft中节点的几个状态:Follower,Candidate,Leader。状态变迁如下图所示。

Raft节点状态变迁图

选举投票需要两个条件:

3.2 日志复制

Raft是强Leader机制,日志只能从Leader复制到其他节点。日志项LogEntry包括index,term,command三个元素。其中index为日志索引,term为任期,而command为具体的日志内容。日志格式如下图所示:

Raft日志格式示意图

通常的日志复制流程是这样的:

即便出现网络分割,集群中同时存在多个Leader时,也不会有问题。假定5个节点的集群分割成了3节点和2节点两个大小集群,3节点大集群因为数目3过半,可成功提交日志,而节点数不够的小集群没法成功提交日志。当网络恢复时,因为另外分割的一个大集群已经成功提交了日志,最终新的Leader会在大集群中产生(基于选举投票的条件二保证)并同步到之前分割的小集群节点中。

关于日志复制的几个要点:

时序和可用性:

Raft的一个特点就是安全性不依赖时序,系统不会因为时序问题而导致错误发生,但是系统的可用性不可避免的会对时序有所依赖。如果服务器崩溃会导致Candidate节点选举不成功而不停的发起选举,而Raft必须有一个稳定的Leader,否则无法工作。领导选举是Raft中对时序要求最关键的地方,Raft能够选举出并保持一个稳定的Leader需要系统满足如下时序要求:

broadcastTime << electionTimeout << MTBF

其中broadcastTime是指一台服务器并行地向集群其他服务器发送RPC并接收到响应的平均时间,而electionTimeout是选举超时时间,MTBF则是指单个服务器发生故障的平均间隔时间。broadcastTime远小于electionTimeout可以保证Leader持续发送心跳包给Follower节点以防止Follower节点发起选举,electionTimeout远小于MTBF是为了保证系统的稳定运行。Leader崩溃后,理论上大约只有electionTimeout的时间内服务不可用。

根据存储方式的不同,broadcastTime一般设置为0.5ms到20ms(实验中设置心跳间隔略有不同,推荐是100ms左右,我设置的50ms),而electionTimeout一般是10-500ms(实验设置的是300+ms)。通常服务器的MTBF一般是几个月甚至几年,很容易符合这个要求。

4 Raft日志复制状态分析

4.1 前一条日志相同才能复制成功

复制状态图示1

4.2 Leader最新任期有日志已经复制到了大多数节点(安全)

复制状态图示2

如图所示,S1-S3在任期2已复制成功了第4条LogEntry,这个时候Leader必须包括第4个LogEntry,因此重新选举时S4和S5都不能选举为Leader,第4条日志可以安全提交。

4.3 Leader试图从一个较老的任期提交日志(不安全)

复制状态图示3

如上图所示,这时候如果提交第3条LogEntry是不安全的,因为后续如果S5选举为Leader的话会覆盖S1,S2,S3的第3条日志。

4.4 Leader安全的提交日志

复制状态图示4

如上图所示,此时Leader最新任期4的一个日志条目4已经复制到大多数节点S1-S3,此时S5不能选举成功,日志条目3和4都是安全的。这就印证了前面提到的Leader当前任期的新日志条目至少有一个复制到了大多数Follower节点才能提交。

4.5 Leader变化导致日志不一致

Leader变化导致日志不一致

如上图所示,Leader变化会导致各节点日志不一致,则需要做如下处理:

日志不一致处理流程示意

5 Raft实现需注意的几个地方

Raft实现需要的数据结构在论文中已经很完整,如下图:

Raft实现数据结构和流程要点

6 参考资料

上一篇下一篇

猜你喜欢

热点阅读