关于运维:Joint-Consensus两阶段成员变更的单步实现

3次阅读

共计 4855 个字符,预计需要花费 13 分钟才能阅读完成。

简介:Raft 提出的两阶段成员变更 Joint Consensus 是业界支流的成员变更办法,极大的推动了成员变更的工程利用。但 Joint Consensus 成员变更采纳两阶段,一次变更须要提议两条日志,在一些零碎中间接应用时有些不便。那么 Joint Consensus 成员变更是否只应用单步实现呢?

作者 | 祥光
起源 | 阿里技术公众号

一 引言

分布式系统运行过程中节点常常会呈现故障,须要反对节点的动静减少、删除和替换。

成员变更是分布式系统绕不开的话题,特地是在一致性零碎中,对于晋升运维能力和服务可用性都有很大的帮忙。

Raft 提出的两阶段成员变更 Joint Consensus 是业界支流的成员变更办法,极大的推动了成员变更的工程利用。但 Joint Consensus 成员变更采纳两阶段,一次变更须要提议两条日志,在一些零碎中间接应用时有些不便。尽管 Raft 也提出了单步成员变更办法,但单步成员变更办法一次只能减少或缩小一个成员,限度较大,并且容易踩坑,个别不举荐应用。

那么很天然的想到,Joint Consensus 成员变更是否只应用单步实现呢?本文对这个问题进行了深入探讨。

二 成员变更

咱们先来回顾下一致性协定中的成员变更问题。成员变更是在集群运行过程中扭转运行一致性协定的节点,如减少、缩小节点、节点替换等。成员变更过程不能影响零碎的可用性。

成员变更也是一个一致性问题,即所有节点对成员配置达成统一。然而成员变更又有其特殊性,因为在成员变更的过程中,参加投票的成员会发生变化。


图 1 成员变更的某一时刻 Cold 和 Cnew 中同时存在两个不相交的多数派

如果将成员变更当成个别的一致性问题,成员变更过程中,各节点从旧成员配置 Cold 切换到新成员配置 Cnew 的时刻可能有差别,可能在某一时刻 Cold 和 Cnew 中同时存在两个不相交的多数派,造成双 Quorum,毁坏一致性。

为了解决这个问题,Raft 提出了两阶段的成员变更办法 Joint Consensus。

1 Joint Consensus 成员变更

Joint Consensus 成员变更为了防止双 Quorum 问题,引入一个联结成员配置 Cold,new 作为过渡配置,Cold,new 是 Cold 和 Cnew 的组合。Cold 与 Cold,new 的 Quorum 有交加,Cold,new 与 Cnew 的 Quorum 也有交加。成员变更先从 Cold 切换到 Cold,new,待 Cold,new 提交后,再切换到 Cnew,保障 Cold 与 Cnew 不同时应用,因此不会造成双 Quorum,保障安全性。


图 2 Cold 与 Cold, new 与 Cnew 三者的 Quorum 汇合之间的关系

Joint Consensus 应用两条日志实现成员变更过程。Leader 收到成员变更申请后,先向 Cold 和 Cnew 同步一条 Cold,new 日志,尔后所有日志都须要 Cold 和 Cnew 两个多数派的确认。Cold,new 日志在 Cold 和 Cnew 都达成多数派之后能力提交,尔后 Leader 再向 Cold 和 Cnew 同步一条只蕴含 Cnew 的日志,尔后日志只须要 Cnew 的多数派确认。Cnew 日志只须要在 Cnew 达成多数派即可提交,此时成员变更实现,不在 Cnew 中的成员主动下线。


图 3 Joint Consensus 成员变更过程

成员变更过程中如果产生 Failover,老 Leader 宕机,Cold,new 中任意节点都可能成为新 Leader,如果新 Leader 上没有 Cold,new 日志,则持续应用 Cold,Follower 上如果有 Cold,new 日志会被新 Leader 截断,回退到 Cold,成员变更失败;如果新 Leader 上有 Cold,new 日志,则持续将未实现的成员变更流程走完。

2 单步成员变更

Joint Consensus 成员变更之所以须要两个阶段,是因为对 Cold 与 Cnew 的关系没有做任何假如,为了防止 Cold 和 Cnew 各自造成不相交的多数派而造成双 Quorum,才引入了两阶段计划。

