CONSENSUS:BRIDGING THEORY AND PRACTICE(第4章)

第4章 集群成员变更

截止到现在我们假定集群的配置(参加一致性算法的servers)是固定的。在实践中,偶尔是有必要更改配置的,例如当server故障或者改变复制组级别的时候需要做替换。这可以通过手动来做到,通过如下两个方法:

  • 可以通过停止集群,然后更新配置,再重新启动集群来做到配置的变更。然而在改变的过程中集群会处于不可用状态。
  • 可选择的,新的server可以沿用其要替换的server的网络地址。然而管理员要保证被替换的server绝对不会重新回来,否则系统将失去安全性保证(例如可能增加一个额外的投票)。

    这两个改变成员关系的方法有明显的缺点,而且手动处理的话本身就附带着操作出错的风险。

为了避免这些问题,我们决定实现自动的配置变更并包含到Raft算法中。Raft允许集群在配置变更期间可以继续提供服务,并且成员变更的实现仅在基础一致性算法中引入了很少的扩展。图4.1概要描述了成员变更所需要的RPCs,在剩下的章节中有描述。

译者注:小论文中只提到了两阶段提交的配置变更方式,共同一致性的解决方式增加了复杂性,这也是LogCabin中的实际实现方式。但是本大论文中提到了新的配置变更方式,更加简单了,是一次更新一个配置的方式,没有了两阶段、共同一致性。不过这里也介绍了小论文中提到的两阶段提交方式,本文会先后介绍。


这里写图片描述

4.1 安全性

配置变更的第一个挑战就是保证安全性。想要保证这个机制的安全性,必须是的在转换期间不会出现一个term选举出两个leaders的情况。如果一个配置变更添加或者移除多个servers,直接将集群的旧配置切换到新配置是不安全的,不可能自动的立刻将所有的servers的配置切换了,因此集群在转换期间会有潜在的分裂为两个互不依赖的法定集合的可能(见图4.2)。

大多数的成员变更算法引入了额外的机制处理上面提到的问题。这也是我们在Raft的最初做法,但是我们后来发现了一个简单的方法,那就是不允许成员变更导致集群出现两个法定集合的可能。因此,Raft限制了允许配置变更的方式:一次请求只允许增加或者减少一个server。更复杂的成员变更通过一系列的single-server变更来实现。本章的大部分章节描述的是single-server的方法,比我们最初使用的方法(两阶段)要简单。为了完整,4.3 节描述了最初的方法,引入了额外的复杂性去处理任意的配置变更的情况。在发现single-server change这种方法之前我们在LogCabin中使用更加复杂的方法,在写这篇文章的时候LogCabin中仍然使用的是更复杂的方法。


这里写图片描述

这里写图片描述

当从集群中添加或删除一个server的时候,新旧集群的任意法定集合(majority)都是有交集的(译者注:有交集的话新旧配置集群中就至少有一个成员持有双方的信息,那么在切换的时候就能保证最新的日志者才能胜选),见图4.3。交集部分也能阻止集群分离成两个majorities,根据3.6.3章节的论证,它保证了the existence of “the voter”(译者注:即两个配置集群中majority的交集部分)。因此如果仅仅添加或删除一个server,那么直接切换到新的配置是安全的。Raft利用了这种特性,并添加了一点额外的机制实现了集群成员变更。

集群配置的存储和交流在log中使用了专门的entries。这里用了Raft中已存在的机制去复制和持久化配置信息。在配置变更过程中也允许接收客户端请求,会强制配置变更和客户端请求有序(可以通过pipeline或者batches的方式达到并发的目的)。

当leader在当前配置(C-old)下收到添加或删除一个server的请求之后,leader把新的配置(C-new)entry追加到log中并通过正常的Raft机制复制。新的配置在每个服务上都是写入log后立即生效:C-new entry被复制到C-new的servers上,新配置下的majority被用来决议C-new entry是否已提交。这意味着servers不会的等待配置entries被提交了再生效,每个server都使用log中最近的配置。

