一致性协议raft详解(一):raft整体介绍

前言

有关一致性协议的资料网上有很多,当然错误也有很多。笔者在学习的过程中走了不少弯路。现在回过头来看,最好的学习资料就是Leslie LamportDiego Ongaro的数篇论文、Ongaro在youtube上发的三个视频讲解,以及何登成的ppt。

本系列文章是只是笔者在学习一致性协议过程中的摘抄和总结,有疏漏之处敬请谅解,欢迎讨论。

概述

一致性协议的目的是实现CAP,最好在CA之间有很好的平衡,既保证高可用,又保证一致性

所以这类算法大概涉及两个方面

  1. leader的选举
  2. 多副本状态机的每个副本上都保存有完全相同的操作日志,保证所有状态机副本按照相同的顺序执行相同的操作
    1. raft只保证了不同节点上的log可以最终达成一致,至于应用log的状态机的一致性不是raft的任务,应自行实现

raft独特的特性

  • 强领导者:和其他一致性算法相比,**Raft 使用一种更强的领导能力形式。**比如,日志条目只从领导者发送给其他的服务器。这种方式简化了对复制日志的管理并且使得 Raft 算法更加易于理解。
  • 领导选举:Raft 算法使用一个随机计时器来选举领导者。这种方式只是在任何一致性算法都必须实现的心跳机制上增加了一点机制。在解决冲突的时候会更加简单快捷。
  • 成员关系调整:Raft 使用一种共同一致的方法来处理集群成员变换的问题,在这种方法下,处于调整过程中的两种不同的配置集群中大多数机器会有重叠,这就使得集群在成员变换的时候依然可以继续工作。
  1. Raft 算法保证所有已提交的日志条目都是持久化的并且最终会被所有可用的状态机执行。
  2. raft日志保证:
    • 如果在不同的日志中的两个条目拥有相同的索引和任期号,那么他们存储了相同的指令。因为同一个任期只有一个leader,leader最多在一个任期里在指定的一个日志索引位置创建一条日志条目
    • 如果在不同的日志中的两个条目拥有相同的索引和任期号,那么他们之前的所有日志条目也全部相同。因为日志在raft节点中必须是顺序添加的。leader保存着每个follower当前的日志进展,并持续给follower推送
    • 但是某条log到底能不能被commit,还是要看他有没有被大多数节点接受(Figure 8) (并且整个集群具备幂等性)
特性解释
选举安全特性对于一个给定的任期号,最多只会有一个领导人被选举出来(5.2 节)
领导人只附加原则领导人绝对不会删除或者覆盖自己的日志,只会增加(5.3 节)
日志匹配原则如果两个日志在相同的索引位置的日志条目的任期号相同,那么我们就认为这个日志从头到这个索引位置之间全部完全相同(5.3 节)
领导人完全特性如果某个日志条目在某个任期号中已经被提交,那么这个条目必然出现在更大任期号的所有领导人中(5.4 节)
状态机安全特性如果一个领导人已经将给定的索引值位置的日志条目应用到状态机中,那么其他任何的服务器在这个索引位置不会应用一个不同的日志(5.4.3 节)
raft集群的特点

摘自何登成老师的ppt

  • 系统存在一个Leader角色(Proposer),接受Clients发过来的所有读写请求
  • Leader负责与所有的Followers(Acceptors)通信,将提案/Value/变更复制到所有Followers,同时收集多数派Followers的应答
  • 少数派宕机,不会影响系统整体的可用性
  • Leader日常维护与所有Followers的心跳
  • Leader宕机,会触发系统自动重新选主,选主期间系统对外不可服务

raft中commit何意?

The leader decides when it is safe to apply a log entry to the state machines; such an entry is called committed.

raft作者说只要leader把当前log写入自己的RSM中,就算是commit了(该日志已经被系统中大部分节点接受),并且提供raft一致性协议的以下两点保证:

  1. 已经commit提交的日志不会再修改(可靠性),这里指不会更改,其意为Quorum不会更改,单个节点上的数据还是会被leader更改。
  2. 所有节点上的数据一致(希望达到线性一致性)

Our goal for Raft is to implement linearizable semantics (each operation appears to execute instantaneously, exactly once, at some point between its invocation and its response).

raft leader election

