引言

分布式系统除了提升整个体统的性能外还有一个重要特征就是提高系统的可靠性。

提供可靠性可以理解为系统中一台或多台的机器故障不会使系统不可用(或者丢失数据)。

保证系统可靠性的关键就是多副本(即数据需要有备份),一旦有多副本,那么久面临多副本之间的一致性问题。

  • 比如,一台机器上的磁盘损坏,数据丢失,可以从另一台机器上的磁盘恢复(分布式系统会对数据做备份)
  • 比如,集群中某些机器宕机,整个集群还可以对外提供服务

什么是RAFT

Raft is a consensus algorithm for managing a replicated log.

Raft是一种管理复制日志的一致性算法。

它的首要设计目的就是易于理解,所以在选主的冲突处理等方式上它都选择了非常简单明了的解决方案。

Raft将一致性拆分为几个关键元素:

  • Leader选举
  • 日志复制
  • 安全性

Raft算法

所有一致性算法都会涉及到状态机,而状态机保证系统从一个一致的状态开始,以相同的顺序执行一些列指令最终会达到另一个一致的状态。

以上是状态机的示意图。所有的节点以相同的顺序处理日志,那么最终x、y、z的值在多个节点中都是一致的。

角色

在Raft中,节点有三种角色:

  • Leader:负责接收客户端的请求,将日志复制到其他节点并告知其他节点何时应用这些日志是安全的。
  • Candidate:用于选举Leader的一种角色。
  • Follower:负责响应来自Leader或者Candidate的请求。
    角色转换如下图所示:
  • 所有节点初始状态都是Follower角色
  • 超时时间内没有收到Leader的请求则转换为Candidate进行选举
  • Candidate收到大多数节点的选票则转换为Leader;发现Leader或者收到更高任期的请求则转换为Follower
  • Leader在收到更高任期的请求后转换为Follower

从状态转换关系图中可以看出,集群刚启动时,所有节点都是follower,之后在time out信号的驱使下,follower会转变成candidate去拉取选票,获得大多数选票后就会成为leader,这时候如果其他候选人发现了新的leader已经诞生,就会自动转变为follower;而如果另一个time out信号发出时,还没有选举出leader,将会重新开始一次新的选举。可见,time out信号是促使角色转换得关键因素,类似于操作系统中得中断信号。

在Raft协议中,将时间分成了一些任意长度的时间片,称为term,term使用连续递增的编号的进行识别,如下图所示:

每一个term都从新的选举开始,candidate们会努力争取称为leader。一旦获胜,它就会在剩余的term时间内保持leader状态,在某些情况下(如term3)选票可能被多个candidate瓜分,形不成多数派,因此term可能直至结束都没有leader,下一个term很快就会到来重新发起选举。

term也起到了系统中逻辑时钟的作用,每一个server都存储了当前term编号,在server之间进行交流的时候就会带有该编号,如果一个server的编号小于另一个的,那么它会将自己的编号更新为较大的那一个;如果leader或者candidate发现自己的编号不是最新的了,就会自动转变为follower;如果接收到的请求的term编号小于自己的当前term将会拒绝执行。

server之间的交流是通过RPC进行的。只需要实现两种RPC就能构建一个基本的Raft集群:

  • RequestVote RPC:它由选举过程中的candidate发起,用于拉取选票
  • AppendEntries RPC:它由leader发起,用于复制日志或者发送心跳信号。

leader选举

选举流程

1.首先,在初始状态下,集群中所有的节点都是Follower状态,并被设定一个随机 election timeout(150ms-300ms):


2.如果超时时间到期后没有收到来自Leader的心跳,节点就发起选举:将自己的状态切换为 Candidate,增加自己的任期编号,然后向集群中的其它 Follower 节点发送请求,询问其是否选举自己成为 Leader:

3.其他节点接收到候选人 A 的请求投票消息后,如果在编号为 1 的这届任期内还没有进行过投票,那么它将把选票投给节点 A,并增加自己的任期编号:


4.当收到来自集群中过半数节点的接受投票后,节点即成为本届任期内 Leader,他将周期性地发送心跳消息,通知其他节点我是Leader,阻止Follower发起新的选举:


如果在指定时间内(一个随机时间,每个节点都不同),Follower没有接收到来自Leader的心跳消息,那么它就认为当前没有Leader,推举自己为Candidate,发起新一轮选举。

通信方式

在 Raft 算法中,节点之间的通信采用的是RPC方式,在Leader选举中,需要用到两类RPC:

  1. 请求投票(RequestVote)RPC:由Candidate在选举期间发起,通知各Follower进行投票;
  2. 日志复制(AppendEntries)RPC:由Leader发起,用来进行日志复制和心跳。