如果加强成员变更的限度,假如 Cold 与 Cnew 的 Quorum 交加不为空,Cold 与 Cnew 就无奈造成双 Quorum,则成员变更就能够简化为一阶段。

实现单步的成员变更,关键在于限度 Cold 与 Cnew,使 Cold 与 Cnew 的 Quorum 交加不为空。那么怎么样限度 Cold 与 Cnew,能力使 Cold 与 Cnew 的 Quorum 交加不为空呢?办法就是每次成员变更只容许减少或删除一个成员。


图 4 减少或删除一个成员时 Cold 与 Cnew 的 Quorum

减少或删除一个成员时的情景,如图 4 所示,能够从数学上严格证实,只有每次只容许减少或删除一个成员,Cold 与 Cnew 不可能造成两个不相交的 Quorum。因而只有每次只减少或删除一个成员,从 Cold 可间接切换到 Cnew,无需过渡成员配置,实现单步成员变更。

单步成员变更一次只能变更一个成员,如果须要变更多个成员,如实现替换成员等,能够通过执行屡次单步成员变更来实现。

单步成员变更实践尽管简略,但却埋了很多坑,理论用起来并不是那么简略。先前的文章 Raft 成员变更的工程实际中有具体介绍。

三 两阶段成员变更的单步实现

Joint Consensus 成员变更尽管通用然而采纳两阶段,一次变更须要提交两条日志,单步成员变更尽管只须要提交一条日志,然而限度较大,一次只能变更一个成员。两者的劣势是否联合呢?Joint Consensus 成员变更是否只用单步实现呢?

Joint Consensus 成员变更过程中,Cold,new 日志的提交曾经让各节点对 Cnew 配置达成了统一,那么 Cnew 日志有什么作用呢?是否在 Cold,new 日志提交后就从 Cold,new 配置切换到 Cnew 配置呢?这样是不是就能够不须要 Cnew 日志,变成单步实现了呢?

思考 Joint Consensus 成员变更中 Cnew 日志的作用,Cnew 日志在 Cold,new 日志提交之后发动提议,节点收到并长久化 Cnew 日志后从 Cold,new 配置切换到 Cnew 配置,不在 Cnew 配置中的成员在 Cnew 日志提交后下线。依据这个过程,能够总结出 Cnew 日志的作用:

  • 告诉节点在收到并长久化 Cnew 日志后从 Cold,new 配置切换到 Cnew 配置。
  • 告诉不在 Cnew 配置中的节点在 Cnew 日志提交后下线。
  • 成员变更过程中产生 Failover 后,本地有 Cnew 日志的节点具备优先选举权。

如果能不应用 Cnew 日志同时又实现 Cnew 日志的工作,不就能够用单步实现两阶段的 Joint Consensus 成员变更吗?事实上曾经有零碎摸索过这条路。

1 ZooKeeper 成员变更

ZooKeeper 从 3.5.0 版本开始在 Zab 的根底上反对了成员变更。ZooKeeper 具备 Primary Order 个性,而应用两条日志的 Joint Consensus 成员变更无奈保障 Primary Order 个性,为了既满足成员变更的通用性,又不丢失 Primary Order 个性,ZooKeeper 在论文《Dynamic Reconfiguration of Primary/Backup Clusters》中提出了本人的成员变更办法,并在 ZooKeeper 中利用了此办法,比 Raft 的提出还早。

如图 5 是 ZooKeeper 成员变更协定,图中旧成员配置用 S 示意,新成员配置用 S‘示意,P 为 Leader 节点,图 5 展现了将 B1 和 B2 节点替换成 B3 和 B4 节点的过程:


图 5 ZooKeeper 成员变更协定

初始化:为了让新节点追上最新数据,新成员配置 S’中的新节点 B3、B4 先连贯到以后的主节点 P,P 会向它们传输本人以后的状态作为他们的初始状态。在 Zab 协定中当备节点连贯上主节点时这样的状态传输就会主动产生,并且会持续从主节点 P 接管所有后续的操作日志(例如图中的 Op1 和 Op2),这个过程中节点 B3、B4 不参加投票。

步骤 1:主节点 P 向连贯到它的所有备节点(S U S‘)发送成员变更日志 COP,COP 日志中携带旧成员配置 S 和新成员配置 S‘,并期待旧成员配置 S 中的节点确认。一旦 S 中的多数派确认了 COP 日志,就对 S’达成了共识。