一旦C-new entry处于已提交状态了,配置变更就完成了。这个时候leader知道C-new配置中的majority已经接受了C-new。Leader也知道了任何没有转到C-new配置的server是不可能再组成majority了,而且不在C-new中的server是不能被选为leader的。C-new配置已提交后就允许继续下面三件事:

  1. Leader确认了配置变更成功的完成了。
  2. 如果配置变更是移除一个server,那么这个server可以停止了。
  3. 进一步的配置变更可以开始了。在这一点之前,交叠的配置变更会是集群处于不安全的状态中,如图4.2

如上所述,服务器始终使用在其日志中的最新配置,而不管该配置条目是否已提交。Leader可以轻松的避免重叠的配置更改(上面的第3项):直到前一个更改的条目已经提交后才可以开始新的更改。仅当旧的集群中的majority都处于C-new状态时才能安全的开始另一个成员变更。如果servers等到C-new已提交后才接受C-new配置的话,Raft leader了解到旧集群中的majority已经接受了新配置的时间点是困难的。Leader必须跟踪哪些servers知道entry的已提交状态,并且servers需要持久化commit index。这些机制在Raft中都不需要。取而代之的是每一个server只要把entry持久化后就立刻接受新的配置,leader只要知道C-new entry已提交了就立刻允许进一步的配置变更。不幸的是,这样的决议确实意味着也可以删除配置更改的log entry(比如leader发生了变化译者注:新的leader是可能比之前的leader日志旧的,它会导致已经持久化的日志被清除以保持与leader一致,领导人完整特性),这种情况下server必须把配置回退到log中前一个配置。


在Raft中,调用发起者的配置被用于达成一致性,无论是投票还是日志复制:

  • server会接受不在它最新配置中的leader的AppendEntries请求。否则一个新的server将永远不会被加入到集群(它将不会接受任何除添加服务器这个配置变更entry之前的任何log entries)。译者注:新的待加入集群的server一开始是没有任何配置的,如果没有这样的处理,它永远不会接收任何log entry,也就永远无法被leader catch up,参考AddServer RPC,后面 4.2节也会讲。

  • server也会把它的投票授予不再它最新配置中的候选人(candidate)(如果候选人的term和日志符合要求)。这样的投票可能偶尔对保持集群的可用性是必要的。例如考虑在3个servers的集群中加入第4个server,如果一个server挂了,新的server的投票对于组成majority并选出leader是必要的。译者注:4个servers的集群,majority为3。假如当前的leader已经将C-new同步给了其中两个servers,之后挂了,这时候剩下的旧配置集群中剩下两个了,而新加入的还没有任何配置,如果它拒绝对陌生的节点投票的话,集群就会一直处于无leader状态了。而这么做我们也无需担心安全性,参考图4.3中的b就知道了。

因此,servers不根据当前配置处理RPC请求。

4.2 可用性

集群成员变更引入了几个保持集群可用性需要面对的问题。4.2.1节讨论了在添加server到集群之前使新server追赶上集群log的问题,因此它们不会拖延新请log entries的一致性达成;4.2.2节阐述了如何从集群中删除现有leader;4.2.3节描述了如何阻止已经被移除的servers扰乱新集群leader的问题。最后,4.2.4节论证了为什么在任何成员变更期间成员变更算法能保证集群的可用性。


这里写图片描述

4.2.1 追赶新servers日志

当一个server被添加到集群时,典型的情况是它没有任何log entries。如果以这种状态将它加入到集群中,它的log将会有一阵子才能赶得上leader的,在这段时间内,集群更容易处于不可用状态。例如一个three-server集群可以容忍一个server失效而不丧失可用性。然而如果有这样一个没有log的第4个server被加入到这个集群一旦3个原始server当中有一个挂了,则集群将临时的处于不可用状态(如图4.4(a))。另一个可用性问题是如果很的uo新的servers快速成功的加入到了集群,majority的组成就必须有新的servers(如图4.4(b))。这两种情况都要等到新的servers的log赶上了leader的集群才可用。

