分布式一致性算法--Raft简介

分布式一致性算法简介

在讲分布式一致性算法之前,我们来讲一道经典面试题,你会如何设计一个系统架构。

  • 系统初期

5.jpg 在一个流量不是很大的情况下,我们只需要一台服务器,上面装一个数据库,起一个应用,就可以跑一个服务,一些非常经典的开源论坛就是这么玩的

  • 系统早期

随着越来越多的用户,我们的单服务渐渐无法承载,这时,我们可能不得不经常重启我们的服务,以及受到用户的吐槽,怎么系统又挂了,又502啦。此时,你遭到了领导的威胁,怎么回事啊,用户上不来了,再上不来你不用干了,那么这时,最快最好的方案自然是升级配置或者服务分离,此时,我们将应用程序和数据库分开

6.jpg

可惜,我们的网站太受欢迎了,没过多久,又不行了,此时老板找上了门,那么我们该怎么办?聪明的小伙伴提出,我们的系统基本上都是查询查询那些数据,是不是可以优化一下呢?正如与民间的系统一样,我们也符合28定律,80%的业务集中访问在20%的数据上。那么我们可以上缓存了。

7.jpg

随着系统的继续升级,我们发现一台应用服务器无法再满足我们的需要,于是我们拓展了系统,采用了集群的模式来应对压力

8.jpg

服务器应用的问题解决以后,DB的压力随之而来,此时,我们增加读写分离

9.jpg

相对而言,我们已经是一个比较成熟的系统,自此相安无事了一段日子。

某天老板又找上了门:”小孙啊,我们拓展了新疆的服务,你这个网站新疆怎么这么慢啊?”我们又遇到了下一个问题,网络地域问题。

此时,我们采用了CDN和反向代理,这两者的基本原理都是缓存,可以更高效的返回数据,那么我们的架构就成了这样。

10.jpg

  • 系统后期

再后来,我们拆解了数据库,从单库主从到了分库分表,拆解了应用,从单机到微服务,使用了各种中间件

11.jpg

那么让我们回到问题,你会怎么设计一个系统。

讲完这些,是不是你会觉得,这和分布式一致性算法没有关系,只是讲了一个系统的演变过程,那么其实我们可以看到,在系统的演变中,我们拆分了系统,使用了分布式缓存、数据库、搜索引擎、应用等等。这里有些是高可用,有些是强一致。

CAP定理

  • 一致性
  • 可用性
  • 分区容错性

CP应用

  • zookeeper

AP应用

  • eureka

共识算法

  • Paxos
  • Raft
  • Zab

Raft算法

简要介绍

Raft is a consensus algorithm for managing a replicated log. It produces a result equivalent to (multi-)Paxos, and it is as efficient as Paxos, but its structure is different from Paxos; this makes Raft more understandable than Paxos and also provides a better foundation for building practical systems. In order to enhance understandability, Raft separates the key elements of consensus, such as leader election, log replication, and safety, and it enforces a stronger degree of coherency to reduce the number of states that must be considered. Results from a user study demonstrate that Raft is easier for students to learn than Paxos. Raft also includes a new mechanism for changing the cluster membership, which uses overlapping majorities to guarantee safety.

Raft 是用来管理复制日志(replicated log)的一致性协议。它跟 multi-Paxos 作用相同,效率也相当,但是它的组织结构跟 Paxos 不同。这使得 Raft 比 Paxos 更容易理解并且更容易在工程实践中实现。为了使 Raft 协议更易懂,Raft 将一致性的关键元素分开,如 leader 选举、日志复制和安全性,并且它实施更强的一致性以减少必须考虑的状态的数量。用户研究的结果表明,Raft 比 Paxos 更容易学习。 Raft 还包括一个用于变更集群成员的新机制,它使用重叠的大多数(overlapping majorities)来保证安全性。

论文地址:In Search of an Understandable Consensus Algorithm

Raft角色

  1. Leader:正常情况下,只会有一个Leader,负责日志同步与响应客户端的请求,与其他Follower保持心跳。在网络异常的情况下,可能会存在多个Leader,但在网络恢复后,会同步最高Term的Leader
  2. Follower:响应Leader的消息,响应Candidate的选举邀请,重定向客户端到Follower的请求
  3. Candidate:发起投票选举,在集群初始化或Leader宕机的时候,发起Leader选举的投票

概念

  1. Term:在Raft中使用了一个可以理解为任期的概念,用Term作为一个周期,每个Term都是一个连续递增的编号,每一轮选举都是一个Term周期,在一个Term中只能产生一个Leader。

Term是一个逻辑时钟值,Lamport Timestamp的一个变种。

假设多进程要维护一个全局时间,每个进程本地要有一个全局时间的副本。

1)每个进程在事件发生时递增自己本地的时间副本

2)每当进程发送消息时,带上自己本地的时间副本

3)当进程收到消息时,比较消息中的时间值和自己本地的时间副本,选择比较大的时间值加1,并更新自己的时间副本

  1. election timeout:选举超时,Follower申请成为Candidate的一个时间。为了减少同时申请的概率,是一个150ms-300ms的随机时间
  2. heartbeat timeout:Leader 为了维护自己的任期,定期通知 Follower 自己还健在,如果心跳超时,Follower
  3. RequestVote:Candidate发起投票,注意此处的内容,对安全性校验有影响

image.png

  1. AppendEntries RPC: Leader与Follower通信内容

image.png

选举

  1. 假设我们现在有节点Node A,Node B,Node C
  2. 集群启动时,所有的节点Term都为0,由于没有Leader,此时触发election timeout

