从raft论文出发
介绍
Raft是一种为了管理复制日志的一致性算法。为了提升可理解性,Raft 将一致性算法分解成了几个关键模块,例如领导人选举、日志复制和安全性。同时它通过实施一个更强的一致性来减少需要考虑的状态的数量。同时,raft还提供了集群变更的应对方法。
Raft独有特性:
- 强领导者:日志条目只从领导者发送给其他的服务器
 - 领导选举:Raft 算法使用一个随机计时器来选举领导者
 - 关系调整:Raft 使用一种共同一致的方法来处理集群成员变换的问题
 
Raft一致性算法
Raft 基础
一个 Raft 集群包含若干个服务器节点;通常是 5 个,这允许整个系统容忍 2 个节点的失效。
- 领导者:领导人处理所有的客户端请求(如果一个客户端和跟随者联系,那么跟随者会把请求重定向给领导人)。
 - 跟随者:他们不会发送任何请求,只是简单的响应来自领导者或者候选人的请求。
 - 候选人,选举新领导人时使用
 
一些相关状态
| 状态 | 所有服务器上持久存在的 | 
|---|---|
| currentTerm | 服务器最后一次知道的任期号(初始化为 0,持续递增) | 
| votedFor | 在当前获得选票的候选人的 Id | 
| log[] | 日志条目集;每一个条目包含一个用户状态机执行的指令,和收到时的任期号 | 
| 状态 | 所有服务器上经常变的 | 
|---|---|
| commitIndex | 已知的最大的已经被提交的日志条目的索引值 | 
| lastApplied | 最后被应用到状态机的日志条目索引值(初始化为 0,持续递增) | 
| 状态 | 在领导人里经常改变的 (选举后重新初始化) | 
|---|---|
| nextIndex[] | 对于每一个服务器,需要发送给他的下一个日志条目的索引值(初始化为领导人最后索引值加一) | 
| matchIndex[] | 对于每一个服务器,已经复制给他的日志的最高索引值 | 
所有服务器需遵守的规则
所有服务器:
if(commitIndex > lastApplied){
    那么就 lastApplied 加一,并把log[lastApplied]应用到状态机中;
}
if(T > currentTerm){
     currentTerm 等于 T,并切换状态为跟随者
}
跟随者
- 响应来自候选人和领导者的请求;
 - 如果在超过选举超时时间的情况之前都没有收到领导人的心跳,或者是候选人请求投票的,就自己变成候选人
 
候选人
- 在转变成候选人后就立即开始选举过程
- 自增当前的任期号(currentTerm)
 - 给自己投票
 - 重置选举超时计时器
 - 发送请求投票的 RPC 给其他所有服务器
 
 - 如果接收到大多数服务器的选票,那么就变成领导人
 - 如果接收到来自新的领导人的附加日志 RPC,转变成跟随者
 - 如果选举过程超时,再次发起一轮选举
 
领导人
- 一旦成为领导人:发送空的附加日志 RPC(心跳)给其他所有的服务器;在一定的空余时间之后不停的重复发送,以阻止跟随者超时
 - 如果接收到来自客户端的请求:附加条目到本地日志中,在条目被应用到状态机后响应客户端
 - 如果对于一个跟随者,最后日志条目的索引值大于等于nextIndex,发送从 nextIndex 开始的所有日志条目
- 如果成功:更新相应跟随者的 nextIndex 和 matchIndex
 - 如果因为日志不一致而失败,减少 nextIndex 重试
 
 
领导人选举
如果跟随者一段时间没有接受到Leader的任何消息,开始超时选举。竞选者增加当前term号,向其他节点请求选票。他保持当前状态,直到以下三种情况发生。
- 赢得本次选举
 - 其它服务器赢得选举
 - 选举超时
 
请求投票 RPC
| 参数 | 解释 | 
|---|---|
| term | 候选人的任期号 | 
| candidateId | 请求选票的候选人的 Id | 
| lastLogIndex | 候选人的最后日志条目的索引值 | 
| lastLogTerm | 候选人最后日志条目的任期号 | 
| 返回值 | 解释 | 
|---|---|
| term | 当前任期号,以便于候选人去更新自己的任期号 | 
| voteGranted | 候选人赢得了此张选票时为真 | 
接收者实现:
if(term < currentTerm){
    return false;
}
//先来先投票原则,并且只投票给大多数
if(voteFor为空 || 参与者就是接收者){
    if(候选人的日志和自己的一样新)
        return true;
}
日志复制
日志遵循一下特性:
- 如果在不同的日志中的两个条目拥有相同的索引和任期号,那么他们存储了相同的指令
 - 如果在不同的日志中的两个条目拥有相同的索引和任期号,那么他们之前的所有日志条目也全部相同
 