为了避免可用性间隙,Raft在配置变更前引入一个额外的阶段,就是新加入集群的server作为一个非投票成员。Leader把日志复制给它,但是投票和一致性达成的majority统计并不和它有关。一旦新的server日志追赶上了集群,配置变更就可以按照上面的描述去做了。(支持非投票server的机制其在他情况下也很有用,比如可以把状态复制给大量的server上(译者注:可能小于majority),这些servers提供松散的一致性读语义)。


这里写图片描述

Leader需要决定什么时候一个新的server充分的赶上了以便继续配置变更。这需要一些关注点以保证可用性:如果一个server被加入的太快,集群的可用性可能有风险,就如上面描述的。我们的目标是保证集群临时不可用在一个选举超时时间内,由于client需要有能力保证偶尔的不可用(比如leader挂了)。此外,如果可能的话我们希望通过使新server的log尽可能的接近leader以进一步降低不可用性。

如果新的server不可用了或者太慢了赶不上来了,Leader也应该取消变更。这个检查是重要的:Lamport的古老Paxon政府就因为没有包含这项在内崩溃了。他们意外地将成员资格改为了仅由会被淹死的水手组成,导致可能没有进展。尝试添加不可用或者慢的server经常是因为错误。事实上我们(译者注:LogCabin使用时)第一个配置请求更改就包含了网络端口号的错别字,系统正确终止更改并返回了错误。

我们建议用一下的算法判定一个新的server是否充分的追赶上了以添加到集群。通过多轮复制把log同步到新的server,如图4.5。每轮复制所有的本轮开始时的log entries到新的server。在复制当前轮次entries的时候,新的日志可能会出现,下一轮将会复制它们。随着不断的进展,每一轮的持续时间会逐渐缩短。本算法等待一个固定数目的轮次(比如10次)。如果最后一轮的时间小于选举超时时间,则leader把新的server加入到集群中,这基于到此下不会有足够多的未复制的entries能带来显著的可用性问题的假定。否则leader就取消配置变更并返回错误。调用着一般可能会重试(下一次很可能会成功,因为新server的日志已经追上一部分了)。

一开始的第一步,leader一定会发现新的server的log是空的。对于一个新的server来说,AppendEntries的一致性检查一直会重复失败知道nextIndex最终下降到1。这中来回可能实在集群中添加新server的主要性能因素(在这一阶段之后,log entries可以通过batching的方式以少量的RPCs传递给follower_译者注:第一步是发现nextIndex,新的server肯定为空,确定了nextIndex之后就可以以batching的方式传递log entry了_)。有多种方法来快速的收敛确定nextIndex,包含第3章中描述的方法。然而,解决添加新server这一特定问题的最简单方法是让follower在AppendEntries响应中返回其日志长度,这使得leader得以根据它限制nextIndex以快速确定follower的nextIndex。

4.2.2 移除当前leader

如果要求现有leader从集群中移除自己,则必须在某个时刻下台。一个直截了当的方法是使用第三章提到的领导人转换:被要求移除的leader可以把领导关系转换给另一个server,然后就可以使用正常的方式来处理成员变更了。

我们最初在Raft中开发了一个不同的方法,在这个方法中,现有leader执行成员变更来移除自己,然后下台。这使得Raft某种程度上显得有点尴尬,一个leader临时管理着不包含它的配置。最初我们在任意的配置变更里(见4.3节)需要这种方法,旧的配置和新的配置可能没有任何共同的server做领导人转换。相同的方法对于没有实现领导人转换功能的系统也是可行的。

