分布式一致性协议Raft介绍

分布式一致性协议Raft

分布式共识算法

  在介绍Raft之前,先了解一下分布式共识算法的基本概念,主要是分布式一致性和一致性状态机。

分布式一致性

  分布式一致性(distributed consensus) 是分布式系统中最基本的问题, 用来保证一个分布式系统的可靠性以及容灾能力。简单的来讲,就是如何在多个机器间对某一个值达成一致,并且当达成一致之后,无论之后这些机器间发生怎样的故障,这个值能保持不变。
  抽象定义上, 一个分布式系统里的所有进程要确定一个值v,如果这个系统满足如下几个性质, 就可以认为它解决了分布式一致性问题, 分别是:
  Termination: 所有正常的进程都会决定v具体的值。
  Validity: 任何正常的进程确定的值v’, 那么v’肯定是某个进程提交的。
  Agreement: 所有正常的进程选择的值都是一样的。

一致性状态机

  对于一个无限增长的序列 a[1, 2, 3…], 如果对于任意整数 i, a[i] 的值满足分布式一致性,这个系统就满足一致性状态机的要求。基本上所有的系统都会有源源不断的操作, 这时候单独对某个特定的值达成一致是不够的。为了真实系统保证所有的副本的一致性,通常会把操作转化为 write-ahead-log(简称WAL)。然后让系统的所有副本对WAL保持一致,这样每个进程按照顺序执行WAL里的操作,就能保证最终的状态是一致的。
在这里插入图片描述

如何理解分布式共识算法?

  多个参与者某一件事一致 :一件事,一个结论
  已达成一致的结论,不可推翻

有哪些分布式共识算法?

  Paxos:被认为是分布式共识算法的根本,其他都是其变种,但是 Paxos 论文中只给出了单个提案的过程,并没有给出复制状态机中需要的 multi-paxos 的相关细节的描述,实现 Paxos 具有很高的工程复杂度(如多点可写,允许日志空洞等)。
  Zab:被应用在 Zookeeper 中,业界使用广泛,但没有抽象成通用的 library。
  Raft:以容易理解著称,业界也涌现出很多 Raft 实现,比如大名鼎鼎的 etcd, braft, tikv 等。

Raft算法介绍

CAP定理

  在正式介绍raft协议之前,先了解一下分布式系统中的一个基本定理,CAP定理。
  CAP定理又被称作布鲁尔定理(Brewer’s theorem),是分布式系统中的一个基本定理。 指的是在一个分布式系统中, Consistency(一致性)、Availability(可用性)、Partition tolerance(分区容错性),三者不可得兼。

  Consistency(一致性):在分布式系统中的所有数据备份,在同一时刻是否同样的值。
  Availability(可用性):保证每个请求不管成功或者失败都有响应。
  Partition tolerance(分区容忍性):当发生网络分区故障时,仍能够保证对外提供服务。

  Raft满足CAP中的CP,并不能保证可用性,当节点中半数以上的节点崩溃时,整个服务不可用。

什么是raft协议?

  Raft是一种新型易于理解的分布式一致性复制协议,提供了更完整更清晰的协议描述,并提供了清晰的节点增删描述。
  Raft作为复制状态机,是分布式系统中最核心最基础的组件,提供命令在多个节点之间有序复制和执行,当多个节点初始状态一致的时候,保证节点之间状态一致。系统只要多数节点存活就可以正常处理,它允许消息的延迟、丢弃和乱序,但是不允许消息的篡改。

  通过RAFT提供的一致性状态机,可以解决复制、修复、节点管理等问题,极大的简化当前分布式系统的设计与实现,让开发者只关注于业务逻辑,将其抽象实现成对应的状态机即可。基于这套框架,可以构建很多分布式应用:
  分布式锁服务,比如Zookeeper
  分布式存储系统,比如分布式消息队列、分布式块系统、分布式文件系统、分布式表格系统等
  高可靠元信息管理,比如各类Master模块的HA

raft中的三种角色

Raft中存在三种角色,分别是follower,candidate,leader

  leader:系统中在同一时刻必须有且只有一个leader,只有leader才可以去处理Clients发过来的请求,leader会将日志分发给followers,同时收集多数派的followers应答,leader会向所有followers主动发送心跳维持领导地位。

  candidate:用来选举一个新的leader,是由follower触发超时而来。

  follower: 完全被动,不能发送任何请求,只接受并响应来自leader和candidate的Message,每个节点启动后的初始状态一定是follower。

