介绍

Raft是一种为了管理复制日志的一致性算法。为了提升可理解性,Raft 将一致性算法分解成了几个关键模块,例如领导人选举、日志复制和安全性。同时它通过实施一个更强的一致性来减少需要考虑的状态的数量。同时,raft还提供了集群变更的应对方法。

Raft独有特性:

  1. 强领导者:日志条目只从领导者发送给其他的服务器
  2. 领导选举:Raft 算法使用一个随机计时器来选举领导者
  3. 关系调整: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,并切换状态为跟随者
}

跟随者

  1. 响应来自候选人和领导者的请求;
  2. 如果在超过选举超时时间的情况之前都没有收到领导人的心跳,或者是候选人请求投票的,就自己变成候选人

候选人

  1. 在转变成候选人后就立即开始选举过程
    1. 自增当前的任期号(currentTerm)
    2. 给自己投票
    3. 重置选举超时计时器
    4. 发送请求投票的 RPC 给其他所有服务器
  2. 如果接收到大多数服务器的选票,那么就变成领导人
  3. 如果接收到来自新的领导人的附加日志 RPC,转变成跟随者
  4. 如果选举过程超时,再次发起一轮选举

领导人

  1. 一旦成为领导人:发送空的附加日志 RPC(心跳)给其他所有的服务器;在一定的空余时间之后不停的重复发送,以阻止跟随者超时
  2. 如果接收到来自客户端的请求:附加条目到本地日志中,在条目被应用到状态机后响应客户端
  3. 如果对于一个跟随者,最后日志条目的索引值大于等于nextIndex,发送从 nextIndex 开始的所有日志条目
    1. 如果成功:更新相应跟随者的 nextIndex 和 matchIndex
    2. 如果因为日志不一致而失败,减少 nextIndex 重试

领导人选举

如果跟随者一段时间没有接受到Leader的任何消息,开始超时选举。竞选者增加当前term号,向其他节点请求选票。他保持当前状态,直到以下三种情况发生。

  1. 赢得本次选举
  2. 其它服务器赢得选举
  3. 选举超时

请求投票 RPC

参数 解释
term 候选人的任期号
candidateId 请求选票的候选人的 Id
lastLogIndex 候选人的最后日志条目的索引值
lastLogTerm 候选人最后日志条目的任期号
返回值 解释
term 当前任期号,以便于候选人去更新自己的任期号
voteGranted 候选人赢得了此张选票时为真

接收者实现:

if(term < currentTerm){
return false;
}
//先来先投票原则,并且只投票给大多数
if(voteFor为空 || 参与者就是接收者){
if(候选人的日志和自己的一样新)
return true;
}

日志复制

日志遵循一下特性:

  1. 如果在不同的日志中的两个条目拥有相同的索引和任期号,那么他们存储了相同的指令
  2. 如果在不同的日志中的两个条目拥有相同的索引和任期号,那么他们之前的所有日志条目也全部相同

第一个特性来自这样的一个事实,领导人最多在一个任期里在指定的一个日志索引位置创建一条日志条目,同时日志条目在日志中的位置也从来不会改变。第二个特性由附加日志 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 使用投票的方式来阻止候选人赢得选举除非这个候选人包含了所有已经提交的日志条目。

  1. 如果两份日志最后的条目的任期号不同,那么任期号大的日志更加新。
  2. 如果两份日志最后的条目任期号相同,那么日志比较长的那个就更加新。

提交之前任期内的日志条目

主要是对commit的定义:只有它自己提交当前term号的操作才能将之前的日志看作是真正的可以提交。

