由浅入深理解Raft协议

2017-10-16 12:12操作系统/设计

0 - Raft协议和Paxos的因缘

读过Raft论文《In Search of an Understandable Consensus Algorithm》的同学都知道,Raft是因为Paxos而产生的。Paxos协议是出了名的难懂,而且不够详细,紧紧依据Paxos这篇论文开发出可用的系统是非常困难的。Raft的作者也说是被Paxos苦虐了无数个回合后,才设计出了Raft协议。作者的目标是设计一个足够详细并且简单易懂的“Paxos协议”,让开发人员可以在很短的时间内开发出一个可用的系统。

Raft协议在功能上是完全等同于(Multi)-Paxos协议的。Raft也是一个原子广播协议(原子广播协议参见《由浅入深理解Paxos协议(1)》),它在分布式系统中的功能以及使用方法和Paxos是完全一样的。我们可以用Raft来替代分布式系统中的Paxos协议如下图所示:

1 - Raft的设计理念

严格来说Raft并不属于Paxos的一个变种。Raft协议并不是对Paxos的改进,也没有使用Paxos的基础协议(The Basic Protocol)。Raft协议在设计理念上和Paxos协议是完全相反的。正是由于这个完全不同的理念,使得Raft协议变得简单起来。

Paxos协议中有一个基本的假设前提:可能会同时有多个Leader存在。这里把Paxos协议执行的过程分为以下两个部分:

  • Leader选举

  • 数据广播

在《由浅入深理解Paxos协议(2)》的“Leader的选取”一节中提到过,Paxos协议并没有给出详细的Leader选举机制。Paxos对于Leader的选举没有限制,用户可以自己定义。这是因为Paxos协议设计了一个巧妙的数据广播过程,即Paxos的基本通讯协议(The Basic Protocol)。它有很强的数据一致性保障,即使在多个Leader同时出现时也能够保证广播数据的一致性。

而Raft协议走了完全相反的一个思路:保证不会同时有多个Leader存在。因此Raft协议对Leader的选举做了详细的设计,从而保证不会有多个Leader同时存在。相反,数据广播的过程则变的简单易于理解了。

2 - Raft的日志广播过程

为了保证数据被复制到多数的节点上,Raft的广播过程尽管简单仍然要使用多数派协议,只是这个过程要容易理解的多:

  1. 发送日志到所有Followers(Raft中将非Leader节点称为Follower)。

  2. Followers收到日志后,应答收到日志。

  3. 当半数以上的Followers应答后,Leader通知Followers日志广播成功。

- 日志和日志队列

Raft将用户数据称作日志(Log),存储在一个日志队列里。每个节点上都有一份。队列里的每个日志都一个序号,这个序号是连续递增的不能有缺。

日志队列里有一个重要的位置叫做提交日志的位置(Commit Index)。将日志队列里的日志分为了两个部分:

  • 已提交日志:已经复制到超过半数节点的数据。这些日志是可以发送给应用程序去执行的日志。

  • 未提交日志:还未复制到超过半数节点的数据。

当Followers收到日志后,将日志按顺序存储到队列里。但这时Commit Index不会更新,因此这些日志是未提交的日志,不能发送给应用去执行。当Leader收到超过半数的Followers的应答后,会更新自己的Commit Index,并将Commit Index广播到Followers上。这时Followers更新Commit Index,未提交的日志就变成了已提交的日志,可以发送给应用程序去执行了。

从上面的解释我们可以知道,日志队列中已经提交的日志是不可改变的,而未提交的日志则可以被更新成其他的日志(在Leader发生变化时会发生)。

Raft的日志队列和《》中的“预存储队列+存储队列”功能是一样的,但是巧妙的合并到了一起。这样做解决的问题和中“预存储队列+存储队列”解决的问题也是一样的,这里就不再叙述。

3 - Raft的Leader选举

Raft称它的Leader为“Strong Leader”。Strong Leader 有以下特点:

  • 同一时间只有一个Leader

  • 只能从Leader向Followers发送数据,反之不行。

下面我们看一下Raft通过哪些机制来实现Strong Leader。

- 多数派协议

为了保证只有一个Leader被选举出来,选举的过程使用了多数派协议。这样很好理解,当一个Candidate(申请成为Leader的节点)请求成为Leader时,只有半数以上的Followers同意后,才能成为Leader。投票过程如下:

  1. 当发现Leader无响应后(一段时间内没有日志或心跳),Candidate发送投票请求。

  2. Followers投票。

  3. 如果超过半数的Followers投了票,则Candidate自动变成Leader,开始广播日志。

- 随机超时机制

