分布式一致性协议-Raft详解 (三):集群变更

前言

前两篇文章分别解析了Raft中领导人选举和日志复制规范,对于一个固定的集群来说,有了这些规范的保护,就可以快乐的一致运行下去了。随着业务的变化,总归会出现集群规模变更的需求。最简单的处理方式当然是停掉整个集群服务,变更配置,重启服务。有时候,这对于在线业务是无法接受的。如果不停止服务,直接变更又存在问题,比如下图添加节点的情况:


Raft集群

开始只有Server1-3,这时候我们添加2个节点,但是配置变更不可能在所有节点上同时生效。如果在箭头所在的时间点刚好触发选举,server1 和server 2的保存的配置还是只有3个节点的集群,所以server1可以通过自己和server 2的选票成为Leader。而Server5通过3、4、5的选票也超过半数,也可以成为Leader。造成存在2个Leader的错误。

Raft的解决方案

Raft协议通过在新旧配置变更中间添加过渡阶段来处理这个问题,称之为联合共识(joint consensus)阶段。在联合共识阶段,集群会做如下的约束:

  • 日志条目被复制给集群中新、老配置的所有节点
  • 新加入节点和原有节点都可以成为 leader
  • 达成一致(针对选举和提交)需要分别在两种配置上都超过半数

满足了上面的条件,集群就可以在切换的同时仍然对外提供服务。下面用具体场景来看下Raft的解决方案。

添加节点

当前有一个3个节点的集群,现在要往集群中添加2个节点。Raft通过发送日志的方式来发送配置变更日志指令。
发送联合共识日志
Leader首先向集群中的节点发送一条新的日志,日志内的指令就是配置变更。这条日志跟普通的日志一样复制给Follower节点,但是提交后不会对状态机的数据有任何改动,因为它不是数据操作指令。当前的状态如下图:

配置变更 Phase 1:原集群中S1是Leader,新增两个节点S4,S5,新启动的节点角色一定是Follower。Leader向集群中复制一个C(o+n)的日志到所有节点

上图中C(o+n)的日志就是配置变更日志,告诉其它几点集群需要进入old和new共存的联合共识阶段。跟处理普通的日志条目不一样,节点在收到C(o+n)的日志后,立马就进入联合共识阶段,而不需要等到Leader提交这条日志。也就是说,上图中S1,S4和S5进入联合共识阶段,而S2,S3因为还没收到日志,所以还处于旧配置阶段。
按照前面讲的,进入联合共识阶段后,Raft要求任何决议的达成必须在新老配置中都达成半数。对于上图中的S1来说,因为它已经处于联合共识阶段,所以如果它要将配置变更的日志提交,必须在老的集群(S1,S2,S3)中超过半数,在新的5个节点的集群中也要超过半数。上图中日志已经复制到3个节点,满足新集群中超过半数的要求,但是在老的3个节点的集群中未超过半数,所以这条日志现在还不能提交。
提交联合共识日志

配置变更Phase 2:Leader将C(o+n)的日志复制到新老集群中超过半数节点,提交了C(o+n)的日志后,发送一个新C(new)日志

Phase2的图中,Leader提交了C(o+n)日志,现在超过半数节点都运行在联合共识阶段,即使Leader崩溃,新选出Leader的集群也仍然会运行在联合共识阶段。这时,Leader会复制一条新集群生效(图中的C(new))的日志到集群中,告诉Follower节点现在可以切换到新的集群模式下运行了。跟C(o+n)一样,Follower也不需要等到Leader提交C(new),只要一收到日志,可以立刻切换到新集群。这里有一点需要注意,在C(o+n)提交和复制C(new)日志的这一阶段,集群仍然是正常对外提供服务的,就是说在C(new)日志发出之前,客服端发给Leader的指令可以正常提交,只是提交的条件是日志被复制到新老两个集群的超过半数节点。
提交新集群日志
配置变更Phase 3:新集群日志被提交,整个集群扩容完成