一个领导人不能断定一个之前任期里的日志条目被保存到大多数服务器上的时候就一定已经提交了。

  1. (a) 中,S1 是领导者,部分的复制了索引位置 2 的日志条目。
  2. (b) 中,S1 崩溃了,然后 S5 在任期 3 里通过 S3、S4 和自己的选票赢得选举,然后从客户端接收了一条不一样的日志条目放在了索引 2 处。
  3. (c),S5 又崩溃了;S1 重新启动,选举成功,开始复制日志。在这时,来自任期 2 的那条日志已经被复制到了集群中的大多数机器上,但是还没有被提交。
  4. 如果 S1 在 (d) 中又崩溃了,S5 可以重新被选举成功(通过来自 S2,S3 和 S4 的选票),然后覆盖了他们在索引 2 处的日志。但是,在崩溃之前,如果 S1 在自己的任期里复制了日志条目到大多数机器上
  5. 如 (e) 中,然后这个条目就会被提交(S5 就不可能选举成功)。 在这个时候,之前的所有日志就会被正常提交处理。

图中展示了一种情况,一条已经被存储到大多数节点上的未提交的老日志条目(这不是废话吗?),也依然有可能会被未来的领导人覆盖掉。

只有它自己提交当前term的操作才能看作是真正的提交。(leader只有提交了当前term号的日志后才能将之前的日志应用到状态机)

成员组变更

成员组变更的难点

  1. 成员组正常Paxos日志同步服务不中断
  2. 任何情况下宕机都能够保证存活的多数派成员间能够选举leader
  3. 不会出现1个以上的多数派选出大于1个leader的情况

基本思路

  1. “旧朝代”的多数派成员对“旧朝代结束”这件事达成一致,达成一致后旧成员组不再投票
  2. “新朝代”的多数派成员对“新朝代开启”这件事达成一致,达成一致后新成员组开始投票

上面的思路可以满足难点3,但是不能满足难点1,2。比如Pa执行成功后,在Pb执行成功之前:没有成员组可以投票,服务会中断;如果集群宕机重启,新的成员组的各个成员由于还未对新成员组达成一致,而无法选出leader。

Joint-Consensus

通用成员组变更方法--Joint-Consensus

  • 变更操作
  1. 成员变更操作前,C(old)的多数派中持久化的成员组为[[C(old)]]
  2. 成员变更操作由leader执行,leader收到命令后,将成员组[[C(old),C(new)]]发送给C(old)∪C(new)的所有成员,在此之后新的日志同步需要保证得到C(old)和C(new)两个多数派的确认
  3. leader收到C(old)和C(new)两个多数派确认后,将成员组[[C(new)]]发送给C(new)的所有成员,收到C(new)多数派确认后,表示成员变更成功,后续的日志只要得到C(new)多数派确认即可
  • 投票规则
  1. 持有[[C(old),C(new)]]的候选人要得到C(old)和C(new)两个多数派都确认,才能当选leader
  2. 持有[[C(old)]]的候选人要得到C(old)多数派确认,才能当选leader
  3. 持有[[C(new)]]的候选人要得到C(new)多数派确认,才能当选leader

总结

  1. 领导者选举
  2. 普通附加操作
  3. 安全性和一致性
  4. 平衡旧领导者:出现网络分区,通过Term号使旧领导者变为follower
  5. 客户端交互:只有leader和客户端交互
  6. 更改配置

