暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

导读:分布式一致性协议 Raft

容膝居 2021-01-09
185

本篇文章内容来自 In Search of an Understandable Consensus Algorithm (Raft) 的英文论文,算是一篇读书笔记。文档中保留了和论文基本一致的顺序和章节结构,部分英文名词做了标注解释和引用,方便对照阅读理解。



内容提要
  1. 状态机
  2. 了解一致性算法中 State Machine/Replicated State Machine 工作机制
  3. Raft 基本认识
  4. 介绍 Raft 中基本的概念,如:节点状态、Term
  5. Leader 选举
  6. 介绍 Leader 的选举过程
  7. 日志复制
    介绍分布式系统中日志的复制机制
  8. 安全性保障
    Leader 选举和日志复制组成了分布式协议的核心部分,但是可靠的一致性保障还需要额外的机制来保障
  9. 配置变更
    分布式系统中节点数量等配置信息不是一成不变的,配置的变更也需要满足分布式系统的要求


  • 论文下载:https://raft.github.io/raft.pdf

  • 演示工具:https://raft.github.io/


01

状态机


在一致性算法中,集群中服务器节点上的 state machine 通常会保持一致,这样即便是集群中部分机器宕机,整个集群仍然可以继续工作。在分布式系统中 replicated state machine,可以简单从字面意思来理解,同样是用于解决集群容灾的问题。
replicated state machine 通常通过日志复制来实现。如下图所示:这里每一个服务器节点都会存储一份包含一系列操作命令的日志,通过按序执行日志中的操作命令来就可以生成自己的 state machine。因为这份操作命令的日志,需要在每个服务器节点上保证完全相同(也就是操作日志数量和顺序相同),这样 state machine 都是处理相同的操作日志得到的,那么所有服务器节点就能保证状态一致,这里保证日志的完全一致就是一致性算法需要解决的问题。

处理过程:

  1. 一致性处理模块接收到客户端的请求(也就是操作命令),会将这些操作命令记录在该服务器节点的日志中;

  1. 该节点会与集群中其他节点通信,将新增的操作命令同步到集群中的其他节点(即便是有部分服务器节点宕机);

  1. 当这次的操作命令同步完成,各个服务器节点state machine就可以自行按序处理日志;

  1. 最后便可以响应客户端的这次请求。


上述 replicated state machine 的执行流程基本上就是分布式一致性协议 Raft 的一个大致工作流程,只是在实际系统中一致性算法的实现需要具备下面特性:
  1. 安全可靠性,如在 non-Byzantine 发生:网络延迟、网络分区、丢包等情况下的安全可靠;

  2. 可用性,只要大多数的服务器节点可以正常工作,就需要保证集群可用;

  3. 去“时间”依赖,算法不能依赖自然时间系统来保证一致性;

  4. 低延时,不能因为集群中存在小部分慢节点,而影响集群响应速度;


脚注:

  • non-Byzantine: 把出现故障(crash 或 fail-stop,即不响应)但不会伪造信息的情况称为“非拜占庭错误”( non-byzantine fault)或“故障错误”( Crash Fault); 
  • Byzantine: 伪造信息恶意响应的情况称为“拜占庭错误”( Byzantine Fault),对应节点为拜占庭节点。



02


Raft 基本认识


在 Raft 的实现中,最重要的角色莫过于 leader。前面提到的操作日志的管理,全权由 leader 来负责。leader 负责接收来自客户端的请求(log entries),然后将这些 log entries 复制到集群的其他节点,并通知这些节点什么时候可以将这些log entries应用到各自的 state machine。日志的管理有如下特点:
  • leader 不需要和其他节点协商log entries的处理方式;

  • 数据流始终是从 leader 到 follower;


脚注

  • log entries:可以抽象理解成客户端发起的请求中包含的操作命令,如果是存储系统,则可以是对数据的写操作。
  • command:log entry 中包含的,可以应用于 state machine 中的操作命令,如存储系统中,将某个数值更新等。
  • log:日志,也即是节点用于存储 log entries 的地方。



节点状态

Raft 集群中的节点共可以有三个状态,也就是 Raft 集群中可能会存在三类节点:
  • Leader:处理所有的客户端请求,即便是部分请求到达了 Follower,Follower 也不会处理而是将请求转发到 Leader 来处理;

  • Candidate:这类节点是 Leader 的候选节点,新的 Leader 会从这一类节点中选出;

  • Follower:这类节点不处理任何请求,只会响应来自 Leader 和 Candidate 的请求,是被动型状态;