leader选举原则:最大提交原则: During elections, choose candidate with log most likely to contain all committed entries

  • Follower在收到Leader或者Candidate的RPC请求的情况下一直保持Follower状态。而当一段时间内(election timeout)没有收到请求则认为没有Leader节点而出发选举流程。
  • 选举开始之后,自己作为candidate,先投自己一票,向其他candidate发选举请求,如果自己收到选举请求并且自己没有选举过,就把票投出去
  • 某台机器收到投票过半,则成为主
  • 选举windows的时长是随机决定的(150ms-300ms) electionTimout,这种方式只是在任何一致性算法都必须实现的心跳机制上增加了一点机制。在解决冲突的时候会更加简单快捷。
  • 如果follower在election timeout超时了,会成为candidate;如果heartbeat timeout超时了,也会成为candidate
  • 其实不太会出现那种大家一起变成candidate,导致投票一直失败那种情况,因为electionTimout是一个随机的,包括服务刚起来的时候也会等一个随机的时间
  • 安全性
    • 拥有最新的已提交的log entry的Follower才有资格成为Leader(相当于是term(任期)和log entry都要最大)
      • 这个保证是在RequestVote RPC中做的,Candidate在发送RequestVote RPC时,要带上自己的最后一条日志的termlog index,其他节点收到消息时,如果发现自己的日志比请求中携带的更新,则拒绝投票。日志比较的原则是,如果本地的最后一条log entry的term更大,则term大的更新,如果term一样大,则log index更大的更新
        • 这点隐含了一个事实:如果一个candidate能当选主,那么它一定包含了最新的日志,并且最新这条日志被半数以上节点承认。
          • 抽屉原理,选举出来的Leader是多数中数据最新的,一定包含已经在多数节点上commit的数据
    • Leader只能推进commit index来提交当前term的已经复制到大多数服务器上的日志,旧term日志的提交要等到提交当前term的日志来间接提交(log index 小于 commit index的日志被间接提交)。
      • 还有一种实现方式是,保证只让最大commit index的人做leader,就算是因为网络分区导致一些stale节点的term值很大,leader也会先收到这个leader选举rpc之后追齐term(StepDown),然后再次成为leader,这也就是为什么braft的文档中说原始的RAFT论文中对非对称的网络划分处理不好
  • 在等待投票的时候,候选人可能会从其他的服务器接收到声明它是leader的附加日志项 RPC。如果这个leader的任期号(包含在此次的 RPC中)不小于候选人当前的任期号,那么候选人会承认leader合法并回到跟随者状态。 如果此次 RPC 中的任期号比自己小,那么候选人就会拒绝这次的 RPC 并且继续保持候选人状态。

log replication

  1. client写给leader的log,会被leader同步给follower,
  2. 一旦半数以上的follower被写成功了,leader就可以回client成功了(并且可以写入自己的log了(如果除自己外一半写成功了,就需要写入自己才能回client,如果除自己外一半以上写成功了,自己不写入就可以先回client)),这时follower的log还是uncommit状态(是否有uncommit这个状态要看系统实现是怎样的)
  3. 返回client之后,leader再通知follower,这条log就被commit了。(这步也可以没有,还是看系统实现是怎样的,比如6.824就没要求你做prepare/commit)
  4. 某些Followers可能没有成功的复制日志,Leader会无限的重试 AppendEntries RPC直到所有的Followers最终存储了所有的日志条目。
  5. 不一致的时候:
    1. Leader通过强制Followers复制它的日志来处理日志的不一致,Followers上的不一致的日志会被Leader的日志覆盖。
    2. Leader为了使Followers的日志同自己的一致,Leader需要找到Followers同它的日志一致的地方,然后覆盖Followers在该位置之后的条目。
    3. Leader会从后往前试,每次AppendEntries失败后尝试前一个日志条目,直到成功找到每个Follower的日志一致位点,然后向后逐条覆盖Followers在该位置之后的条目。
  6. 两种情况:
    1. leader发送给follower之后还没有得到回复就下线了
    2. leader发送很多个logindex给follower,但是并不是每个follower都收到了一样的index,他们可能有大有小(这时候leader是要保序的?)

网络分区

主要摘自baidu braft文章

Symmetric network partitioning

原始的RAFT论文中对于对称网络划分的处理是,一个节点再次上线之后,Leader接收到高于currentTerm的RequestVote请求就进行StepDown。这样即使这个节点已经通过RemovePeer删除了,依然会打断当前的Lease,导致复制组不可用。对于这种case可以做些特殊的处理:Leader不接收RequestVote请求,具体情况如下:

  1. 对于属于PeerSet中的节点,Leader会在重试的AppendEntries中因为遇到更高的term而StepDown
  2. 对于不属于PeerSet中的节点,Leader永远忽略

这样,属于PeerSet中的节点最终能够加入,不属于PeerSet的节点不会加入也不会破坏。如果网络划分是因为节点故障导致的,那么稳定的多数复制组不会收到更高term的AppendEntries应答,Leader不会StepDown,这样节点可以安静的加入集群。

Asymmetric network partitioning

原始的RAFT论文中对非对称的网络划分处理不好,比如S1、S2、S3分别位于三个IDC,其中S1和S2之间网络不通,其他之间可以联通。这样一旦S1或者是S2抢到了Leader,另外一方在超时之后就会触发选主,例如S1为Leader,S2不断超时触发选主,S3提升Term打断当前Lease,从而拒绝Leader的更新。这个时候可以增加一个trick的检查,每个Follower维护一个时间戳记录收到Leader上数据更新的时间,只有超过ElectionTImeout之后才允许接受Vote请求。这个类似Zookeeper中只有Candidate才能发起和接受投票,就可以保证S1和S3能够一直维持稳定的quorum集合,S2不能选主成功。