在开始复制C(new)日志后,Leader已经运行于新的集群中,所以只要C(new)被复制到不少于3个节点,就可以提交了。之后整个集群的扩容就完成了。
扩容安全性

  1. 在上面的Phase 1阶段,如果Leader崩溃了,会不会出现多个Leader?
    Raft协议中规定,对于配置变更的日志,不需要等到提交,各个节点只要收到了就按新的配置运行。在Phase 1阶段,如果S1崩溃了,S2和S3处于旧配置阶段,假设它们中的S2变成候选人,收到S3的选票即超过半数,当选Leader。S4和S5处于联合共识阶段,假设S4变成候选人,那么它必须同时在新旧两个集群超过半数。假设现在S1崩溃后迅速重启并加入集群,那么在新集群中,S4可以收到S1和S5的选票,超过半数;在旧集群中,收到S1的选票,但是无法收到S2或者S3的选票,因为在同一轮选举中,S2和S3已经把票投给了S2。所以,S4无法赢得旧集群的选举,不会出现两个Leader的情况。
  2. 在Phase 1阶段,如果Leader崩溃,新当选的Leader会继续提交C(o+n)吗?
    答案是不一定。如果是S2当选Leader,因为它没有C(o+n)的日志,按照上篇文章讲到的日志复制规范,S4和S5上的C(o+n)会被删除,所以不会提交。客户端必须重新发送配置变更指令。如果是S4当选Leader,会继续复制C(o+n)的日志,配置变更流程会继续进行。这两种情况都是符合Raft规范的。
  3. 在Phase 2阶段Leader崩溃了,已经提交的C(o+n)会受到影响吗?
    不会。Raft协议承诺已提交的日志永久生效。S1在Phase 2崩溃,在老的集群节点中,因为S2有最新的日志,所以只有S2可以当选新的Leader。在新加到集群的节点中,无论是S4还是S5成为新的Leader,都会继续复制和提交C(o+n)
  4. 在Phase 3中,按照Raft协议Leader只要把C(new)日志发出,后续的客户端指令就可以按新配置来提交,会不会造成提交的日志丢失。
    答案是不会,如下图所示,S1发出C(new)的日志后,收到客户端日志:


    C(new)下收到新日志

    这时候新日志被提交的条件是在新的集群中,超过半数节点确认收到日志,因为Raft要求日志必须是按顺序复制,所有收到新日志的节点必然也收到了C(new)的日志,如下图:


    提交新的日志

    如果Leader在此时崩溃,S3和S5按照新旧集群都超过半数的原则进行选举,S2和S4按照新集群超过半数的原则进行选举。只有S2和S4可以当选,因为只有它们才有最新的日志。所以C(new)后提交的日志不会丢失。

删除节点

对于集群缩容的情况,由于缩容后的集群中可能不包含当前Leader节点,所以复杂度进一步上升,我们解析下Raft如何应对这种情况。
当前有一个5个节点的集群,现在将集群缩容至3个节点,如下图所示:


图1:Leader将C(o+n)提交后,发送C(new)的日志

上图中,旧集群S1是Leader,新集群中将会把S1和S5移除。首先Leader发送C(o+n)日志至其它节点,当超过半数节点确实收到后,将C(o+n)提交。然后,Leader开始发送C(new)至其它节点,按照Raft的规范,只要C(new)日志一发出,新配置在Leader上就生效了。S1虽然已经不属于新集群,但是因为C(new)还没提交,所以S1仍然行驶Leader的职责,只是计算超过半数的时候,它自己不计算在内。如上图中的状态,S1已经将C(new)复制到2个节点,但是还不能提交,因为它自己不是新集群中的节点,不计算在内。直到S1将C(new)发送到新集群(S2,S3,S4)的超过半数节点,就可以提交C(new),提交之后S1就退位了,之后由新集群中的节点重新选举Leader。
缩容安全性
在上面的图中,如果客户端发来新的日志如何提交?
如果在S1复制C(new)之后提交C(new)之前,收到客户端的日志,S1工作在新的集群配置下,所以会将日志复制到新集群的节点上,当收到新集群(不包含S1)超过半数节点确认后,就可以提交日志。所以在缩容期间集群仍然可用。

可用性

Raft在集群配置变更时,为了提高可用性,还有以下两个问题需要解决:

  1. 新节点同步数据期间无法提交新日志,造成集群短时间不可用
    这个问题发生在有新节点加入集群,并且初始化数据较多,从Leader同步数据需要时间很长的情况下。因为Raft中日志必须按顺序同步,在老的日志没有同步完成之前,不会接收Leader提交的新的日志。导致客户端新发来的指令迟迟达不到半数,无法提交。
    针对这种情况,Raft规定在新节点同步数据期间,可以以没有投票权的方式加入进来(这里类似于ZooKeeper中的Observer节点),比如一共5个节点,有2个节点在同步数据阶段,那Leader可以按3个节点的集群来组织投票。等到数据同步完成后,再正式按正常节点来处理。
  2. 被删除的服务器在Leader进入C-new阶段后将不再收到心跳,会触发选举使Leader失效。
    这就是上面讲的删除节点时图1中的情况。S5将要被集群移除,Leader在发送C(new)之后就进入新集群阶段,不会向S5发送心跳和日志。S5在等待一段时间后没有收到Leader的心跳和日志,会以为超时从而将自己作为候选人向其它节点发投票申请。对于图1中的S3和S4来说,因为还没收到C(new),可以当成是新一轮的选举开始,从而使新集群进入选举阶段。
    为了避免这个问题,Raft规定,当节点确认当前领导人存在时,服务器会忽略请求投票的 RPCs。确认领导人存在的方法就是,收到投票RPC的时间距离上次收到Leader心跳或日志的时间还不到最小选举超时时间,那可以拒绝这个请求。最小选举超时时间请参考之前的Raft详解(一)。

总结

Raft协议对于集群变更的规范,充分体现了它的初衷,就是定义一种易于理解和工程实现的一致性算法。至此,用了3篇文章做了Raft协议的解析,后续会将重点放在基于Raft协议实现的系统,如etcd的源码解读。

链接:
分布式一致性协议-Raft详解 (一)
分布式一致性协议-Raft详解 (二)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,386评论 6 479
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,939评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,851评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,953评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,971评论 5 369
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,784评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,126评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,765评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,148评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,744评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,858评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,479评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,080评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,053评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,278评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,245评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,590评论 2 343

推荐阅读更多精彩内容