raft中的三种消息
  1. RequestVote RPC:由 Candidate 发出,用于发送投票请求;

  2. AppendEntries (Heartbeat) RPC:由 Leader 发出,用于 Leader 向Followers 复制日志条目,也会用作 Heartbeat (日志条目为空即为 Heartbeat);

  3. InstallSnapshot RPC:由 Leader 发出,用于快照传输,虽然多数情况都是每个服务器独立创建快照,但是Leader 有时候必须发送快照给一些落后太多的 Follower,这通常发生在 Leader 已经丢弃了下一条要发给该Follower 的日志条目(Log Compaction 时清除掉了) 的情况下。

raft任期逻辑时钟

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

  1. 时间被划分为一个个任期 (term),term id 按时间轴单调递增;
  2. 每一个任期的开始都是 Leader 选举,选举成功之后,Leader 在任期内管理整个集群,也就是 “选举 + 常规操作”;
  3. 每个任期最多一个 Leader,可能没有 Leader (spilt-vote 导致)。
    在这里插入图片描述
raft leader选举过程

在这里插入图片描述

  在网络初始化时,网络中所有的服务器都以Follower的角色启动。由于Follower只被动接收消息。所以全网中所有服务器都处于等待状态。同时每一个服务器都在本地维护一个计时器。计时器的作用很简单,就是判断当前阶段(选举阶段或正常运行阶段)是否超时。
  在网络启动后所有服务器等待一段时间过去以后。计时器将会超时。这时候计时器超时的服务器将转换自己的角色为Candidate。进入选举阶段。而进入选举阶段的Candidate将会做以下几件事:
  1.将自己的任期号加1.
  2.为自己投一票用以选举出新的Leader。
  3.将本地的计时器重置
  4.发送投票请求到网络中的其他所有的服务器。
  5.等待下一次的计时器超时

同时在leader选举的过程中需要满足以下几个要求:
  1.每个服务器在一个任期内只能投一票,并且使先到者先得(即投票给自己收到的第一个请求投票的,满足要求的服务器的请求)
  2.请求投票的消息中需要带有请求者所处的当前任期号。
  3.投票者只会投票给任期号大于等于自己当前任期号的服务器。

在上述的选举过程中会出现三种可能:
  1.自己当选为leader
  2.其它节点当选为leader
  3.没有选出leader,等待计时器超时,再次选举leader

  当网络中某一个Candidate接收到网络中大多数成员的投票后,即可将自己的身份转换为Leader。在当选Leader后,该服务器将周期性地发送心跳信息(心跳信息包含成功当选Leader的服务器的当前任期号)到网络中其他服务器。在网络中其他的服务器收到心跳信息后检查心跳消息中的任期号是否大于等于自己的任期号。如果满足该条件的话Candidate将会转换为Follower状态,并重置计时器。而如果任期号小于自己的任期号,服务器将拒绝该心跳消息并继续处于Candidate状态。

  第三种情况为网络中没有服务器成功当选Leader。这种情况在当很多Follower同时成为Candidate时会发生。因为当角色转换为Candidate后将会将选票投给自己。从而导致选票被分散开来,没有Candidate可以得到网络中大部分节点的选票。从而没有节点可以成为Leader.这种情况下计时器将再次超时,网络状态将从选举阶段进入下一个选举阶段。同时Candidate将会再次执行上面说明的几件事。
  
  Raft算法采用了随机选举超时机制来避免出现这种情况。即当计时器超时后,服务器将随机延迟指定的时间后才进入选举阶段。由于随机延迟的原因,将降低服务器在同一时间选举超时的情况,可以有效避免选票分散的情况。

  当Leader成功选举之后,将周期性发送心跳消息到网络中其他服务器。同时其他服务器将转换自己的角色为Follower。并且每次收到心跳消息后都会重置自己的计时器,防止超时再次进入选举阶段。
  而如果Leader因为特殊情况崩溃时,网络中的其他服务器将不再接收到心跳消息,在等待指定时间后计时器将会超时,从而再次进入选举阶段。
  如果Leader崩溃时间较短,可以在其他服务器计时器超时之间恢复,并发送心跳消息,网络仍然可以恢复为Leader崩溃之前的状态。
  如果Leader崩溃时间较长,在网络中已有新的Leader选举产生后恢复,由于旧的Leader任期号将小于新的Leader,在旧的Leader接收到新的Leader发送的心跳消息后则会变为Follower状态。

小结

服务器转变为Candidate后:
  1.term号+1
  2.为自己投票
  3.发送RPC请求其它节点为自己投票
  4.计时器重置
  5.等待下一次超时
  当收到心跳消失时,如果任期号大于当前节点任期号,则转变成follower。
  当收到大多数选票时,转变成leader。
  计时器超时,则再次做上面五件事。

服务器成为Leader后:
  1.重置计时器,并周期性发送心跳消息(带有自己的任期号)到网络中其他服务器。
  2.等待客户端请求消息。