第一个特性来自这样的一个事实,领导人最多在一个任期里在指定的一个日志索引位置创建一条日志条目,同时日志条目在日志中的位置也从来不会改变。第二个特性由附加日志 RPC 的一个简单的一致性检查所保证。在发送附加日志 RPC 的时候,领导人会把新的日志条目紧接着之前的条目的索引位置和任期号包含在里面。如果跟随者在它的日志中找不到包含相同索引位置和任期号的条目,那么他就会拒绝接收新的日志条目。
附加日志 RPC
由领导人负责调用来复制日志指令;也会用作heartbeat
| 参数 | 解释 | 
|---|---|
| term | 领导人的任期号 | 
| leaderId | 领导人的 Id,以便于跟随者重定向请求 | 
| prevLogIndex | 新的日志条目紧随之前的索引值 | 
| prevLogTerm | prevLogIndex 条目的任期号 | 
| entries[] | 准备存储的日志条目(表示心跳时为空;一次性发送多个是为了提高效率) | 
| leaderCommit | 领导人已经提交的日志的索引值 | 
| 返回值 | 解释 | 
|---|---|
| term | 当前的任期号,用于领导人去更新自己 | 
| success | 跟随者包含了匹配上 prevLogIndex 和 prevLogTerm 的日志时为真 | 
//接收者实现:
//Leader肯定有最大的term
if (term < currentTerm)
    return false;
if(prevLogIndex位置上的term和prevLogTerm不匹配){
    删除这一条之后所有的日志;
    return false;
}
附加任何在已有的日志中不存在的条目;
if(leaderCommit > commitIndex){
    leaderCommit =  min(leaderCommit, 新日志条目索引值);
}
return true;
安全性
选举限制
Raft 使用投票的方式来阻止候选人赢得选举除非这个候选人包含了所有已经提交的日志条目。
- 如果两份日志最后的条目的任期号不同,那么任期号大的日志更加新。
 - 如果两份日志最后的条目任期号相同,那么日志比较长的那个就更加新。
 
提交之前任期内的日志条目
主要是对commit的定义:只有它自己提交当前term号的操作才能将之前的日志看作是真正的可以提交。
一个领导人不能断定一个之前任期里的日志条目被保存到大多数服务器上的时候就一定已经提交了。
- (a) 中,S1 是领导者,部分的复制了索引位置 2 的日志条目。
 - (b) 中,S1 崩溃了,然后 S5 在任期 3 里通过 S3、S4 和自己的选票赢得选举,然后从客户端接收了一条不一样的日志条目放在了索引 2 处。
 - (c),S5 又崩溃了;S1 重新启动,选举成功,开始复制日志。在这时,来自任期 2 的那条日志已经被复制到了集群中的大多数机器上,但是还没有被提交。
 - 如果 S1 在 (d) 中又崩溃了,S5 可以重新被选举成功(通过来自 S2,S3 和 S4 的选票),然后覆盖了他们在索引 2 处的日志。但是,在崩溃之前,如果 S1 在自己的任期里复制了日志条目到大多数机器上
 - 如 (e) 中,然后这个条目就会被提交(S5 就不可能选举成功)。 在这个时候,之前的所有日志就会被正常提交处理。
 
图中展示了一种情况,一条已经被存储到大多数节点上的未提交的老日志条目(这不是废话吗?),也依然有可能会被未来的领导人覆盖掉。
只有它自己提交当前term的操作才能看作是真正的提交。(leader只有提交了当前term号的日志后才能将之前的日志应用到状态机)
成员组变更
成员组变更的难点
- 成员组正常Paxos日志同步服务不中断
 - 任何情况下宕机都能够保证存活的多数派成员间能够选举leader
 - 不会出现1个以上的多数派选出大于1个leader的情况
 
基本思路
- “旧朝代”的多数派成员对“旧朝代结束”这件事达成一致,达成一致后旧成员组不再投票
 - “新朝代”的多数派成员对“新朝代开启”这件事达成一致,达成一致后新成员组开始投票
 
上面的思路可以满足难点3,但是不能满足难点1,2。比如Pa执行成功后,在Pb执行成功之前:没有成员组可以投票,服务会中断;如果集群宕机重启,新的成员组的各个成员由于还未对新成员组达成一致,而无法选出leader。
Joint-Consensus
通用成员组变更方法--Joint-Consensus
- 变更操作
 
- 成员变更操作前,C(old)的多数派中持久化的成员组为[[C(old)]]
 - 成员变更操作由leader执行,leader收到命令后,将成员组[[C(old),C(new)]]发送给C(old)∪C(new)的所有成员,在此之后新的日志同步需要保证得到C(old)和C(new)两个多数派的确认
 - leader收到C(old)和C(new)两个多数派确认后,将成员组[[C(new)]]发送给C(new)的所有成员,收到C(new)多数派确认后,表示成员变更成功,后续的日志只要得到C(new)多数派确认即可
 
- 投票规则
 
- 持有[[C(old),C(new)]]的候选人要得到C(old)和C(new)两个多数派都确认,才能当选leader
 - 持有[[C(old)]]的候选人要得到C(old)多数派确认,才能当选leader
 - 持有[[C(new)]]的候选人要得到C(new)多数派确认,才能当选leader
 
