基于Raft 的分布式一致性协议是构建不少分布式服务的基础,某种程度上它充当了心脏的角色,为此有必要对Raft 的一些难点进行深刻理解。安全
一个常见的误解就是复制到多数副本的就能够视做commited, 其实还不够。缺乏必须已经执行了对应的操做这个步骤。我的理解在实际 Raft使用过程当中,就是存在某个Raft log 在append到多个副本的瞬间宕机了,因为尚未执行on_apply() ,其实尚未向上层用户ACK 这条日志已经成功。
所以这条日志能够truncate, 也能够后续随着后续的log commited 以后本身也被commited。网络
这个比较大规则以下:
某个follower 收到 vote 请求以后,会计算出收到请求的时间和上次收到主心跳的时间间隔,而后把这个间隔和一个约定的较小时间进行比较。若是前者还小(此时说明还有其余节点长时间没有收到主的心跳,发起了vote),拒绝此次vote 请求,不然投同意票。app
考虑三个互联互通的IDC A、B、C,B是主副本,若是因为某种缘由B、C网络不通了,若是没有上面限制,B发起带有最高term信息的vote请求,A会一样vote请求,这样主就从B变成了C。同理,过了之后,主又极可能从C再变成B。如此 反复循环,整个复制组上没有办法提供持续稳定的主。分布式
这个规则以下:
某个follower 收到 vote 请求以后,在current term和请求中的term 相等的状况下:须要使用当前节点的LastLogIndex 、LastLogTerm和请求中的LastLogIndex、LastLogTerm进行比较,若是前者大,则拒绝请求;若是后者大,则赞成请求。ide
考虑下,为啥要这样?仍是考虑上面三个互联互通的IDC A、B、C,B是主副本,若是因为某种缘由B、C网络不通了,C是划分节点。当网络划分以后,C的current Term是最高的,它极可能发起vote,这样把B Stepdown了,A和B的currentTerm 提高了;但是由于C的LogIndex很低,它发到A和B的Vote 请求,在同A、B的Last LogIndex 比较时发现本身过小,而无法拿到A、B的信任票,所以Vote失败。随后A或B再发起Vote , 就是用最高的Term(C的Term+1 )、最新的LastLogIndex(A 或B的LastLogIndex),必定会选主成功,而且拥有最新的数据。日志
Raft采用的成员变动单策略相对简单,每次只增删一个节点,这样就不会出现两个多数集合,不会形成决议冲突的状况。按照以下规则进行处理:
按照上面的规则,能够实现安全的动态节点增删,由于节点动态调整跟Leader选举是两个并行的过程,节点须要一些宽松的检查来保证选主和AppendEntries的多数集合:code
为了不同时有两个节点变动正在进行,在有未committed的change正在进行的时候,不容许进行节点变动。节点变动有一个问题,对一个只有两个节点的Cluster,发起RemovePeer。这个时候一个节点挂掉,另一个节点没有收到RemovePeer请求,这样系统将中止工做。所以强烈建议集群节点数>=3个。pdo