任期

在选举流程中,我提到Leader节点是有任期的,每个任期由单调递增的数字标识,比如节点 A 的任期编号是 1。任期编号是随着选举的举行而变化的:

  1. Follower在等待Leader的心跳信息超时后,会推举自己为Candidate,此时会增加自己的任期编号;
  2. 如果一个节点发现自己的任期编号比其他节点小,那么它会更新自己的编号为较大的编号值。比如节点 B 的任期编号是 0,当收到来自节点 A 的请求投票消息时,因为消息中包含了节点 A 的任期编号,且编号为 1,那么节点 B 将把自己的任期编号更新为 1。
  3. 当任期编号相同时,日志完整性高的Follower(也就是最后一条日志项对应的任期编号值更大,索引号更大),拒绝投票给日志完整性低的Candidate。

日志复制过程

在 Raft 算法中,副本数据是以日志的形式存在的,Leader接收到来自客户端写请求后,处理写请求的过程就是一个日志复制的过程。

日志项

日志是由日志项组成的,那么日志项究竟是什么样子呢?

日志项是一种数据格式,它主要包含客户端的指令(Command),还有索引值(Log index)、任期编号(Term)等信息:

从上图可以看到,一届领导者的任期中,往往有多条日志项,而且日志项的索引值是连续的,

复制流程

  1. 首先,当Leader节点接收到客户端的写请求后,会创建一个新日志项,并附加到本地日志中;
  2. Leader通过日志复制(AppendEntries)RPC 消息,将日志项复制到集群其它Follower节点上;
  3. 如果Leader接收到大多数的“复制成功”响应后,它将日志项应用到自己的状态机,并返回成功给客户端。如果Leader没有接收到大多数的“复制成功”响应,那么就返回错误给客户端;
  4. 当Follower接收到心跳信息,或者新的AppendEntries消息后,如果发现Leader已经提交了某条日志项,而自己还没应用,那么Follower就会将这条日志项应用到本地的状态机中。

    从上面这个过程可以看出,当Follower节点接受Leader的心跳消息或者AppendEntries消息后,会将日志项应用到自己的状态机。这个优化,降低了处理客户端请求的延迟,将二阶段提交优化为了一段提交,降低了一半的消息延迟。

一致性检查

在 Raft 算法中,Leader通过强制Follower直接复制自己的日志项,处理不一致日志。具体有 2 个步骤:

  1. 首先,Leader通过AppendEntries消息,找到Follower节点上与自己相同日志项的最大索引值。也就是说,这个索引值之前的日志,Leader和Follower是一致的,之后的日志是不一致的了。
  2. 然后,Leader强制Follower更新不一致日志项,实现日志的一致。

举个例子来理解下, 为了方便演示,我们引入 2 个新变量 :

  • PrevLogEntry:表示当前要复制的日志项的前一条日志项的索引值。比如下图中,如果Leader将索引值为8的日志项发送给Follower,那么此时 PrevLogEntry 值为 7;
  • PrevLogTerm:表示当前要复制的日志项的前一条日志项的任期编号。比如下图中,如果Leader将索引值为8的日志项发送给Follower,那么此时 PrevLogTerm 值为 4。
  1. 首先,Leader通过AppendEntries消息,发送当前最新的日志项到Follower,这个日志项的 PrevLogEntry 值为 7,PrevLogTerm 值为 4;
  2. 如果Follower在它的日志中,找不到PrevLogEntry 值为 7、PrevLogTerm 值为 4 的日志项,也就是说它的日志和Leader的不一致了,那么Follower就会拒绝接收新的日志项,并返回失败信息给Leader;
  3. 此时Leader会递减要复制的日志项的索引值,并发送新的日志项到Follower,这个消息的 PrevLogEntry 值为 6,PrevLogTerm 值为 3;
  4. 如果Follower在它的日志中,找到了 PrevLogEntry 值为 6、PrevLogTerm 值为 3 的日志项,那么AppendEntries消息返回成功,这样一来,Leader就知道在 PrevLogEntry 值为 6、PrevLogTerm 值为 3 的位置,Follower的日志项与自己相同;
  5. 最后, Leader通过AppendEntries消息,复制并更新覆盖该索引值之后的日志项(也就是不一致的日志项),最终实现了集群各节点日志的一致。