StepDown

RAFT原始协议中Leader收到任何term高于currentTerm的请求都会进行StepDown,在实际开发中应该在以下几个时刻进行StepDown:

  1. Leader接收到AppendEntries的失败应答,Term比currentTerm大
  2. Leader在ElectionTimeout内没有写多数成功,通过logic clock检查实现(1个ElectionTimeout内会有10个HeartBeat)
  3. Leader在进行RemovePeer的LogEntry被Commit的时候,不在节点列表中,进行StepDown,通常还会进行Shutdown

raft冲突解决方法

在 Raft 算法中,leader处理不一致是通过强制follower直接复制自己的日志来解决的。这意味着在follower中的冲突的日志条目会被leader的日志覆盖。

  • 要使得跟随者的日志进入和自己一致的状态,leader必须找到最后两者达成一致的地方(最简单的方法就是领导者一个个的减少index去试,看哪个能append成功),然后删除从那个点之后的所有日志条目,发送自己的日志给跟随者。

参考链接

  1. 解读Raft(一 算法基础)
  2. Raft在etcd中的实现(三)选举流程
  3. Raft一致性算法中集群成员变更 节点由少变多的话要维护中间状态,否则会出现由于只有一部分节点更新了节点数目,导致的双主
  4. Raft算法详解 写得很好
  5. Paxos、Raft分布式一致性算法应用场景 简单介绍了CAP,以及CAP为什么不能满足三者,写的很好,一致性协议入门必读
  6. ZooKeeper和CAP理论及一致性原则zookeeper是CP
  7. OceanBase的一致性协议为什么选择 paxos而不是raft?
  8. 分布式一致性算法-Paxos、Raft、ZAB、Gossip只写了选举,太简单了
  9. Raft协议处理各种failover情况这个写的很好
  10. The Raft Consensus Algorithmraft 官方网站
  11. RAFT介绍百度braft写的raft介绍,写的挺好的,真的要看看
  12. 分布式系统中的幂等性
  13. raft可能出现丢数据么?日志落后的人不会被选为主
  14. MIT 6.824 Raft 设计文档
  15. 分布式一致性算法-Raft学习笔记
  16. 如何理解拜占庭将军问题?
  17. 为 Raft 引入 leader lease 机制解决集群脑裂时的 stale read 问题stale read一般出现在有网络分区的时候
  18. etcd raft如何实现Linearizable Read
  19. 强一致性、顺序一致性、弱一致性和共识
  20. Students’ Guide to Raft助教写的指南
  21. 分布式系统理论之Quorum机制swift用到了,防止脑裂的思想和一般的zk选主不一致
  22. NameNode 高可用整体架构概述HDFS的namenode使用zk选取主节点
  23. 深入理解Hadoop HDFS【一篇就够】整个HDFS集群由Namenode和Datanode构成master-worker(主从)模式。Namenode负责构建命名空间,管理文件的元数据等,而Datanode负责实际存储数据,负责读写工作。
  24. 微信分布式数据存储协议对比——Paxos和Quorum 写的挺好的
  25. 分布式一致性最强算法之Paxos透析 paxos和Quorum
  26. Parameter Server for Distributed Machine Learning
  27. 2020 MIT 6.824 分布式系统课程 Kylin创始人
  28. Raft一致性算法论文的中文翻译
  29. When does a term begin and end in Raft?人工的定义一个term的任期是没有必要的,因为leader election会造成不可服务。在设计系统的时候要尽量避免。但是raft作者在figure5中说的话仍是正确的,分布式系统何时会发生term变更时不确定的,有可能因为网络波动、网络分区、节点宕机而导致term变更
  30. raft协议里面,leader只能commit自己任期内的日志这条规则,仔细推理的话似乎是有空间的?
  31. Raft 作者亲自出的 Raft 试题,你能做对几道?
  32. Immutable 详解及 React 中实践 immutable 数据结构
  33. 精读 Immutable 结构共享
  34. logcabin
  35. Immutable collections for JavaScript immtable-js官方文档
  36. 分布式Server中使用braft来构建高可用系统包含raft中各种rpc操作,可以看看
  37. LVM文件的备份和恢复lvm可以实现快照
  38. Log-structured file systems: There’s one in every SSD
  39. Log-structured File System
  40. Linearizability versus Serializability
  41. 理论基础 · Raft phd 论文中的pipeline 优化那么使用多条连接的话可能存在什么问题?
  42. Raft 算法原理及其在 CMQ 中的应用(上)raft论文翻译的挺详细的
  43. 一文看尽 Raft 一致性协议的关键点log recovery/no-op/membership变更/为什么Current Term、VotedFor需要持久化
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值