Raft配置变更及Pre-Vote

文章讲述了在分布式系统中,如何通过Raft协议的两阶段协议处理节点配置变更,特别是涉及Pre-Vote阶段以防止旧集群干扰和网络分区带来的问题。还讨论了配置变更中存在的Bug及解决方案,如提交no-op空日志和使用学习者状态提高可用性。

随着时间推移,需要进行节点配置变更,需要安全、自动的方式添加节点到集群.
通常系统配置是由每台服务器的id和地址组成,之所以不能直接从旧配置切换到新配置,在于可能导致出现矛盾的多数派。
如图
请添加图片描述

系统中本来以3台服务器的配置运行,此时系统管理员添加两台服务器。如果系统管理员直接修改配置,那么集群中的节点可能无法完全在同一时间做到配置切换,这会导致服务器S1和S2形成旧集群的多数派,而同一时间服务器S3、S4、S5切换到新配置,会产生两个不同的多数派。
需要使用两阶段(two-phase)协议,Raft通过联合共识来完成两阶段协议,即让新、旧两种配置都获得多数派选票。

如图所示,在第一阶段,领导者收到CnewC_{new}Cnew的配置变更请求后,先写入一条Cold+newC_{old+new}Cold+new的日志,配置变更立即生效。然后领导者将日志通过AppendEntries消息复制到跟随者上,收到Cold+newC_{old+new}Cold+new日志的节点立即应用该配置作为当前节点的配置;当Cold+newC_{old+new}Cold+new日志被复制到多数派节点上时,Cold+newC_{old+new}Cold+new的日志就被领导者提交。请添加图片描述
Cold+newC_{old+new}Cold+new日志已提交保证了后续任何领导者一定保存了Cold+newC_{old+new}Cold+new日志,领导者选举必须获得旧配置中的多数派和新配置中的多数派同时投票。
Cold+newC_{old+new}Cold+new日志提交后,进入第二阶段,领导者立即写入一条CnewC_{new}Cnew的日志,并将该日志通过AppendEntries消息复制到跟随者上,收到CnewC_{new}Cnew日志的跟随者立即应用该配置作为当前节点的配置。CnewC_{new}Cnew日志复制到多数派节点上时,CnewC_{new}Cnew日志被领导者提交。在CnewC_{new}Cnew日志提交后,后续配置都基于CnewC_{new}Cnew
值得注意的是,配置变更过程中,来自新旧配置的节点都有可能成为领导者。如果当前领导者不在CnewC_{new}Cnew的配置中,一旦CnewC_{new}Cnew提交,则它必须下台(step down)。

如图所示,旧领导者不再是新配置的成员后,还有可能继续服务一小段时间,即旧领导者可能在CnewC_{new}Cnew配置下继续当领导者,直到CnewC_{new}Cnew的日志被复制到多数派上并提交后,旧领导者就要下台。请添加图片描述
如果没有额外的机制,配置变更可能扰乱集群。举例来说,如果领导者创建了CnewC_{new}Cnew日志条目,那么不在CnewC_{new}Cnew中的跟随者不再收到心跳,如果该服务器没有关闭或杀死进程,那么跟随着会超时并开始新的选举。此外,该跟随者不会收到CnewC_{new}Cnew日志条目,它不知道自己已经被移除,它将带有新任期号的RequestVote消息发送给新集群中的领导者,(旧领导者知道新领导者的地址),这会导致当前领导者转为跟随者状态,虽然旧领导者因为日志不完整无法选举成为领导者,但也会影响新的领导者重新选举。这个过程可能一直重复,导致系统可用性变差,如果同时移除多个节点,那么情况可能更糟糕。

解决方式