在这个方法中,一旦C-new entry被提交了,配置中被移除的leader就会下台。如果leader在这个点之前下台,它可能还会超时并重新成为leader,这会拖慢配置变更进度。在一个只有两个servers的集群这种极端的情况下移除一个leader server,这个server可能不得不再一次成为leader以完成变更,见图4.6。因此leader等待直到C-new已提交后再下台。这是第一个新配置在完全没有被移除的leader参与的情况下就能运行的点:从C-new成员中一直是可能选出主的(译者注:C-new已提交了,自然在C-new中能选的出leader来。在没提交C-new的配置之前,没法保证有成员有C-new配置,Raft为了易用性没有提供这种探测机制,只有已提交这个点才是确定的点,C-new已提交之前是可能选不出leader的。假设C-new已提交之前有一个follower A有了C-new的配置,那么A是可能被选为leader的,参考RPC接受规则)。在被移除的leader下台后,C-new中的一个server将会超时并胜选(译者注:为什么C-old不能胜选呢?因为C-new处于已提交状态,C-old的日志旧,不可能胜选。)。这个小的可用性间隙可以通过容错处理掉,正如leader出问题后的可用性间隙处理方法。

这里写图片描述
这种方法导致对于做决策来说有两个含义,没有特别的危害但是可能令人惊讶:一个是将有一段时间(提交C-new的期间)leader管理者集群但是并不包含自己。它复制log entries但是并不在majorities中统计自己。另一个是一个server即便自身不在最新的配置中也依然需要开始选举,由于知道C-new entry已提交之前它可能还是必要的leader(见图4.6)。它不会统计自己的投票除非它是新配置servers中的一员。

4.2.3 破坏者

如果没有额外的机制,不在C-new中的servers可能会破坏扰乱集群。一旦leader应用了C-new entry,不在C-new中的servers就不会再收到心跳了,因此它将超时并开始新的选举。而且它将收不到C-new entry或者了解到C-new entry的提交情况,因此它不知道自己已经被移除集群了。这个server将用新的term发起RequestVote RPC,这将导致当前的leader回退到follower的状态。最终C-new中的一个新的leader将会被选举出来(译者注:是指如果这种情况发生了,最后配置总会通过重试最终完成更改,更改完之后依旧面临搅局server的干扰),但是搅局server将会再次超时并重复上述过程,导致一个较差的可用性。如果多个servers从集群中移除,这个情况会更糟。

我们第一个消除破坏扰乱的想法是如果一个server想要开始选举,它需要先检查自己会不会浪费每个人的时间–它有机会赢得选举。这引入了一个新的选举阶段,叫做Pre-Vote(预选举)阶段。一个candidate首先要问其他servers它的log是否足够up-to-date以获取它们的投票。仅当candidate确保它能获得majority的投票后再增加它的term并发起正常的选举。

这里写图片描述
不幸的是Pre-Vote并不能解决搅局servers:有些场景下搅局server的日志是充分的up-to-date的,但是开始选举依然是破坏性质的。可能令人惊讶的是,配置变更完成之前这种捣乱就会开始。例如,图4.7显示了正在集群中删除一个server。一旦leader创建了C-new log entry,这个正在被移除的server就会成为搅局破坏者。Pre-Vote检查对这种情况是没有帮助的,由于被移除的server的日志对于集群的majority是up-to-date的(尽管Pre-Vote阶段没有解决搅局server,它对于一般情况下的提升集群健壮行是一个有用的想法,见第9章)。

由于这个场景,我们现在确信没有办法通过单独的比较日志(比如Pre-Vote检查)来充分地了解到一个选举是不是由搅局者发起的。我们不能要求一个server在选举开始之前在C-new中检查每一个server的log,由于Raft必须能够容忍故障(译者分析:怎么通过log确定自己不是搅局者?如果通过对比log来分析,就需要拿到每一个server的最新log,判断自己在不在其中,假如不在,则不发起选举。但是这样的话带来的问题就是假如有一个5 servers集群,如果C-new只发送给了一个server之后leader就挂了,而拥有C-new的这个server却网络有问题不能被选为leader,但是其他servers却能通过它了解到C-new的配置,这样的话集群就没有server能选为leader了,这是Raft不能容忍的,Raft要能保证集群的容错能力,这样做就丧失了某种情况下的容错能力)。我们也不希望假定leader能够快速地复制entries,从而跳过图4.7所示的场景。这在实践中可能行得通,但它依赖于更强的假设:我们更倾向于避免发现log分歧位置以及复制log entries的性能。