Raft论文概述的更多相关文章

  1. 【转】分布式一致性算法:Raft 算法(Raft 论文翻译)

    编者按:这篇文章来自简书的一个位博主Jeffbond,读了好几遍,翻译的质量比较高,原文链接:分布式一致性算法:Raft 算法(Raft 论文翻译),版权一切归原译者. 同时,第6部分的集群成员变更读 ...

  2. raft 论文

    raft 论文,摘自  http://www.infoq.com/cn/articles/raft-paper raft动画:https://raft.github.io/ raft说明动画:

  3. Raft论文《 In Search of an Understandable Consensus Algorithm (Extended Version) 》研读

    Raft 论文研读 说明:本文为论文 < In Search of an Understandable Consensus Algorithm (Extended Version) > 的 ...

  4. Raft论文的一些问题

    抛些问题出来,真正解释了这些问题才算理解了论文.:) 1. 什么是复制状态机 2. Raft vs Paxos 3. Raft的设计目标understandability,为达到设计目标在做设计时如何 ...

  5. Raft论文学习笔记

    先附上论文链接  https://pdos.csail.mit.edu/6.824/papers/raft-extended.pdf 最近在自学MIT的6.824分布式课程,找到两个比较好的githu ...

  6. 从raft论文出发

    介绍 Raft是一种为了管理复制日志的一致性算法.为了提升可理解性,Raft 将一致性算法分解成了几个关键模块,例如领导人选举.日志复制和安全性.同时它通过实施一个更强的一致性来减少需要考虑的状态的数 ...

  7. 分布式一致性算法:Raft 算法(论文翻译)

    Raft 算法是可以用来替代 Paxos 算法的分布式一致性算法,而且 raft 算法比 Paxos 算法更易懂且更容易实现.本文对 raft 论文进行翻译,希望能有助于读者更方便地理解 raft 的 ...

  8. Raft概述

    Raft 1. 概述 Raft是一种一致性(共识)算法,相比Paxos,Raft更容易理解和实现,它将分布式一致性问题分解成多个子问题,Leader选举(Leader election).日志复制(L ...

  9. 学习Raft算法的笔记

    Raft是一种为了管理日志复制的一致性算法.它提供了和Paxos算法相同的功能和性能,但是它的算法结构和Paxos不同,使得Raft算法更加容易理解并且更容易构建实际的系统.为了提升可理解性,Raft ...

随机推荐

  1. VSCode + PicGo + Github + jsDelivr 搭建稳定快速高效图床

    VSCode + PicGo + Github + jsDelivr 搭建稳定快速高效图床 目录 前言 准备 配置 验证 前言 所谓图床,就是将图片储存到第三方静态资源库中,其返回给你一个 URL 进 ...

  2. Parallel NetCDF 简介

    Parallel NetCDF API 所有C接口前加ncmpi前缀,Fortran接口前加nfmpi前缀 函数返回整数 NetCDF 状态变量 1. Variable and Parameter T ...

  3. 拒绝恶意同构ssh登陆服务器脚本

    #!/bin/bash #Deny specified IP access #IP:who is fail to login sever SECURE_LOG=/var/log/secure #通过s ...

  4. Shell中 ##%% 操作变量名

    在linxu平台下少不了对变量名的处理,今天记录下shell中 ##%% 对变量名的操作. #操作左侧,%操作右侧. #号处理方式: 对于单个#,处理对象为变量中指定的第一个符号左侧字符串, 对于两个 ...

  5. mongodb存储的基本使用

    Python连接mongodb一般使用pymongo模块 1. pymongo模块的简单使用 ### MongoDB存储 ## 连接MongoDB import pymongo # 建立连接对象,2种 ...

  6. lsof之列出已打开的文件

    lsof命令常用解析 Linux中常用 lsof 来查看文件调用进程等相关信息,也可用来查看活跃的进程信息和端口监听进程信息等 1. lsof 命令介绍 NAME lsof - list open f ...

  7. 学习Java的第十八天

    一.今日收获 1.java完全学习手册第三章算法的3.1比较值 2.看哔哩哔哩上的教学视频 二.今日问题 1.在第一个最大值程序运行时经常报错. 2.哔哩哔哩教学视频的一些术语不太理解,还需要了解 三 ...

  8. 巩固javaweb第十六天

    巩固内容: 下拉框 在注册功能中,地区的选择使用了下拉框,可以从地区选项中选择一个地区.在这个 例子中,只允许选择一个,而在有些情况下,下拉框可以进行多选.所以,从功能上来说, 下拉框具有单选按钮和复 ...

  9. 学习java 7.6

    学习内容: 方法重写注意事项:子类不能重写父类的私有方法 子类的访问权限不比父类的低(父类默认,子类可以是默认也可以是public) java中继承的注意事项:java中类只支持单继承,java中类支 ...

  10. adverb

    An adverb is a word or an expression that modifies a verb, adjective, another adverb, determiner [限定 ...