和《由浅入深理解Paxos协议(1)》中提到问题一样,这里也会发生多个Candidate同时发送投票请求,而导致谁都不能够得到多数赞成票的情况,有可能永远也选不出Leader。为了保证Leader选举的效率,Raft在投票选举中使用了随机超时的机制:

  1. 在每个Followers上设定的Leader超时时间是在一个范围内随机的。这样可以尽量让Followers不在同一时间发起Leader选举。

  2. 每个Candidate发起投票后,如果在一段时间内没有任何Candidate称为Leader则,需要重新发起Leader选举。这段等待的时间,在每个Candidate上也是随机的。从而保证不会有多个Candidate同时重新发起Leader选举。

虽说是随机的超时时间,但是也有个范围,太小或者太大都会影响系统的可用性。太小会导致过多的选举冲突,太大又会影响系统的平滑运行。在Raft的论文中,作者将这个超时时间称为electionTimeout,并给出了合理的范围,公式如下:

broadcastTime ≪ electionTimeout ≪ MTBF

“≪”代表数量级上的差异(10倍以上)。

- Candidate的日志长度要等于或者超过半数节点才能选为Leader

当Leader故障时,Followers上日志的状态很可能是不一致的。有的多有的少,而且Commit Index也不尽相同。

我们知道已经提交的日志是不能够丢弃的,必须要最终复制到所有的节点上才行。假如在选Leader时,图中Candidate A变成了Leader,就必须要首先从Candidate B上将日志4复制过来,然后才能开始处理新的日志。为了减少复杂性,raft就规定,只有包含了所有已提交日志的Candidate才能当选为Leader。

实现也很简单:

  • 当发现Leader无响应后(一段时间内没有数据或心跳),Candidate发送投票请求,请求中包含自己日志队列的长度(或者说最大日志的Index)。

  • Followers检查Candidate的日志长度,只有Candidate的日志等于或者长于自己才投票。

  • 如果超过半数的Followers投了票,则Candidate自动变成Leader,开始广播数据。

因为已经提交的日志一定被复制到了多数节点上,所以日志长度等于或者长于多数节点的Candidate一定包含了所有已经提交的日志。

为什么不是检查Commit Index?

因为Leader故障时,很有可能只有Leader的Commit Index是最大的。

如果图中的Candidate A被选举为Leader,那么日志4就会被丢弃。但是日志4已经在原来的Leader上提交了,因此必须被保留才行。所以只能让日志长度更长的Candidate B选为Leader。这种做法有可能把原来Leader没广播完成的日志(图中的日志5)接着广播完成,这没有什么关系。

- Followers日志补齐

当Leader故障时,Followers上的日志状态是不一样的,有长有短。因此新的Leader选出后,首先要将所有Followers的日志补齐才行。因此Leader要询问Followers的日志长度,从最小的日志位置开始补齐。

- Followers未提交日志的更新

新Leader的日志一定包含所有已经提交的日志。但新Leader的日志不一定是最长的,那些新Leader没有的日志,一定是未提交的日志,因此可以被更新,没有关系的。Leader只需要从自己的当前位置开始插入日志并广播出去就可以了。Followers会用新的日志去更新指定位置上的日志。

4 - 新旧Leader的交替

新的Leader选出后,开始广播日志。这时如果旧的Leader故障恢复了(比如网络临时中断),并且还认为自己是Leader,也会广播日志。这不就导致了同时有两个Leader出现吗?是的,Raft也没办法让旧的Leader不发日志,但是Raft有办法让Followers拒绝旧Leader的日志。

- Term

Raft将时间划分为连续的时间段,称为Term。 Term是指从一次Leader选举开始到下一次Leader选举的一段时间。这段时间内只能有一个Leader被选举成功,并负责管理系统或者没有Leader选出。

Raft论文上的Term图片

每个Term都有一个唯一的数字编号。所有Term的数字编号是从小到大连续排列的。

- 作废旧Leader

Term编号在作废旧Leader的过程中至关重要,但却十分简单。过程如下:

  1. 发送日志到所有Followers,Leader的Term编号随日志一起发送。

  2. Followers收到日志后,检查Leader的Term编号。如果Leader的Term编号等于或者大于自己的当前Term(Current Term)编号,则存储日志到队列并且应答收到日志。否则发送失败消息给Leader,消息中包含自己的当前Term编号。

  3. 当Leader收到任何Term编号比自己的Term编号大的消息时,则将自己变成Follower。收到的消息包括:Follower给自己的回复消息、新Leader的日志广播消息、Leader的选举消息。

- Raft的实现

论文中作者仅用了两个RPC就实现了Raft的功能,它们分别是:

  • RequestVote() Candidate发起的投票请求

  • AppendEntries() 将日志广播到Followers上

AppendEntries()除了广播日志外,作者还巧妙的用它实现了以下的功能:

  • 发送心跳(heartbeat): 没有客户日志时,通过AppendEntries()广播空日志,当做心跳。

  • 发送Commit Index:当Commit Index更新后,可以随着当前的日志通过AppendEntries()广播到Followers上。如果没有客户端日志,则可以随着心跳广播出去。