Raft使用心跳来确定何时存在有效的leader。在Raft中,leader如果维护了与followers的心跳就会被认为是活动的(否则,另一个server将发起选举)/因此servers不应该能够搅乱有leader心跳的集群。我们修改RequestVote RPC来实现这个:如果一个server在选举超时时间内收到过leader的RPC,则它不会更新其term或者投票。它可以丢弃请求,回复一个投票被拒绝或者延迟这个请求,结果本质上市相同的。这不影响正常的选举,因为每一个server都需要等待至少一个选举超时周期才会发起选举。然而,它帮助避免了不在C-new中的搅局者:如果集群能收到leader的心跳,leader就不会被更大的term废黜。

这个改变和第三章中描述的领导人转换有些冲突,其中提到的是一个server合法的发起了一个选举,并不是因为超时。在这种情况下,RequestVote消息需要被其他servers处理即使确认当前集群中有leader存在。这种RequestVote请求可以包含一个特别的标志去标识这个行为(“我有捣乱leader的许可–它告诉我这么做的!”)。

4.2.4 可用性论证

这一节论证上米那的方案在成员变更期间能够充分的维持可用性。由于Raft的成员变更是leader-based的,我们表明该算法能够在成员变更期间维持和替换leader,并且leader将同时服务客户端请求并完成配置更改。我们假定,在其他方面,旧配置的majority是可用的(至少直到C-new被提交)并且C-new配置的majority是可用的(译者注:要记得这里讨论的是一次改变一个server的方案,参考图4.3)。

1 在配置变更的所有步骤都能够选举出一个leader:

  • 如果新集群中的可用server有C-new entry并且log是up-to-date的,它能收集到C-new majority的投票并成为leader
  • 否则,C-new entry还不能提交。新旧集群间具有up-to-date log的可用server能从C-old的majority和C-new的majority收集到投票,因此它用哪个配置没关系,都能成为leader。

2 假定它的心跳通过配置获得,一个leader一旦被选举出来就会被维持,除非它因不在C-new中并且C-new已经提交了而主动下台。

  • 如果一个leader能可靠地对它拥有的配置成员发送心跳,那么它和其followers都不会采用更高的term:他们不会超时并发起选举,并且它们将忽略其他servers任何更高term的RequestVote消息。因此,leader不会被强制下台。
  • 如果不在C-new中的server提交了C-new entry并且下台了,Raft将选举一个新的leader。新的leader很可能是C-new的成员,允许配置变更完成。然而,有一些已经下台的server再次成为leader的小风险。如果它再次被选举了,他将确认C-new已提交并且立刻下台,之后很可能C-new中的一个server在下一次成功竞选(译者分析:实际工程实现被移除的server应该会直接自杀吧,不会再有机会发起选举了吧)。

3 Leader将在配置变更的过程中服务客户端请求

  • Leader能继续在配置变更的过程中添加客户端的请求到log中
  • 由于新的servers在加入到集群之前需要追赶log,因此leader在这个时间窗口内是可以增加commit index并且回复给客户端的(译者注:新server是需要先追赶log的,追赶完之后再开始配置变更,一旦开始等效于增加了一条客户端同步处理请求而已,不影响什么)

4 leader将通过提交C-new来实现和完成配置更改,如果有必要,将会下台以允许C-new中的server成为leader

因此,在上述假定情况下,这一节描述的机制是能在配置变更期间充分保证可用性的。

4.3 共同一致性实现任意的配置变更

译者注:本节结合小论文内容

