简介: 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成员变更的单步实现办法,并做了一些改良,为成员变更的工程利用提供了更多的抉择。
原文链接
本文为阿里云原创内容,未经容许不得转载。