Raft 一致性算法
RSM典型实现采用复制log(replicated log)
每一个server上的存储的命令(commands)log都相同,且按相同的顺序执行命令。每个server都具有确定
性,计算同样的状态信息,因此其输出也是一致的。保证复制日志的一致性就是一致性算法的主要任务
http://thesecretlivesofdata.com/raft/
raft的出现其主要的目的使一致性算法能运用实际的系统当中,而且更具有可理解性,相比于Paxos算法(比
较难理解,需要复杂的改动才能运用实际系统当中)
raft 3种状态
terms
Terms:Raft将时间分成任意长度的,连续不断的整数。terms在raft中作为Logical clock,使所有server发
现过期信息如废弃的leader等。每一个server存储当前的term,且随着时间单调递增。server的当前term可以被
改变,在server之间通信(RPCs)当中:
如果一个server当前的term小于其他server的,会用最大的term替换当前的term;
如果一个候选server或leader的当前term已经过期,它们会立即转化成follower的状态;
如果server收到过期term的请求,将会被拒绝。
RPCs
Raft server之间通信采用远程调用方式
server如果没有收到RPC的回复,会及时重试,RPC请求都是并行发出已确保最好的性能。
raft采用心跳的机制去触发leader选举。
server 已 follower的状态启动。server会一直处于follower状态,在能收到从leader或候选者来的正常RPCs请求。
leader会周期的发送心跳给所有的follower状态的节点,以维护自己的leader角色。如果一个follower节点
在超过一个election timeout的时间内未收到心跳会认为没有可用的leader,会开始选举一个新的leader。
开始一次选举,follower节点将当前的term加1,并且转化成候选者(candidate)。紧接着会给自己投一票,
同时会并行的给其他节点发送RequestVote RPCs。candidate状态的节点会一直处于该状态直到一下任何一点发生
会触发candidate节点改变状态的任意条件:
投票:一个节点最多只能投一票,先到先得的原则[TODO,额外限制]
Election Safety:在一个给定的term时间内最多只能有一个leader产生
在等待投票的过程中,candidate节点可能会收到另外一个号称自己是leader的AppendEntries RPC.如果该
leader节点的term(包含在RPC当中)大于等于收到AppendEntries RPC的candidate节点的当前term。
则candidate节点承认其leader角色,并退居follower角色。
candidate节点在选举中可能即没有选举成功也没有失败,打成了平手:
多个follower 同时转化成了candidate,投票可能会分裂导致没有一个candidate能获得大多数投票。
当此情况出现后,每个candidate将会time out 并开始新的选举并将自己的term加1.必须采取其他措施
否则这种投票僵持的结果可能会一直持续下去。
保障措施:raft采用随机的选举超时时间去确保分裂投票很少并能很快解决。随机超时时间从一个固定
的区间产生(e.g,150-300ms)。
角色转化阶段follower 转化成candidate(first place.扼杀在摇篮中,只有一个candidate):采用保障措施
(randomized election timeout) 这样会使各个节点间在绝大多数的场景下只有一个节点超时,超时的
节点成为candidate获得选举胜利成为leader并往其他节点发送心跳,在他们超时之前。
出现了分裂投票(多candidate,而且是平手):采用保障措施(randomized election timeout),每个candidate
节点采用随机超时时间开始选举并一直处于等待状态在超时时间耗尽开启下一次新的选举前。这样会减小在
一次新的选举中再次产生分裂投票的情况。
leader产生后,开始接受客户端的请求。客户端的每次请求都包含有一个要在所有状态机rsm上执行的命令command。
leader将这些command做为新的log条目追加到自己的日志当中,然后并行分发AppendEntries RPCs到其他节点使其复制这个日志条目。
但这个日志条目被安全的复制后,leader会将该条目应用到自己的状态机中进行执行操作,然后在返回给客户端
如果follower节点crash或执行的比较慢或网络丢包,leader或一直重试发送AppendEntries RPCs
(设置在已经对客户端进行了响应后,仍然会重试)直到所有follower节点都存储这些日志条目。
日志条目包含term,可以用其检测日志中的不一致性。如下图:
Raft会确保committed的日志条目持久化,并在所有可用的节点最终被执行其中的command。
leader追踪要被commit的最高log index对应的日志条目,leader将这个log index包含在之后的AppendEntries RPCs(包括心跳)中。
一旦follower节点得知日志条目已经committed.follower节点会将这个日志条目应用到本地的状态机中(按日志顺序)
Log Matching Property
leader在一个term内一个log index最多创建一个条目并且在日志中永远不改变位置
这点通过由AppendEntries来执行的简单一致性检查保障:正在发送的AppendEntries RPC,leader包含有条目的log index和term优先于新的条目。如果follower节点在其日志中没有找到log index 和 term相同的条目,follower节点会拒绝这些新的条目。
leader处理日志不一致的措施:
leader强制follower节点复制其日志。也就意味着follower中冲突的日志条目就会被覆盖掉。[加上一个限制,这个操作将是安全的]
leader节点找到与follower节点一致的最近的日志条目,将位于此条目后的所有日志条目删除。这些操作都是对一致性检查做出的响应中触发的。
怎么着最近一致的点:
leader节点为每一个follower节点维护了一个nextIndex.当一个节点选举成为leader后,leader 为所有
follower 维护的nextIndex都是leader节点日志最后一个index值
如果follower节点与leader日志不一致,AppendEntries一致性检查将在下一次AppendEntries RPC失败。在follower拒绝后,
leader将nextIndex减1,然后在重试AppendEntries RPC.最终nextIndex会到达一个两者日志匹配的点。找到后,
删除冲突日志条目和追加从leader中过来的日志的AppendEntries将会成功。一旦成功,follower与leader两者日志一致,
在接下来的其他term也将仍然按此逻辑进行。
Leader Append-Only原则:leader永远不会覆盖或删除自己的日志
选举限制(Election restriction)
Raft采用一个比较简单的方法,在选举的阶段每次新产生的leader都必须包含之前term已经处于committed状态的所有日志条目。日志的流向,只有从leader复制到follower,且leader永远不会覆盖自己存在的日志条目。
未包含全部已经提交的日志条目的candidate,raft在投票阶段不容许其选举成功。一个candidate必须与集群中的其他节点建立连接,已获取更多的投票来到达选举成功。也就说在这些节点中必须至少有一个包含了每次被提交的日志条目。