Raft算法、协议原理详解相关推荐

  1. Nacos如何实现Raft算法与Raft协议原理详解

    前言 大名鼎鼎的Paxos算法可能不少人都听说过,几乎垄断了一致性算法领域,在Raft协议诞生之前,Paxos几乎成了一致性协议的代名词.但是对于大多数人来说,Paxos算法太难以理解了,而且难以实现 ...

  2. 文本分类算法TextCNN原理详解

    详情请看:膜拜大佬![原创]文本分类算法TextCNN原理详解(一) - ModifyBlog - 博客园 Textcnn 原理 与rnn lstm 的比较, Textcnn更快 textCNN的总结 ...

  3. Raft 协议原理详解,10 分钟带你掌握

    之前写了一篇文章<肝了一个月的ETCD,从Raft原理到实践>,干货真的很多,但是无人转载,同事说文章太长了,不方便阅读.那这篇文章,我只选取里面的 Raft 协议,精华提炼,可读性更强! ...

  4. Raft 协议原理详解

    前言 先简单聊聊分布式算法的重要性问题 在分布式微服务系统的项目中,最重要的是 如何选择或者设计合适的算法,来解决一致性问题以及可用性等相关问题 而分布式系统的最关键之处,在于根据场景的需要选择合适的 ...

  5. 机器学习经典算法决策树原理详解(简单易懂)

    ↑ 点击上方[计算机视觉联盟]关注我们 最经典的决策树算法有ID3.C4.5.CART,其中ID3算法是最早被提出的,它可以处理离散属性样本的分类,C4.5和CART算法则可以处理更加复杂的分类问题, ...

  6. QUIC协议原理详解

    1.QUIC是啥? 1.1 什么是QUIC QUIC(Quick UDP Internet Connection)是谷歌推出的一套基于UDP的传输协议,它实现了TCP + HTTPS + HTTP/2 ...

  7. JVM垃圾回收算法与原理详解

    垃圾回收 参考文档 GC参考手册-Java版 理解Java的强引用.软引用.弱引用和虚引用 JVM系列(五) - JVM垃圾回收算法 如何判断对象可以回收 引用计数法 参考文章 Java JVM的引用 ...

  8. FTP文件传输协议原理详解(两种工作模式)

    初始FTP     文件传输协议(File Transfer Protocol,缩写:FTP)是一个用于在计算机网络上在客户端和服务器之间进行文件传输的应用层协议.文件传送(file transfer ...

  9. 目标跟踪算法KCF原理详解

    一直以来没有很想写这个,以为这个东西比较简单,还算是比较容易理解的一个算法,但是在知乎上回答过一个问题之后就有朋友私信我一些关于细节的东西,我一直以为关于细节的东西大家可以自己去理解,大家都是想快速了 ...

最新文章

  1. HDU1051Wooden Sticks
  2. fl out of focus插件_高质量插件之激励器篇
  3. 云服务器怎么拷贝和删除文件,怎样给云服务器拷贝文件
  4. jenkins docker 安装_docker 安装 Jenkins
  5. Vuex与登录状态保存
  6. 朴素贝叶斯分类器python_朴素贝叶斯分类器及Python实现
  7. 小程序 array.map_Array.map解释了4个复杂级别:从5岁到功能程序员。
  8. 【拉普拉斯机制代码实现demo】差分隐私代码实现系列(四)
  9. sperling指标 matlab,sperling指标计算实验报告
  10. android 判断服务是否正在运行,Android 判断某个服务(service)是否运行
  11. 多角度解读优酷土豆合并的深意
  12. spring自动扫描的注解@Component @Controller @Service @Repository
  13. IntelliJ IDEA 2018.2设置背景图片及透明度
  14. Firefox Private Network使用方法(极详细)
  15. layoutit+Bootstrap html页面布局+CSS
  16. qt 导出word中插入图片
  17. android 8三星note8,三星note8和s8哪个好 三星note8和s8对比【详解】
  18. erlang游戏服务器
  19. tar.gz和tar.xz的解压方法及解压命令说明
  20. OUC软件开发实验6

热门文章

  1. badblocks命令来检查硬盘是否有坏道
  2. php使用正则表达式获取域名,PHP正则表达式从url中取得域名
  3. asp.net mvc ajax get读取服务器数据 ,post检测用户名 实例讲解下载
  4. 黄金3:雨露均沾-不要让你的线程在竞争中被“饿死”
  5. 骚操作!三亚00后老板中秋扣员工50%工资代尽孝
  6. Google adsense帐号最新申请方法与心得
  7. 移动端判断iPhone的手机型号
  8. 今天你们的朋友圈是不是被它刷屏了?
  9. HTTP的四种请求方式
  10. vim显示/隐藏行号,永久显示行号