raft算法是用来解决分布式一致性问题,该算法可以看做一个Log日志复制机制的状态机,它的日志是连续的,同时集群中的日志内容始终是从 leader 节点同步到follower节点,是单向的。
下面列出有关raft的名词:
Term:逻辑时钟,term值和log index(指令id)用于保证时序,term用连续的数字进行表示。
Raft算法将Server划分为3种状态:
Leader:负责Client交互和log复制,同一个term代表只有一个Leader。
Follower:被动响应请求RPC,从不主动发起请求RPC。
Candidate:一种临时的状态,只存在于leader的选举阶段,某个节点想要变成leader,那么就发起投票请求,同时自己变成candidate。
在raft算法实现中,发生远程过程调用(rpc调用)三种:
RequestVote RPC:Candidate在选举期间发起
AppendEntries RPC:Leader发起的一种心跳机制,复制日志也在该命令中完成
InstallSnapshot RPC: Leader使用该RPC来发送快照给落后太多的Candidate
Raft 将一致性算法分为了几个部分,包括领导选取(leader selection)、日志复制(log replication)、安全(safety)。
raft动画演示地址:
http://thesecretlivesofdata.com/raft/
这个动画主要包含三部分内容,第一部分介绍简单版的领导者选举和日志复制的过程,第二部分内容介绍详细版的领导者选举和日志复制的过程,第三部分内容介绍的是如果遇到网络分区(脑裂),raft 算法是如何恢复网络一致的,下面解说raft主要都是根据该动画来说明。
raft主要讲解领导选取(leader selection)、日志复制(log replication)这两个阶段。
1领导选取(leader selection)
该阶段可细分为三种状况:
简单版的领导者选举:
我们就以集群刚启动时为例。首先集群中每个节点都是Follower状态,因为没有Leader状态的节点,所以会有一个节点都变为Candidate状态发起选举,开始时都是选举自己作为Leader节点,然后该节点Term值递增,向其他节点发起RequestVote RPC请求,如果该RequestVote请求接收到n/2+1(过半数)个节点的投票,从Candidate转为Leader,向其他节点发送heartBeat以保持Leader的正常运转。
详细版的领导者选举:
我们就以集群刚启动时为例。首先集群中每个节点都是Follower状态,就会发生有节点变为Candidate状态发送自己作为Leader节点的投票,那么集群中哪个节点最先变为Candidate状态呢?raft通过设置选举超时时长(Election timeout)来确定谁先变为Candidate状态,通常这个选举超时时长范围为150ms 到 300ms,该选举超时时长是随机的,当某个节点的Election timeout 发生以后,这个节点马上变为Candidate状态,该节点Term值递增,把票(vote)投给自己,然后向其他节点发起RequestVote RPC请求,当其他Follower 节点收到该vote时,会把自身的 Election timeout重置,term值变为发起选举节点的term值,其他Follower 节点ack Candidate状态的节点,这时Candidate状态的节点马上变为Leader节点。Leader节点开始发送心跳信息给各个Follower节点,确保选举的结果是正确的。
当 Candidate状态的节点发送 vote给其他Follower 节点时, 这时候有三种可能的情况发生:
a:该RequestVote请求接收到n/2+1(过半数)个节点的投票,从Candidate转为Leader,向其他节点发送heartBeat以保持Leader的正常运转。
b:在此期间如果收到其他节点(取名为other)发送过来的AppendEntries RPC请求,如other节点的Term大则当前节点转为Follower,否则拒绝该请求。
c:Election timeout发生则Term递增,重新发起选举。
在一个Term期间每个节点只能投票一次,所以当有多个Candidate存在时就会出现每个Candidate发起的选举都存在接收到的投票数都不过半的问题,这时每个Candidate都将Term递增、重新设置Election timeout并重新发起选举,由于每个节点中Election timeout的时间都是随机的,所以就不会多次存在有多个Candidate同时发起投票的问题,这也反映了每一个term只能出现一个leader节点。
网络分区版的领导者选举:
在没有Leader节点的分区内,该分区中的所有节点开始选举Leader节点,选举过程同详细版的领导者选举一样,这时候就会有多个Leader节点。
2日志复制(log replication):
当选举出Leader节点后,集群开始提供服务,请求到来后就会发生日志复制操作。
简单版/详细版的日志复制:当客户端请求该集群,首先都会把请求转到Leader节点来处理,主服务器将客户端的命令封装成一条日志,接着通过heartbeat把该日志同步给其他Follower节点,Follower节点接收到日志后记录日志同时向Leader发送ACK,当Leader收到大多数(n/2+1)Follower节点的ACK信息后将该日志设置为已提交并追加到本地磁盘中,然后Leader节点notifies all给每一个Follower节点该日志被设置为已提交状态,所有Follower节点开始持久化该日志。其中封装的那条日志包含了命令,对应的Terms时期号和该日志在Log文件中的位置索引。这些额外的附加信息,有助于其它服务器判定日志的合法性和之后的数据同步安全逻辑和服务器选主的逻辑。
网络分区版的日志复制:
集群节点数小于原有集群节点数一半的Leader(L1)在处理日志复制的时候都是设置为未提交状态。集群节点数大于原有集群节点数一半的Leader(L2)在处理日志复制的时候都是设置为已提交状态,当网络分区消失时,Leader(L1)节点会变成Follower 节点,然后Leader(L2)会把最新的日志复制给它,Leader(L1)节点原有的日志会被覆盖。
(重点:当时面试被问raft日志怎样保证是连续的,为啥Election timeout是随机的,和zab有哪些异同点)
查看的博客
https://blog.csdn.net/kojhliang/article/details/80270223
https://www.backendcloud.cn/2017/11/12/raft-gossip/
https://raft.github.io/
https://www.cnblogs.com/binyue/p/8647733.html
https://blog.csdn.net/xu__cg/article/details/73555161
https://www.cnblogs.com/hzmark/p/raft.html
https://www.jdon.com/artichect/raft.html
https://blog.csdn.net/u013970991/article/details/76602213
https://blog.csdn.net/colorant/article/details/73887706