这一节展示一个更复杂的能在一次处理任意的配置变更的方法。例如两个servers在一次里被加入到集群之中,或者一个5 servers集群的所有servers都在一次请求中被替换掉。这是我们提出成员变更的第一个方法,它只描述了完整性。我们现在知道了单个server变更的方法,我们提供一个替代,由于处理任意的变更需要额外的复杂性。任意配置变更通常是在文献中提及的,但我们不认为这样的灵活性在现实系统中有必要,一系列single-server变更能实现任意期望的配置变更。

为了让配置修改机制能够安全,那么在转换的过程中不能够存在任何时间点使得两个领导人同时被选举成功在同一个任期里。不幸的是,任何服务器直接从旧的配置直接转换到新的配置的方案都是不安全的。一次性自动的转换所有服务器是不可能的,所以在转换期间整个集群存在划分成两个独立的大多数群体的可能性(见图4.2)。

为了保证安全性,配置更改必须使用两阶段方法。目前有很多种两阶段的实现。例如,有些系统在第一阶段停掉旧的配置所以集群就不能处理客户端请求;然后在第二阶段在启用新的配置。在 Raft 中,集群先切换到一个过渡的配置,我们称之为共同一致;一旦共同一致已经被提交了,那么系统就切换到新的配置上。共同一致是老配置和新配置的结合:

  • 日志条目被复制给集群中新、老配置的所有服务器。
  • 新、旧配置的服务器都可以成为领导人。
  • 达成一致(针对选举和提交)需要分别在两种配置上获得大多数的支持。

共同一致允许独立的服务器在不影响安全性的前提下,在不同的时间进行配置转换过程。此外,共同一致可以让集群在配置转换的过程人依然响应服务器请求。

集群配置在复制日志中以特殊的日志条目来存储和通信;图4.8展示了配置转换的过程。当一个领导人接收到一个改变配置从 C-old 到 C-new 的请求,他会为了共同一致存储配置(图中的 C-old,new),以前面描述的日志条目和副本的形式。一旦一个服务器将新的配置日志条目增加到它的日志中,他就会用这个配置来做出未来所有的决定(服务器总是使用最新的配置,无论他是否已经被提交)。这意味着领导人要使用 C-old,new 的规则来决定日志条目 C-old,new 什么时候需要被提交。如果领导人崩溃了,被选出来的新领导人可能是使用 C-old 配置也可能是 C-old,new 配置,这取决于赢得选举的候选人是否已经接收到了 C-old,new 配置。在任何情况下, C-new 配置在这一时期都不会单方面的做出决定。

一旦 C-old,new 被提交,那么无论是 C-old 还是 C-new,在没有经过他人批准的情况下都不可能做出决定,并且领导人完全特性保证了只有拥有 C-old,new 日志条目的服务器才有可能被选举为领导人。这个时候,领导人创建一条关于 C-new 配置的日志条目并复制给集群就是安全的了。再者,每个服务器在见到新的配置的时候就会立即生效。当新的配置在 C-new 的规则下被提交,旧的配置就变得无关紧要,同时不使用新的配置的服务器就可以被关闭了。如图4.8,C-old 和 C-new 没有任何机会同时做出单方面的决定;这保证了安全性。

这里写图片描述

在关于重新配置还有三个问题需要提出。第一个问题是,新的服务器可能初始化没有存储任何的日志条目。当这些服务器以这种状态加入到集群中,那么他们需要一段时间来更新追赶,这时还不能提交新的日志条目。为了避免这种可用性的间隔时间,Raft 在配置更新的时候使用了一种额外的阶段,在这个阶段,新的服务器以没有投票权身份加入到集群中来(领导人复制日志给他们,但是不考虑他们是大多数)。一旦新的服务器追赶上了集群中的其他机器,重新配置可以像上面描述的一样处理。