image.png

  1. 由于存在随机时间,率先超时的节点成为Candidate(此时也可能存在多个节点同时成为Candidate)
  2. 此时假设Node A成为了Candidate,Node A的Term变为1,投给自己一票,Node B、Node C的Term为0

image.png

  1. 此时Node A发送RequestVote消息到其他节点。尚未成为Candidate的并且没有投过票的Node会把票投给Node A,并且Node B、Node C会重置election timeout

image.png

  1. 此时Node A获取了最多的选票,成为了Leader。Leader开始往其他节点发送心跳(不包含日志条目)来维系Leader地位,Follower响应Leader的消息

image.png

  1. 当Leader发送心跳超时或者宕机时,Follower触发了election timeout,率先触发election timeout的Follwer申请成为Candidate并开始新一轮的Leader选举,Term+1

image.png

  1. 假设此时Node A与Node B同时成为了Candidate并向其他Node发出申请,并获得同票,触发新一轮的选举

image.png

此处留下两个小问题:

  1. 我们一直的讨论都是基于节点总数不变的情况,如果我们在3个节点的情况下动态添加了两个节点,此时是否会出现脑裂的情况,如果分区出现网络问题呢?[提示:单节点加入和多节点加入,集群配置,Leader]
  2. 如果Node B没有收到Leader的心跳,他能简单通过申请Candidate成为Leader吗?

日志复制

  1. 在Raft算法中,所有来自客户端的数据变更请求都会被当做一个日志条目追加到节点日志中。
  2. Raft算法中的日志条目除了操作还有Term,也就是上面提到的任期,Term也会被用于日志比较。

image.png

  1. 日志条目分为两种状态:已追加但未持久化、已持久化。
  2. Raft算法中会维护一个已持久化的日志条目索引,即commitIndex。小于等于commitIndex的日志条目被认为是已提交的,否则是未持久化的。
  3. 对了跟踪各个节点的复制进度,Leader会记录每个节点的nextIndex(下一个需要复制的日志条目的索引)和matchIndex(已匹配日志的索引)
  4. 选出Leader后,Leader会新建各节点的nextIndex和matchIndex,matchIndex首先为0,nextIndex为Leader节点接下来的日志条目索引,通过和各个节点发送AppendEntries消息来更新日志。
  5. 复制过程中,raft认为Follower节点和Leader节点不会有太大的日志差距,所以会将nextIndex设为最大,然后不断回退,最终匹配日志索引,从匹配点开始,往后与Leader节点不同的日志将被覆盖。
  6. 当客户端提交了一个数据变更,Leader节点会在自己的日志中追加一条日志,但是不提交(不会新增commitIndex)
  7. Leader通过AppendEntries向其他节点同步消息,AppendEntries里包含了最新追加的日志。当一半(包含Leader)以上的节点追加日志成功后,Leader节点会持久化日志并推进commitIndex,并且再次通知其他节点持久化日志。

此处留下一个小问题:

  1. 如果Leader发出了复制消息,并且多数Follower收到了,随即宕机了,还没有将这个commit同步给其他Follower,这个日志会如何?

安全性

前文其实留下了几个小问题,都是针对异常场景的处理,那么在安全性这一节里,我们会解释一下Raft如何针对这些场景做出处理。

image.png

  1. 如上图,假设我们拥有5个节点,S1-S5
  2. 在a时期,S1竞选为Leader,任期Term2,此时他复制了消息,但仅有S2收到,S1宕机
  3. 在b时期,S1宕机后,S5竞选成为Leader,任期Term3,但他刚上任就宕机了
  4. 在c时期,S1恢复,并竞选成为Leader,任期Term4,并正常运行,此时他会如何?是否会将上一任期的日志复制到其他节点?如果会,假设S1复制完S3后宕机,那么S1、S2、S3都拥有Term2时期的日志,多数节点拥有了Term2时期的日志
  5. 在d时期,S5恢复,并竞选成为Leader,他讲term3的日志复制给所有节点。此时是否出现了悖论?S5的Term3覆盖了之前的内容
  6. 事实上,Leader并不会提交上一任的日志,而是采用了no-op的形式,在上任后发送一个空指令,顺带提交了上一任的日志。此时,哪怕S5恢复,任期低的他无法覆盖高任期的内容

我们举个更简单的例子

现在有三个节点Node1、Node2、Node3

Node1为Leader, Log Entry 内容 1、2、3、4

Node2为Follower,Log Entry 内容 1、2、3、4

Node3为Follower,Log Entry 内容 1、2

此时Node1收到新内容,Log Entry 内容变化为 1、2、3、4、5、6

Node1还未提交,宕机了

那么client是查不到5、6的消息的

只有Node2能竞选为Leader

此时三个节点的内容分别为

Node1 宕机, Log Entry 内容 1、2、3、4、5、6

Node2 Leader, Log Entry 内容 1、2、3、4

Node3 Follower,Log Entry 内容 1、2、3、4

此时Node1 恢复,Node2宕机

Node1重新成为Leader,复制日志

Node1为Leader, Log Entry 内容 1、2、3、4、5、6

Node2为Follower,Log Entry 内容 1、2、3、4、5、6

Node3为Follower,Log Entry 内容 1、2、3、4、5、6

此时Client又能查到5、6的消息了,这通常被成为幽灵复现

日志压缩

未完待续

快照同步

未完待续

raft算法视频

文章来源

In Search of an Understandable Consensus Algorithm

大型网站技术架构_核心原理与案例分析

分布式一致性算法开发实战

猜你喜欢

转载自juejin.im/post/7078275803665924127