总结
- 领导者选举
 - 普通附加操作
 - 安全性和一致性
 - 平衡旧领导者:出现网络分区,通过Term号使旧领导者变为follower
 - 客户端交互:只有leader和客户端交互
 - 更改配置
 
从raft论文出发的更多相关文章
- 【转】分布式一致性算法:Raft 算法(Raft 论文翻译)
		
编者按:这篇文章来自简书的一个位博主Jeffbond,读了好几遍,翻译的质量比较高,原文链接:分布式一致性算法:Raft 算法(Raft 论文翻译),版权一切归原译者. 同时,第6部分的集群成员变更读 ...
 - raft 论文
		
raft 论文,摘自 http://www.infoq.com/cn/articles/raft-paper raft动画:https://raft.github.io/ raft说明动画:
 - Raft论文《 In Search of an Understandable Consensus Algorithm (Extended Version) 》研读
		
Raft 论文研读 说明:本文为论文 < In Search of an Understandable Consensus Algorithm (Extended Version) > 的 ...
 - Raft论文的一些问题
		
抛些问题出来,真正解释了这些问题才算理解了论文.:) 1. 什么是复制状态机 2. Raft vs Paxos 3. Raft的设计目标understandability,为达到设计目标在做设计时如何 ...
 - Raft论文学习笔记
		
先附上论文链接 https://pdos.csail.mit.edu/6.824/papers/raft-extended.pdf 最近在自学MIT的6.824分布式课程,找到两个比较好的githu ...
 - Raft论文概述
		
介绍 Raft是一种为了管理复制日志的一致性算法.为了提升可理解性,Raft 将一致性算法分解成了几个关键模块,例如领导人选举.日志复制和安全性.同时它通过实施一个更强的一致性来减少需要考虑的状态的数 ...
 - Raft与MongoDB复制集协议比较
		
在一文搞懂raft算法一文中,从raft论文出发,详细介绍了raft的工作流程以及对特殊情况的处理.但算法.协议这种偏抽象的东西,仅仅看论文还是比较难以掌握的,需要看看在工业界的具体实现.本文关注Mo ...
 - 分布式一致性算法:Raft 算法(论文翻译)
		
Raft 算法是可以用来替代 Paxos 算法的分布式一致性算法,而且 raft 算法比 Paxos 算法更易懂且更容易实现.本文对 raft 论文进行翻译,希望能有助于读者更方便地理解 raft 的 ...
 - 解读Raft(二 选举和日志复制)
		
Leader election Raft采用心跳机制来触发Leader选举.Leader周期性的发送心跳(如果有正常的RPC的请求情况下可以不发心跳)包保持自己Leader的角色(避免集群中其他节点认 ...
 
随机推荐
- 基于Maven的S2SH(Struts2+Spring+Hibernate)框架搭建
			
1. 前言 基于Maven的开发方式开发项目已经成为主流.Maven能很好的对项目的层次及依赖关系进行管理.方便的解决大型项目中复杂的依赖关系.S2SH(Struts2+Spring+Hibernat ...
 - 抱SQL SERVER大腿之我爱用视图(对大数据量的管理)
			
我们拥有一个巨大的表,两千多万条记录.也许在行家眼里,两千多万条记录顶多算条毛,不过这条毛也忒粗壮了一点:我们的数据库占用的空间已经达到5G多了.不要以为是日志文件在搞鬼,日志文件可以自动收缩的,最多 ...
 - asp.net mvc 在JS中跳转到其它controller/action
			
平时在ASP.NET 中经常这样写, $('#loginOut').click(function() { $.messager.confirm('系统提示', '您确定要退出本次登 ...
 - 实现EventHandler的监测
			
的监测", "category":"", "tags":"", "publish":&qu ...
 - Spring Boot 2 实践记录之 使用 ConfigurationProperties 注解将配置属性匹配至配置类的属性
			
在 Spring Boot 2 实践记录之 条件装配 一文中,曾经使用 Condition 类的 ConditionContext 参数获取了配置文件中的配置属性.但那是因为 Spring 提供了将上 ...
 - 用.netcore写一个简单redis驱动,调试windows版本的redis.平且给set和get命令添加参数.
			
1. 下载windows版本的redis 2.开发环境vs2017 新建一个 .net core控制台. private static Socket socket = new Socket(Addr ...
 - Redis的认识和基本操作
			
Redis是什么 Redis 是一个高性能的开源的.C语言写的Nosql(非关系型数据库),数据保存在内存中. Redis 是以key-value形式存储的Nosql,和传统的关系型数据库不一样.不一 ...
 - css字体中英速查表
			
例1(小米米官网):font-family: "Arial","Microsoft YaHei","黑体","宋体",s ...
 - HTML+Javascript制作拼图小游戏详解(终)
			
上次我们已经讲解了制作的原理,并且展示了主要代码. 这次我将完整的代码给大家,仅供参考. HTML部分如下: <!DOCTYPE html> <html lang="en& ...
 - Swift 里字符串(九)UTF16View
			
即以 UTF16 编码的格式来查看字符串. UTF16View 是一个结构体 @_fixed_layout public struct UTF16View { @usableFromInline in ...