第二个问题是,集群的领导人可能不是新配置的一员。在这种情况下,领导人就会在提交了 C-new 日志之后退位(回到跟随者状态)。这意味着有这样的一段时间,领导人管理着集群,但是不包括他自己;他复制日志但是不把他自己算作是大多数之一。当 C-new 被提交时,会发生领导人过渡,因为这时是最早新的配置可以独立工作的时间点(将总是能够在 C-new 配置下选出新的领导人)。在此之前,可能只能从 C-old 中选出领导人。

第三个问题是,移除不在 C-new 中的服务器可能会扰乱集群。这些服务器将不会再接收到心跳,所以当选举超时,他们就会进行新的选举过程。他们会发送拥有新的任期号的请求投票 RPCs,这样会导致当前的领导人回退成跟随者状态。新的领导人最终会被选出来,但是被移除的服务器将会再次超时,然后这个过程会再次重复,导致整体可用性大幅降低。

为了避免这个问题,当服务器确认当前领导人存在时,服务器会忽略请求投票 RPCs。特别的,当服务器在当前最小选举超时时间内收到一个请求投票 RPC,他不会更新当前的任期号或者投出选票。这不会影响正常的选举,每个服务器在开始一次选举之前,至少等待一个最小选举超时时间。然而,这有利于避免被移除的服务器扰乱:如果领导人能够发送心跳给集群,那么他就不会被更大的任期号废黜。

4.4 系统集成

Raft实现可能以不同的方式显示本章中描述的集群成员变更机制。例如,图4.1中的AddServer和RemoveServer RCPs可以被管理员直接调用,或者它们可以被脚本调用,使用一系列single-server变更可以实现任意的配置变更。

可能成员变更会被期望自动调用以相应如server failures事件。然而,这应该根据一个合理的机制来做。例如自动一处failed server可能会使集群陷入危险境地,因为它可能导致留下过少的副本而违背了既有打算和容错需求。一个合理的方法是系统管理员配置一个期望的集群大小,基于这个约束,可用的servers可以自动地替换掉失败的servers。

当集群成员变更需要多个single-server步骤来做时,应该在移除servers之前添加servers。例如,要在一个3 servers的集群中替换一个server,添加一个server然后移除另一个允许系统在整个处理周期内处理一个server的故障。然而,如果一个server在添加其他server之前先被移除了,系统将暂时不能再掩饰任何故障了(由于2个server的集群需要两个servers都可用)。

成员变更引出一个不同的方法来初始化集群。没有动态的成员,每个server简单的用一个静态文件列举配置。有了动态的成员变更,静态的配置文件就不再需要了,由于系统在Raft log中管理配置;这也有潜在的错误倾向(例如新的server应该用什么配置初始化?)。相反,我们建议在第一次创建集群时,首先使用配置entry初始化一个server,将其作为log中的第一个entry。这个配置仅列举了一个server;它自己组成了它配置中的majority,因此它可以直接提交这个配置。其他servers用空log初始化;它们通过成员变更机制加入到集群并学习到当前的配置。

成员变更使得客户端使用动态的方法发现集群成为了必须;这会在第6章中讨论。

4.5 讨论

这一章描述了Raft自动处理成员变更的扩展。这是一个完整的一致性基础系统的重要部分,由于容错需求可能随着时间改变,并且失败的servers最终需要被替换。

一致性算法必须根本上引入保证集群安全的配置变更,由于新的配置会改变“majority”的意义(译者注:配置变更后成员变了,majority的组成当然也变了)。这一章战士了一个简单的方法,一次只添加或移除一个server。这些操作简单地维护了安全性,因为在更改期间至少有一个服务器与大多数服务器重叠。多个single-server改变可以组成更大程度的集群改变。Raft允许集群在成员变更期间继续正常的操作。

在配置变更期间维护可用性需要处理几个重要的问题。特别的是,服务器不再新配置中破坏有效的集群leader的问题出奇地微妙;我们在确定基于心跳的工作解决方案之前,挣扎于一些基于日志比较的不足的解决方案。

猜你喜欢

转载自blog.csdn.net/maxlovezyy/article/details/80017853