如下图,节点启动进入 Follower 状态,如果 Follower 长时间收不到 Leader 或者 Candidate 节点的心跳包(不带log entries 的空 AppendEntries RPC),等到 election timeout超时,Follower 态节点会变更状态为 Candidate,Candidate 接收到集群中大多数的投票,就会变为新的 Leader。


脚注

  • election timeout:follower 达到该时间后,会推断当前集群没有可用 leader,自身状态变为 candidate,并主动发起下一轮 leader 的选举。
  • 心跳请求:不带 log entries 的空 AppendEntries RPC。



Term

前面提到一致性算法实践中不能依赖绝对时间系统。而 Raft 定义了自己的逻辑时间系统。Raft 将时间分割为任意长度的段,叫做 terms(terms 就是一串连续单调递增的整数)。
如下图,每一个 term 最多包含两个阶段。每一轮新的 Leader 选举都会生成一个新的 term,也就意味着,每一个 term 都是以选举期开始的。在这个阶段, Candidate 们有机会成为新的 Leader。并且一旦Candidate 成为新的 Leader,它便负责维护整个 term,直到下一轮 Leader 选举开始产生新的 term。在部分特殊情况下,如果无法选举出新的 Leader,那么这个 term 就会直接结束,很快开启下一轮的选举。


集群中每一个节点都会记录 current term,并且集群中存在如心跳和选举的通讯机制。如果某个节点的 current term 比其他节点的要小,该节点会更新自己的 current term。如果这个节点是 Candidate 或者 Leader,该节点会直接变为 Follower 态。当然,如果该节点接收到的请求中 term < current term,则会直接拒绝该请求。



State
Persistent state on all servers:
持久化状态需要在 RPC Response 返回前持久化存储起来
currentTerm
latest term server has seen (initialized to 0 on first boot, increases monotonically)
votedFor
candidataId that received vote in current term (or null if none)
log[]
log entries, each entry contains command for state machine, and term when entry was received by leader (first index is 1)
Volatile state on all server:
commitIndex
index of highest log entry known to be committed (initialized to 0, increases monotonically)
lastApplied
index of highest log entry applied to state machine (initialized to 0, increases monotonically)
Volatile state on leaders:
leader 选举后重新初始化
nextIndex[]
for each sever, index of the next log entry to send to that server (initialized to leader last log index+1)
leader 用于记录的下次应该复制同步给每个 follower 的 log entry index
matchIndex[]
for each server, index of highest log entry known to be replicated on server (initialized to 0, increases monotonically)

State-表注:

几种不同状态节点中的属性信息





Election Safety
at most one leader can be elected in a given term.
在任意个 term,最多只能选出一个 leader。
该特性在 "Leader election" 小结有较具体的阐述。

Leader Append-Only
a leader never overwrites or deletes entries in its log. it only appends new entries.
不允许 leader 对 log 进行覆盖和删除,仅能追加。
Log Matching
if tow logs contain an entry with the same index and term, then the logs are identical in all entries up through the given index.
如果两份 log 中都包含一个相同 term 和 log index(当然所包含的 command 也相同) 的 log entry,那么这两份 log 中,该 log entry 之前的所有 entries  都应该完全相同。
Leader Completeness
if a log entry is committed in a given term, then that entry will be present in the logs of the leaders for all higher-numbered term.
如果某个 log entry 已经是是 committed 状态,那么这个 entry 应该出现在该 log entry 所在 term 后的所有 terms 的 leader 的 log 中。
State Machine Safety
if a server has applied a log entry at a given index to its state machine, no other server will ever apply a different log entry for the same index.

表注:

Raft 核心特性,协议需要保证它们的成立。


03


Leader Election