步骤 2:在 COP 日志之前的日志只须要旧成员配置 S 中的多数派确认,能够在旧成员配置和新成员配置(S U S‘)中提交;在 COP 命令之后且在 S’的激活音讯 ACTIVATE 之前的日志须要新旧成员配置(S U S‘)两个多数派确认,并且只能在 S’中提交;在 S’的激活音讯 ACTIVATE 后的日志,只须要在 S‘中确认和提交。

步骤 3:主节点 P 期待 COP 日志以及 S ’ 中 COP 之前的日志的确认。

步骤 4:一旦新旧成员配置(S U S1)两个多数派都确认了 COP 日志,主节点 P 就提交 COP 日志,并播送一条激活音讯 ACTIVATE 来激活新成员配置 S’从而实现成员变更。与日志同步音讯相似,ACTIVATE 音讯蕴含主节点 P 的 Epoch,携带过期的 Epoch 的 ACTIVATE 音讯将被疏忽。

成员变更过程中如果产生 Failover,可能呈现上面几种状况:

如果在 COP 日志发送之前 Failover,那么成员变更失败,在旧成员配置中从新选主后持续工作;

如果在 COP 日志发送之后并且在 ACTIVATE 之前 Failover,新旧成员配置中任意节点都可能成为新 Leader,如果新 Leader 上没有 COP 日志,则成员变更失败;如果新 Leader 上有 COP 日志,则持续将未实现的成员变更流程走完。

如果在 ACTIVATE 后 Failover,成员变更曾经实现,但还无奈保障新 Leader 肯定在新成员配置中,此时不在新成员配置中的节点还不能下线。因而在发送 ACTIVATE 音讯后还须要在新成员配置中提交一条 no-op 日志,no-op 日志提交后可保障新 Leader 肯定在新成员配置中,不在新成员配置中的节点能够平安下线。

ZooKeeper 利用异步的 Commit 音讯,也即 ACTIVATE 音讯来告诉节点从新旧成员配置切换到新成员配置。应用异步的 no-op 日志让不在新成员配置中的节点平安下线。ZooKeeper 的 ACTIVATE 音讯和异步的 no-op 日志起到了 Joint Consensus 成员变更中 Cnew 日志的作用。

2 改良的单步实现

ZooKeeper 成员变更协定不如 Joint Consensus 成员变更那么简洁,Joint Consensus 成员变更通过两阶段能够利用协定自身而不须要做过多的限度来保障成员变更的安全性。那么 ZooKeeper 成员变更协定是否能够改良呢?

ZooKeeper 成员变更协定中异步的 ACTIVATE 音讯和 no-op 日志其实就是为了实现 Joint Consensus 成员变更中 Cnew 日志的作用,明确了这一点后那么也能够将 Joint Consensus 成员变更的 Cnew 日志改为异步的,在 Cold,new 日志提交后就认为成员变更实现,而后异步的提交 Cnew 日志。之所以能够将 Cnew 日志改为异步的,在 Cold,new 日志提交后就认为成员变更实现,是因为 Cold,new 日志一旦提交,各节点曾经对新成员配置达成了统一,再也不会回退到旧成员配置了,剩下的过程最终肯定会执行实现,Cnew 日志最终肯定会提交。

还有一种改良办法是持续保留 ACTIVATE 音讯,但不应用 no-op 日志,那么怎么样保障切换到新成员配置的节点具备优先选举权呢?依据选举的安全性,具备最新日志的节点具备优先选举权,那么能够在选举的时候携带节点以后的成员配置,在日志一样新的状况下,优先给曾经切换到新成员配置的节点投票,即可保障切换到新成员配置的节点具备优先选举权。新成员配置中的大多数节点切换到新成员配置后,不在新成员配置中的节点能够平安下线。

四 总结

Joint Consensus 成员变更的提出极大的推动了成员变更的工程利用,其简洁柔美并且通用,然而采纳两阶段,一次变更须要提交两条日志。本文探讨了两阶段的 Joint Consensus 成员变更的单步实现办法,并做了一些改良,为成员变更的工程利用提供了更多的抉择。

原文链接
本文为阿里云原创内容,未经容许不得转载。

正文完
 0