服务器转变为Follower后:
1.等待Leader或者Candidate发送消息给自己。
  如果是心跳消息(心跳消息中的任期号大于等于自己的任期号),则重置计时器。
  如果是选举消息(选举消息中的任期号大于自己的任期号),则将自己变为Candidate,任期号更新为选举消息中的较大的任期号。重置计时器并返回投票响应信息。
2.或者处于正常运行状态时,如果收到客户端请求,将会将该请求重定向到Leader。
3.如果在指定时间间隔内没有收到心跳消息或者是选举消息,则角色变为Candidate。

raft log复制过程

在这里插入图片描述  raft中的一条日志包含了:当前日志的索引,创建日志时,leader所处的任期号,用于状态机执行的命令。如果一条日志能够被状态机安全执行,就认为可以提交了。
  一旦选出leader,它就开始接受客户端请求,每个客户端请求都包含一条需要被复制状态机执行的命令,leader就把这条命令作为新的日志条目追加到自己的日志末尾,然后并行向其它机器发送AppendEntries RPC请求要求复制日志,当半数以上的机器复制成功后leader将当前条目应用到他的状态机并向客户端回复执行结果,如果某个follower崩溃或者网络问题丢包,leader会无限重试AppendEntries RPC(甚至在leader已经响应客户端以后)直到所有follower都成功复制了所有日志条目。
  leader决定什么时候日志条目应用到状态机是安全的;这种条目被称为可被提交,raft保证可被提交的条目是持久化的并且最终会被所有可用的状态机执行。一旦被领导人创建的条目已经复制到了大多数的服务器上,这个条目就称为可被提交的。例如图中的7号条目。leader日志中之前的条目都是可被提交的,包括由之前的leader创建的条目。leader跟踪记录它所知道的被提交条目的最大索引值,并且这个索引值会包含在之后的AppendEntries RPC中(包括heartbeat中),为的是让其它服务器都知道这条条目已被提交。一旦一个追随者知道了一个日志条目已经被提交,它会将该条目应用至本地的状态机。

raft日志复制的特性
  1. 如果在不同的日志中的两个条目有着相同的索引号和任期号,那么他们存储的命令肯定是相同的。
    源于leader在一个任期里给定的一个日志索引最多创建一条日志条目,同时该条目在体制中的位置也从不会改变。
  2. 如果在不同的日志中的两个条目有着相同的索引号和任期号,那么他们之前的所有日志条目都是完全一样的。
    源于AppendEntries RPC 的一个简单的一致性检查:当发送一个AppendEntries RPC时,leader会把新日志之前的一个日志条目的索引位置和任期号都包含在里面,follower会检查与自己的日志中的索引和任期号是否匹配,如果不匹配就会拒绝这个日志条目。接下来就是归纳法证明了。
  3. leader通过强制follower复制它的日志来处理日志的不一致。
      为了follower的日志同自己的一致,leader需要找到follower与它日志一致的索引位置,并让follower删除该位置之后的条目,然后再将自己在该索引位置之后的条目发给follower,这些操作都在一致性检查时完成。
      leader为每个follower维护了一个nextIndex,用来表示将要发给该follower的下一条日志条目索引,当一个leader开始掌权时,会将nextIndex初始化为它的最新日志条目索引值+1,如果follower在一致性检查过中发现自己的日志和leader不一致,会在这个AppendEntries RPC中返回失败,leader收到响应之后会将nextIndex递减然后重试,最终nextIndex会达到leader和follower日志一致的位置,这个时候RPC会成功,follower中冲突的日志也被移除了,此时follower和leader的日志就一致了。
raft的快照

  快照(Snapshot)是最简单的压缩方式,在快照中,全部的当前系统状态都被写入到快照中,存储到持久化的存储中,然后在那个时刻之前的日志都可以被丢弃。
raft快照
每个服务器独立的创建快照,只包含已被提交的日志

快照内容主要包括:
  1.状态机的状态。
  2.raft的少量元数据,保留这些元数据是为了快照后对紧接着的一个AppendEntries进行一致性检查。
  3.为了支持集群成员变化,最新的配置也会作为一个条目被保存在快照中。

虽然多数情况都是每个服务器独立创建快照, 但是leader有时候必须发送快照给一些落后太多的follower, 这通常发生在leader已经丢弃了下一条要发给该follower的日志条目(Log Compaction时清除掉了)的情况下。

快照分块传输

在这里插入图片描述接受者需要实现的:
如果term < currentTerm立刻回复
如果是第一个分块 (offset为0) 则创建新的快照
在指定的偏移量写入数据
如果done为false, 则回复并继续等待之后的数据
保存快照文件, 丢弃所有已存在的或者部分有着更小索引号的快照
如果现存的日志拥有相同的最后任期号和索引值, 则后面的数据继续保留并且回复
丢弃全部日志
能够使用快照来恢复状态机 (并且装载快照中的集群配置)

小结

在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值