前面提到,Follower 节点只要能够持续的接收到来自 Leader 或 Candidate  节点的心跳包,就会维持在 Follower 态。不过,一旦达到 election timeout 超时时长,与 Leader 和 Candidate “失联”,Follower 就会发起一轮 Leader 选举。在新的 Leader 选举开始时, Follower 节点会做如下操作:
  • 递增自己的 term (current term);

  • 将自己状态变为 Candidate;

  • 给自己投票成为 Leader ,并同时向集群中其他节点发起 Vote RPC 请求来拉票;

    而该 "Follower" 节点发起的这一轮的 Leader 选举,无外乎如下三种结果:


    1. 该 Follower 节点赢得选举,成为新 term 的 Leader;
    Candidate 节点只需要得到集群中多数节点的投票,就可以成为 Leader。并且在一个 term 内,每一个节点最多只能给一个 Candidate 节点投票(采取FCFS的原则)。这样就保证了每一轮 Leader 选举,最多只能有一个 Candidate 节点胜出。这些机制是保证 Election Safety Property 的基础。一旦新 leader 被选出,它就会开始向集群中其他节点发出心跳包,以表明自己 Leader 身份。

    脚注:

    • FCFS: first-come-first-served,也就是在 Leader 选举时期,集群中节点会给它收到的第一个满足条件的 Vote RPC 投票。
    • Election Safety Property:会在后面的小结具体解释。


    2. 集群中其他节点赢得选举,成为 Leader;

    在计票的过程中,该节点收到了来自“新Leader”的 AppendEntries RPC(可能是心跳):
    • 如果“新Leader”的 term 不小于该节点的 current term。那么,该节点就会承认并接受“新Leader”,自己再次变回到 Follower 态。

    • 如果“新Leader”的 term 小于该节点的 current term。那么,该节点会忽略这次请求,继续选举计票。


    3. 无节点胜出,没有产生新的 Leader;

    集群中同时成为 Candidate 的节点较多时,可能出现得票相同的情况,无法选出新的 Leader。此时,Candidate 待到超时后,会开启新一轮选举。这样重复开启新一轮选举的情况,可能会在集群中无休止的发生。
    Raft 采用随机的设置 election timeout 的方式,来避免上述情况。具体讲就是,每一个 candidate 在选举的初期会随机的选择 election timeout,在不同节点达到election timeout而超并准备开始下一轮选举时,新的 term 中的 Candidate 节点就会被分散开,这样就可以尽量的保证在大部分的情况下,同一时期只有一个 Candidate 超时进入下一轮选举期,并能够赢得选举成为leader。



    04


    Log Replication


    前面提到 replicated state machine 的概念,而这里要讲的 log replication 就是集群中 leader 节点做状态同步的方式。前面也提到过,Raft 中 leader 节点负责处理所有来自客户端的请求,那么简单的理解 log replication 就是:leader 节点通过将日志复制并同步给集群中其他节点的方式,来达到集群中所有节点 state machine 同步和最终一致的目的的方法。

    基本认识

    一次完整的客户端请求处理大致过程:

    1. leader 收到客户端请求(通常包含 command);

    2. leader 会为这个操作命令,创建 log entry,并加入到自己的 log 中;

    3. leader 通过 AppendEntries RPC 来并行的复制这些 log entry 到集群中其他节点;

    4. 当这些 log entry 被安全可靠的(Raft 中叫做 safely,集群中多数节点)复制,leader 就会将 log entry 应用到自己的 state machine 中,最后响应客户端。


    集群中 follower 节点出现 crash、网络丢包、运行缓慢,leader 都是通过 AppendEntries RPC 来保持和其他节点的状态同步,直至 follower 最终和 leader 保持一致。


    Log index

    下图所示 log (log entries) 的组织方式。一个 log entry 实际上由客户端的 command 和该 log entry 当时所在的 term 组成(AppendEntries RPC 中不仅包含了 command 与还包含了 term 信息)
    Log 中包含很多 log entries,而每个 log entry 在 log 中都有一个整形数字标识其在 log 中位置,这就是 log index。



    Committed entry

    如上面描述的客户端请求处理过程,leader 创建的 log entry 同步到集群中多数节点后,该 log entry 会被 leader 应用到自己的 state machine ,这时我们就将该 log entry 称为 committed。Raft 会保证所有 committed entry 一定是持久化,并且最终能被集群中所有可用的节点应用的。
    leader 自己会记录当前 committed 最大的 log index(如上图,log index = 7)。在 AppendEntries RPC 中会带上这个 log index,这样集群中其他节点就可以计算出,自身和 leader 之间 entry 的差距。一旦 follower 节点发现本地的 log entries 已经 committed,它就可以将这些 entries 顺序的应用于自己的 state machine 了。


    Raft Property: Log Matching Property

    关于 Log Matching property 的具体理解可以拆解为如下两条:
    1. 如果两份不同的 logs 中存在相同 term 和 log index 的两个 entries,那么这两个 entries 一定包含相同的 command;

    2. 上述的这两份 logs 中,该 log entry 之前的所有 log entries 也一定是完全一致的;


    第【1】条比较好理解,因为 leader 在一个 term 时期内,只会为一个 log index 创建最多一个 log entry,并且不会改变 log entry 在 log 中位置。
    第【2】条通过 AppendEntries RPC 来保证。AppendEntries RPC 中包含了 follower 需要追加的 log entries,而 preLogIndex 和 preLogTerm 记录了上一次追加更新到的位置。如果 follower 中并不包含这样的 entry<preLogTerm, preLogIndex>,说明这一次追加操作不能执行(因为如果执行的话 log 就和 leader 不一致了),会拒绝本次 append 请求。



    可以简单用归纳法来证明该特性:
    • 在初始状态,logs 为空的情况下,是满足 Log Matching 特性的;

    • 当 logs 在追加扩充时,只要 AppendEntries RPC 成功,Log Matching 特性就会一直满足;



    Inconsistency handle

    如上所述,在正常情况下,依据上面的规则 leader 和 followers 能够始终保持一致。但是,如果 leader 发生崩溃,新的 leader 被选举出来,而原 leader 的 log entries 还没有完全复制同步完成的情况下,就会出现不一致。同样 followers 也有可能发生崩溃,这就更增加了不一致的可能。
    如下图所示不一致情况,如 (a-b) 缺少某些 entries,又如(c-d) 有了一些额外的 entries,又或者如(e-f)一样两种情形都发生了。

    图注:
    (f): 可能是 term-2 时的 leader,在 index<4-6> 还没来得及提交的时候,发生了崩溃。随后又迅速重启,并且成为了 term-3 时的 leader,而 index<7-11> 也没能提交,又发生了崩溃。这就会造成现在这种不一致的场景。

    在 Raft 中,leader 通过强制 followers 来复制自己的 log 来解决不一致问题。也就是说,followers 如果收到 AppendEntries RPC 请求中与本地 log 冲突的 entries 时,需要删除或者覆盖 follower 本地的 log(这里注意,leader 节点是不会做如此操作的,需要保证「Leader Append-Only Property」),以 leader 同步的 log 为准(当然这里还是会有一些限制条件,后面会讲到)。
    既然需要对 followers 本地的 log 进行删除和覆盖,就需要找到 leader 和 follower 两份 log 中最新的相同 log entry. 根据前面说明的「Log Matching Property」,在该 log entry 之前的所有 entries 一定是完全一致的,leader 只需要将该 log entry 之后的 entries 发给 follower 就可以了。


    nextIndex

    通过「表-State」我们知道,leader 会记录下次要发送给每一个 follower 的 log index — nextIndex。
    • 新选举出来的 leader 会将该 nextIndex 初始化为自身上一个 log index + 1 的位置;

    • 如果某个 follower 与 leader 的 log 不一致,在 AppendEntries 请求到达 follower 时,一致性校验失败 follower 会拒绝该请求;

    • leader 发现请求被拒绝后,会对对应的 nextIndex 做递减操作,然后重新发起 AppendEntries 请求。

    这样,AppendEntries RPC 一旦成功,就意味着确定了 nextIndex, leader 和 followers 就保持了一致。
    脚注:
    这里的“递减操作”,论文中也提出优化方法,即 follower 在 AppendEntries RPC 响应中携带发生冲突的 term 和 first-index 信息,这样,就可以每次请求排查一整个 term 的冲突信息。



    05


    Safety


    到目前为止,还不能完全保证集群中每个节点的 State Machine 能够按序执行相同的 command。例如当 leader 正在提交 log entries 时,有 follower 正好变得不可用,而这个 follower 随后被选举为新的 leader,依据前面所述规则会发生 entries 覆盖的情况,最终就会导致最开始提到的“集群中各个节点的 State Machine 不一致”的情况。Raft 为了保证一致性,对前面我们讲到的两个重要环节 leader election 和 log replication 做了一定的限制。

    Election restriction

    restriction-1: log entries only flow in one direction, from leader to followers

    上述不一致情况的发生其中一个原因是,选出了一个“不合适”的 follower(candidate) 作为 leader。之所以不合适,是因为这个 leader 可能不包含当前集群中所有已提交的日志。所以,在 leader 选举过程中,Raft 需要保证新的 leader 必须包含直到上一个 term 所有已经提交的日志
    脚注:
    当然,有一些一致性算法,没有如此严格的限制,而是通过在选举中或者新 leader 产生后,将 leader 缺失的日志,采用某种方式补齐。Raft 作者认为,如此做法会增加算法的复杂度。Raft 采用了更加严格的限定条件,将日志流向限定在 leader 到 followers 的方向,leader 不会发生覆盖日志的情况,这样算法更加简洁。

    Raft 是如何保证新 leader 一定包含所有已提交的日志的呢?

    • 通过 "Leader election" 小节阐述的 leader 选举过程,我们知道,candidate 能够胜出,必须获得集群中多数节点的投票,我们可以将这个多数节点记作集合:C<lead>;

    • 通过 "Log replication" 小节阐述的日志提交过程,我们同样知道,log entry 要变为 committed,必须通知到集群中的多数节点,也就是集群中的多数节点记录着当前最新的 committed entry 的 log index,我们可以将这个多数节点记作集合:C<committed>;

    两个集合取交集得到 C<v>:

      C<lead> ∩ C<committed> = C<v>

      由于C<lead> 和 C<committed>都是集群中的多数节点,所以它们的交集C<v> 一定不是空集合。也就意味着,C<v> 中一定记录着当前最新的已提交日志记录 latest-committed-log-entry。在选举过程中,只需要保证能够胜选的 candidate 自身已提交日志不晚于C<lead>中所有节点的已提交日志,就可以保证新的 leader 一定是包含当前集群中所有已提交日志的。
      脚注:
      这个限制过程在 candidate 发起的 RequestVote RPC 中实现。Request 中会携带 candidate 的 <lastLogTerm, lastLogIndex>,只需要将<lastLogTerm, lastLogIndex> 与 latest-committed-log-entry<term, index> 比较,就能知道 candidate 是否包含最新提交日志了。


      Replication restriction

      restriction-2: never commit log entries from previous terms by counting replicas

      目前我们应该已经很明确:一个 committed 的 log entry,一定已经从 leader 复制并存储在了集群中的多数节点上。并且上面的阐述解决了因为 leader 状态不是最新而导致的 committed 的 log entry 被覆盖的场景。在下图中,描述了已经在集群中多数节点上复制的 log entry 被覆盖的场景(这样的 log entry 实际上应该是已提交,但是因为 leader 崩溃实际上提交状态未被记录,所以并不应该被覆盖)。

      如下图:S1~S5 是集群中的5个节点,a~d 是顺序的5个不同 term。

      • (a):S1 为 leader,并且正在提交/复制 entry<term-2, index-2>;

      • (b):S1 发生崩溃,S5 获得 S3、S4、S5 的投票成为 term-3 的 leader,但是收到了一条不同于 term-2 时的 entry<term-3, index-2>;

      • (c):S5 发生崩溃,S1 已经重启并在此成为 term-4 的 leader,S1 继续提交/复制 entry<term-2, index-2>,并存储在了多数节点中,但还未变为 committed 状态;

      • (d):S1 再次崩溃,S5 再次变为 leader,并开始继续复制 entry<term-3, index-2>,这时已经复制到集群中多数节点上的 entry<term-2, index-2> 就会被覆盖。

      虽然 entry<term-2, index-2> 并没有变为提交态,但是由于这个 log entry 已经在多数节点上上存储,只是提交状态未被记录,本质上是已提交态,在(d)中发生的覆盖行为是不可接受的,实质上破坏了前面一致性的约定。

      为了避免发生上面所述问题,Raft 只会提交当前 term 的 log entries,一旦这些 entries 变为提交态,Raft 就认为它们之前的 entries 也是提交态(是不是有些眼熟,这里还是运用的「Log Matching Property」)。

      根据上面的约束条件,我们知道问题其实发生在 (c) 。我们重新梳理上图的场景:a、b、e

      • (a) (b) :同上不变;

      • (e):S5 发生崩溃,S1 已经重启并在此成为 term-4 的 leader,这时 S1 不需要继续提交entry<term-2, index-2>,而是只提交/复制entry<term-4, index-3>,如果此时该 entry 复制到了多数节点上,后面 S5 没有条件被选举为 leader;(反之,如果该 entry 没有成功同步到多数节点,又有了新的leader, entry<term-2, index-2> 和entry<term-4, index-3> 都可能被覆盖,是符合预期的 )


      Raft Property: Leader Completeness Property

      下面是对「Leader Completeness Property」更详细的论证。该特性的内容可以先返回到"Raft 核心特性"表格先做了解,基本上就是前面两条限制条件的抽象总结。
      这里我们采用反证法,假设该特性不存在,然后找出系统中矛盾的地方,从而反证该特性。所以这里我们假设一种与该特性不符的场景:
      • leader-T:term-T 的 leader;

      • leader-U:term-U 的 leader;

      • U > T;

      • term-T 中有一个已提交的 log entry(entry-T),而它不存在与 term-U;

      根据上面的假设场景,我们做出如下推断:

      1. 在 term-U 选举阶段,entry-T 一定不会出现在 leader-U 的 log 中(因为 leader 不允许删除或者覆盖 entries)。

      1. 如上图,leader-T 将 entry-T复制到了集群中的多数节点。而 leader-U 获得了集群中多数节点的投票成为 leader。这样至少就会有一个节点,如:S3,即拥有提交态 entry-T,也给 leader-U 投了票。

      1. S3 一定是先接受 entry-T,再给 leader-U 投票。否则,它将会拒绝 leader-T 的 AppendEntries 请求,无法接受 entry-T(因为S3的 current term 已经大于 T 了)。

      1. 在 term-U 选举阶段,S3 一定还包含 entry-T,因为:

        1. 在上面的假设中 T~U 之间的 leader 还是会包含 entry-T

        2. S3 如果中途成为 leader,也不会删除 entry-T

        3. S3 在 term-U 阶段只有在 AppendEntries 发生日志冲突时,才会被删除


      S3 要投票给 leader-U,那么 leader-U 的 log 一定不能比 S3 的旧,而这恰好是矛盾的地方。这里也可以分为两种情况考虑:

      • 最近的 log term 相同

      • 最近的 log term 不相同且 leader-U > S3


      Raft Property: State Machine Safety Property

      有 「Log Matching Property」和「Leader Completeness Property」的保证,该特性也能保证,也很容易理解。



      06


      Cluster membership changes


      直到目前为止,我们讨论的集群配置都是固定的,也就是集群中节点数量以及节点配置不会发生变化的场景。但是在生产实际中,一定会发生替换、增删节点的情况,在这些情况下也一定要保证以上的一致性协议论述要成立。故,Raft 协议也将集群配置自动变更纳入了进来。
      所谓配置变更,简单讲就是从旧的配置变为新的配置,这个过程不可能在一瞬间完成(除非停机重启,但这是无法接受的)。那么,集群就可能被划分为两个独立的部分。如下图,集群中节点数量从3个变为5个的过程中,集群被打散为两个独立的部分。这样就可能在同一时间存在两个 leader,一个是旧配置C-old,一个是新配置C-new。


      为了保证安全可靠性(Safety),配置变更需要采用两阶段提交(two-phase approach):

      1. 集群进入到配置转换的过渡阶段(joint consensus);

      1. joint consensus 被提交后,集群就转换到全新的配置;


      joint consensus 新旧配置同时生效,也即是:

      • 不论是处于哪种配置,log entries 会被复制到所有节点;

      • 不论是处于哪种配置,集群中节点都可以成为 leader;

      • 上面提案的通过,必须同时得到新旧两部分中各自的大多数通过;

      有了 joint consensus,既可以保证安全可靠性,也能够在配置变更过程中继续提供服务。


      如下图描述了集群配置的变更过程:

      第一阶段:joint consensus,创建C-old,new

      • 集群配置实际会被存储在一个特殊的 log entry(C-old,new) 中。

      • leader 接收到配置变更请求后,在第一阶段(joint consensus)添加该 C-old,new,并依照日志复制机制来进行同步复制。

      • 一旦某个节点接收到了新的配置,不论它是否已经 committed,该节点都会使用该配置。也就意味着,leader 会在应用 C-old,new的规则来决定 C-old,new 什么时候应该被提交。

      • 如果 leader 崩溃,新的 leader 是C-old 还是 C-old,new 完全取决于 candidate 是否接收到了 C-old,new.

      • 在该阶段 C-new 子集群是无法独立做决策的(前面提到的 joint consensus 特点)。

      第二阶段:提交 C-old,new

      • C-old,new 一旦处于提交状态,C-old 和 C-new 子集群就可以独立决策了;

      • 由于前面讲到的「Leader Completeness Property」的保证,只有拥有 C-old,new 的节点才能被选举为 leader;此时就可以创建 C-new entry 在集群中执行同步复制,新的配置就可以在整个集群中应用生效了。

      • 一旦 C-new 生效,C-old 节点就可以被摘除,不会有影响。

      从上面的过程中可以看出,C-old 和 C-new 子集群没有时机能够同时独立决策,这样就保证了安全可靠性(Safety)。





      文章转载自容膝居,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

      评论