欢迎订阅我的公众号"MySQL代码研究",这个公众号里的文章,多数是我在开发过程中学习、研究MySQL代码和相关技术的总结和笔记,希望会对你有帮助。返回搜狐,查看更多

[搜狐科技]由浅入深理解Raft协议的更多相关文章

  1. MySQL中间件方案盘点_搜狐科技_搜狐网

    MySQL中间件方案盘点_搜狐科技_搜狐网

  2. 理解Raft协议

    目录 1.Paxos算法存在的问题 2.Raft算法     2.1 复制状态机     2.2. Raft算法     2.2.1 安全性问题     2.2.2 Leader选举     2.2. ...

  3. 李善友《认知升级之第一性原理》--507张PPT全解!_搜狐科技_搜狐网

      http://www.sohu.com/a/151470602_733114

  4. 理解分布式一致性:Raft协议

    理解分布式一致性:Raft协议 什么是分布式一致性 Leader选举 日志复制流程 term选举周期 timeout 选举和选举timeout 选举分裂 日志复制和心跳timeout 在分布式系统中, ...

  5. Raft协议理解

    raft协议最关键的部分是领导选举和日志复制 日志复制 日志匹配原则:如果两个日志在相同索引位置的entry的任期号相同,那么这两个日志从头到这个索引位置之前完全相同. 日志匹配原则可以解释为如下两条 ...

  6. 山寨Unity3D?搜狐畅游的免费开源游戏引擎Genesis-3D

    在CSDN上看到了<搜狐畅游发布3D游戏引擎Genesis-3D 基于MIT协议开源>(http://www.csdn.net/article/2013-11-21/2817585-cha ...

  7. 剑指Offer——搜狐畅游笔试题+知识点总结

    剑指Offer--搜狐畅游笔试题+知识点总结 情景回顾 时间:2016.9.24 10:00-12:00 地点:山东省网络环境智能计算技术重点实验室 事件:搜狐畅游笔试   注意事项:要有大局观,该舍 ...

  8. 利用jieba,word2vec,LR进行搜狐新闻文本分类

    一.简介 1)jieba 中文叫做结巴,是一款中文分词工具,https://github.com/fxsjy/jieba 2)word2vec 单词向量化工具,https://radimrehurek ...

  9. MIT-6.824 Raft协议

    摘要 raft是一种比paxos容易理解的一致性算法,实现起来比paxos简单许多.本文前部分描述算法的细节,后部分尝试探讨下该算法的原理. 算法描述 raft算法之所以简单的原因之一是它将问题分解成 ...

随机推荐

  1. shiro实战系列(八)之安全管理器

    Apache Shiro 提供安全框架界独一无二的东西:一个完整的企业级 Session 解决方案,从最简单的命令行及智能手机 应用到最大的集群企业 Web 应用程序.   这对许多应用有着很大的影响 ...

  2. 理解C#的Lock语法意义

    一. 为什么要lock,lock了什么? 当我们使用线程的时候,效率最高的方式当然是异步,即各个线程同时运行,其间不相互依赖和等待.但当不同的线程都需要访问某个资源的时候,就需要同步机制了,也就是说当 ...

  3. 安装的 Linux 软件包有多少?

    导读 你有没有想过你的 Linux 系统上安装了几千个软件包? 是的,我说的是“千”. 即使是相当一般的 Linux 系统也可能安装了上千个软件包. 有很多方法可以获得这些包到底是什么包的详细信息. ...

  4. Java并发(一)并发编程的挑战

    目录 一.上下文切换 1. 多线程一定快吗 2. 测试上下文切换次数和时长 3. 如何减少上下文切换 4. 减少上下文切换实战 二.死锁 三.资源限制的挑战 四.本章小结 并发编程的目的是为了让程序运 ...

  5. P3195 [HNOI2008]玩具装箱TOY

    列出DP方程式:设f[i]表示分组完前i件物品的最小花费,为方便计算,设sum[i]表示是前i件物品的长度和. f[i]=min(f[j]+(sum[i]-sum[j]+i-j-L-1)^2) [0& ...

  6. C. Greedy Arkady

    kk people want to split nn candies between them. Each candy should be given to exactly one of them o ...

  7. TortoiseSVN 只取下或更新部分文件的方法(Sparse Update/Sparse Checkout)

    Sparse Update/Sparse Checkout   To easily select only the items you want for the checkout and force ...

  8. How develop BigData Project in Visual Studio

  9. Tomcat端口被占用解决方案

    Tomcat端口被占用解决方法 1.在dos下,输入 netstat -ano|findstr 8080 //说明:查看占用8080端口的进程,显示占用端口的进程 2.taskkill /pid 19 ...

  10. Ansible入门笔记(1)之工作架构和使用原理

    目录 Ansible入门笔记(1) 1.Ansible特性 2.ansible架构解析 3.ansible主要组成部分 1)命令执行来源: 2)利用ansible实现管理的方式 3)Ansile-pl ...