这个问题的解决方式是加入一个Pre-Vote阶段,在任何节点发起选举之前,先发出Pre-Vote请求询问整个系统。收到消息的节点根据日志判断它是否真的有机会赢得选举而不是浪费时间(判断逻辑和是否投票相同,即请求参数中的任期更大,或者任期相同但日志索引更大)。如果超过半数的节点同意Pre-Vote请求,则发起请求的节点才能真正发起新的选举。处于旧配置的集群由于日志不会更新,其他节点拒绝它的Pre-Vote请求。
但只根据日志来判断并不能彻底解决该问题,如果领导者还没有把CnewC_{new}Cnew复制到多数派节点,那么此时新集群的跟随者S2和S3依旧会同意就集群的节点S1发起的Pre-Vote请求,S2将继续发起选举,干扰真正的领导者S4,使其转变为跟随者,影响集群的正常工作,如图所示。
请添加图片描述
因此需要增强Pre-Vote请求的判断条件,需要通过心跳来判断是否存在一个活跃的领导者,如果存在,则拒绝向该节点投票。如果一个服务器在选举超时时间内收到领导者的心跳,那么它不会同意Pre-Vote请求,它可以不回复Pre-Vote请求或者直接回复拒绝。虽然这会导致每个服务器在开始选举之前,至少要多等待一个选举超时时间,但这有助于避免来自旧集群的服务器的干扰。
总结,一个节点同意Pre-Vote请求的条件是:

  • 参数中的任期更大,或者任期相同但日志索引更大
  • 至少一次选举超时时间内没有收到领导者的心跳.

Pre-Vote在网络分区的作用

在网络分区时,可能导致某个孤立节点的任期不断增大,因为超时没有收到心跳,进入选举。网络恢复后,由于该节点的任期大于领导者任期,导致领导者转入跟随者状态,但由于该节点的日志并非最新的不可能成为领导者,只会干扰系统的运行。通过新增Pre-Vote阶段,该节点在孤岛情况下, 无法获得多数派的投票,自然无法发起选举,也不会一直增加自己的任期。待网络恢复后,就可以顺利地以跟随者的身份加入集群。
Pre-Vote节点不仅可以解决配置变更干扰领导者的问题,还可以有效解决网络分区时脑裂和任期爆炸的问题。

etcd中完整地实现了Pre-Vote阶段。将候选者状态细分为预候选者(PreCandidate)和候选者,前者是发送Pre-Vote请求时的状态,不会增加自己的任期;而后者是RequestVote请求时的状态,会增加自己的任期。

配置变更存在的Bug

假设一个Raft集群包括4个节点,此时进行状态变更。系统初始状态如下。
请添加图片描述
节点日志中有一个旧的集群配置C,C记录集群当前配置为{S1,S2,S3,S4}.
S5是新加入的节点,S1是领导者,S1将新的配置D写入本地存储,D中配置为{S1,S2,S3,S4,S5},因此,领导者S1还会将旧的配置C和新的配置D复制给S5.
请添加图片描述
之后发生网络分区,领导者S1被隔离,S2发起新一轮选举,获得S2、S3、S4的选票,成为新的领导者。状态如下
请添加图片描述

此时S2收到一份新配置E,E的配置信息为{S2,S3,S4},S2将新配置E复制到S3,此时节点配置如下
请添加图片描述
由于配置E只有3个节点,配置E已经复制到其中2个,满足多数派条件。此时S2认为可以提交该配置。
接着网络分区恢复,S1发起新的选举,获得来自S1、S4和S5的选票,成为任期3的领导者。但此时S1并不包含已提交的新配置E,如图所示。
请添加图片描述
新的领导者S1复制配置D到所有节点,覆盖已提交的配置E并提交。

请添加图片描述
对于这条索引,算法提交了2次不同的日志,违反了日志安全性。
这个问题可以通过提交no-op空日志解决。新领导者必须提交一条no-op空日志,才能将配置写入日志。当S1想成为任期3的领导时,S4会存在一条任期为2的no-op空日志,于是拒绝给S1投票,S1就无法成为新的领导者。
为了保证配置变更的正确性,一次配置变更需要提交一条no-op空日志和一条与配置相关的日志。
配置变更是最危险的操作之一,尤其添加成员可能会改变达成多数派的数量,因此建议一次只添加或减少一个节点,并且推荐先减少成员来替换发生故障的节点。
为了提高可用性,Raft引入了学习者状态,学习者加入集群不参加投票,知道它的日志和状态追赶上了领导者,才参与决策。etcd中实现了学习者,用来减少配置变更时的可用性问题。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值