乐趣区

Raft算法选举流程及情景分析

Raft 算法分为两个阶段

领导者选举

三个角色

Follower、Candidate、Leader(顺序有先后)
三个角色不能越级,即 Leader 只能退化为 Follower、Follower 只能升级为 Candidate、Candidate 可以退化为 Follwer、也可以当选成为 Leader。(怎么转换的?term、timeout 会不会变?)

假设 ABC 三个节点组成的系统启动之初:
1. 假设整个系统启动,也就是没有持久化之前的任何数据(持久化哪些数据),那么三个节点的角色都会被初始化为 Follwer,随后开始计算超时时间(该时间为 150-200ms 随机值), 当达到超时时间后,节点转变自己的角色为 Candidate,发起 RPC 请求投票,先来看一下请求投票的参数:
term:发起者此时的 term(term?在什么情况下会自增?)
candidateId:发起者的 ID
lastLogIndex:当前节点的最新日志(logIndex)的上一条的 Index(logIndex-1),理解为下标
lastLogTerm:上述日志对应的 term

返回的结果:
term:接受者的 term, 这个 term 要注意,是处理完请求后的 term,比如你的 term 比我大请求我投票,那么我返回的将是最新的 term,这种情况我认为是用来处理,当一个候选者请求某节点投票,但这个节点已经投给了别人,更新了自己的 term 且比后来的这个候选者 term 更大
voteGranted:是否投票,为 True 证明投票给我

当发起投票后,会有三种可能:
1. 候选者收到了超过半数者的同意,成功当选,那么候选者升级为 leader,重置自己的超时时间。(有哪些重置自己超时时间的机会?)
2. 候选者收到了别人的投票请求,且别人的 term 比自己的大,那么候选者退化为 follower。
3. 没有消息,比如网络失败,或者两个人同时发起请求,都没有达到半数,那么将会在达到超时后在此重试,rpc 调用失败的情况会直接再次对该节点发起请求。

当候选者把请求发给其他人时,其他人做什么操作呢?
1. 有两种情况,会直接拒绝投票:
发起者的 Term 还没有自己的大,说明发起者早就落后了,这种情况直接拒绝。
发起者和自己的 Term 相同,但是别人到的早,因为一个 Term 只能给一个人投票,那么我只有直接拒绝。
还有一种情况是,我发现这个 term 里我已经投给你了,那么也会返回 true,因为可能是上一次的回复消息把消息丢掉了。
2. 还有几种情况需要进一步判断
Term 相同,且没有投票给别人 或者 别人的 Term 比自己大,不管我之前投没投过,都要状态转换为 follower,且 reset 超时时间。
这两种情况要进一步判断,上一条 log 和 term 的消息。
先去拿到发起者那里上一条 logindex 和他的 term,然后找到自己手里这一条 term 所对应的 term。如果我的 term 比他的大,那么说明我这里有更新的人当选,如果 term 相等,就比较 log 的数量,谁的最新就以谁的为准,如果我的 term 小于他,说明在他那里有更新的 leader 产生。

理论依据:1. 相同的 term 和相同的 index 一定存储一样的数据 2. 相同 term 和相同 index 之前的数据一定也是相同的
raft 算法可以保证在同一个 term 的数据,一定是只有一个 leader 给出来的,每一条数据只会创建一个 logentry 且位置不会再改变。
但是这个位置可能被覆盖,如果还没有提交的话,如果有更大 term 的 leader 产生的话,有可能会以此比较发现你这里的 log 无效,进而把这里给替换掉。
这也就是为什么要比较 lastLogIndex 和 lastLogTerm 的原因。

场景分析:
1.ABCDE,A 在 term1 当选 leader,虽后 DE 发生网络分区,但是 DE 之间无法选出来主,那么他们的 term 会比较高,那么重新连接网络后,会当选吗?
不会,因为他们的上一个 log 对应的 term 比 ABC 低。
2. 前提相同,后来 AB 发生分区,CDE 重新选主,A 接受了很多 log,然后 A 不断重启,自身 term 很大,此时 term 也大,log 也多,能被选吗?不能,原因同上。他的所有 log 都是在 term1 接受到的,不可能在高 term 接到,因为他既然是高 term,就不可能是主,非主不接 log
3.

ABCDE,A 为主,接受了 logIndex1,term1,并且同步给其他人,随后 crash,但是 A 再次为主,此时 term2,并且又接受了一个 logindex2,但是同步给 AB 后又 crash 了。
后 E 为主,在 term3 当选,为什么是 term3,因为 BCD 里给 A 投过第二次票,肯定知道目前最新是 term2。接到了一个 index2,但是没同步,就 crash 了。为什么能当选,首先 term 为 3,lastLoginIndex 是 1,在其他人那里 term 比自己大,index1 对应的 term 也是 1,term 相同,E 的 logIndex 相等,所以 CDE 会投票,B 不会投,因为 log 不如她。
然后 A 又当主了,然后 A 继续复制 logindex2 到其他人,此时 ABC 都复制到了,那么此时 index2 能标记提交了吗?
不能。因为如果 A 又 crash 了,那么 E 再次当选,为什么再次当选?
因为 E 的新 term 为 4,且 logIndex 为 2,首先 term 大,且上一条 logIndex 对应的 term 为为 #,所以 BCDE 都会选。
这个时候,logindex2 会覆盖他们,如果之前的 term2log 被 commit
,就会导致数据不一致。
怎么办,不主动去 commit,如果 crash,没提交就覆盖不会有问题。
如果 crash 了,但是 A 当选,term4 接了更新的 log,在这次同步的时候,会把上一次 commit 的提交过去,这个时候因为 ABC 的 logIndex 更新,所以 E 不会当选。

数据同步

退出移动版