一、集群成员变动可能带来的问题
集群成员变动是一个常见操作,次要是减少、删除节点,次要的场景有降级、服务器老化等,当然如果咱们对服务的SLA没太大要求,间接敞开集群是最简略的方法。但如果要保证系统的可用性而动静地增加、删除节点并且保障不会脑裂等问题则须要一个平安的算法,所以Raft算法把这一部分也纳入其中。
间接将集群成员配置从旧配置切到新配置会有脑裂问题,举个例子:
上图中集群原来有3个节点:Server1、Server2、Server3,当初要减少2个节点:Server4、Server5,咱们来构想下具体的操作步骤:
1、Server4和Server5是新节点,所以这2台机器启动的时候认为集群中有5个节点;
2、而后批改Server3的配置,改成5节点;
这时产生选举,即上图中红色箭头所指的地位,这时候每个节点看到的集群成员如下,为了不便形容,对立将Server字样去掉,即只保留1、2、3、4、5,1示意Server1:
1:1、2、3
2:1、2、3
3:1、2、3、4、5
4:1、2、3、4、5
5:1、2、3、4、5
这时1通过2的投票能够被选举为Leader,因为1和2认为集群只有1、2、3总共3个节点;
3通过3,4,5这3个节点的投票被选举为Leader;
这样集群同时存在1和3两个Leader了。
二、变更计划
官网给的计划是二阶段变更:
集群先从旧成员配置Cold切换到一个过渡成员配置,称为独特统一(joint consensus),独特统一是旧成员配置Cold和新成员配置Cnew的组合Cold U Cnew,一旦独特统一Cold U Cnew被提交,零碎再切换到新成员配置Cnew。
具体过程如下:
Leader收到从Cold切成Cnew的成员变更申请,Leader分两步操作:
1、提交配置Cold U Cnew
Leader在本地生成一个新的日志,这个日志的类型是成员配置,其内容是Cold∪Cnew,代表以后时刻新旧成员配置共存,写入本地日志,但并不提交;
Leader同时将该日志复制至Cold∪Cnew中的所有正本,在此之后新的日志同步须要保障失去Cold和Cnew两个多数派的确认;
Follower收到Cold∪Cnew的日志后更新本地日志,并且马上就以该配置作为本人的成员配置;
如果Cold和Cnew中的两个多数派确认了Cold U Cnew这条日志,Leader就提交这条日志;
2、提交Cnew
接下来Leader生成一条新的日志条目,类型也是成员变更,其内容是新成员配置Cnew,同样将该日志条目先写入本地日志,同时复制到Follower上;
Follower收到新成员配置Cnew后,将其写入日志,并且马上就以该配置作为本人的成员配置,并且如果发现自己不在Cnew这个成员配置中会主动退出。
能够看到,Raft算法将成员配置的变动也作为一条日志,须要通过一轮Raft过程像利用操作一样只有大多数节点确认了就必定不会出出脑裂了。留神Follower收到配置后马上就变更,而不须要等Leader下次发送commitIndex的时候才提交,这点是和失常利用提交不一样的中央。
这里不去做具体的证实,官网有具体阐明,咱们能够看下几个异样,还是以下面的例子,以后集群有3个节点:1、2、3,当初要减少2个节点:4、5,假如以后Leader为1,具体过程如下:
1)节点1收到成员变更的申请,生成一条日志类型为成员变更,内容为:1、2、3、4、5,节点1将这条日志先保留到本地,但不马上更改成员配置;
2)节点1将上述日志同步给2,3,4,5四个节点;
3)2、3、4、5节点收到配置后做2件事:追加日志,马上变更本人的成员配置为:1、2、3、4、5;
4)节点1只有收到2个以上节点回复马上将本人成员配置为:1、2、3、4、5.
咱们看有哪些异样:
如果有1-2过程中失败了,则整个过程就算失败,则须要管理员从新发动成员变更操作;
如果第3步只有1个节点即不是集群少数节点收到变更,这个时候节点1挂了,如果收到日志的先发动选举则有可能推动这条日志,否则就不胜利,即有可能失落;
如果在第4步节点1挂了的话,新集群必定是有这个配置的,因为依据日志最新准则,新选举进去的Leader必定蕴含下面成员变更的日志。
三、其它问题
1、新加的成员入无日志
一开始的时候新的服务器可能没有任何日志条目,如果它们在这个状态下退出到集群中,那么它们须要一段时间来更新追赶,在这个阶段它们还不能提交新的日志条目,这个时候节点没有投票权,有的文章说叫Catch-Up。
2、移除不在 Cnew 中的服务器可能会扰乱集群
次要是移除的状况,构想一个集群有5个节点:1,2,3,4,以后Leader是2,当初要把1移除掉,如果2曾经将新的成员配置:2,3,4曾经同 步给3和4了,如果配置不发给1,因为2认为集群中1不存在了,所以不会向1发心跳,而1没收到2的心跳,会减少本人的Term发动选举,其它成员收到后会进化成Follower,不过不会给1投票,因为1的日志不是最新的,不过这会影响集群的可用性。
针对这个问题,官网给的是PreVote,即在发动投票的RequestVote RPC申请之前再发一个PreVote申请,只有通过这个才正式发动RequestVote,这样能够大大晋升零碎的可用性。其实还有1个更简略的方法,间接将1关机或者将服务程序Kill掉。