Raft论文-读后记录
# Raft论文-读后记录
可以看一下MIT6.824公开课的视频https://www.youtube.com/channel/UC_7WrbZTCODu1o_kfUMq88g
# 1、Raft的正常工作流程:
1、客户端发送请求到服务端,此时服务端有多台机器,如果接收请求的是服务端不是领导者,那么会拒绝此次请求,并把领导者ip返回给客户端(客户端拿到领导者的ip之后请求领导者,如果此时领导者服务崩溃,那么客户端响应会超时,就会重复之前的操作,重新请求,然后获取领导者IP,直到请求成功),如果接受者是领导者,则直接进行后续请求
2、领导者接收到客户端请求之后,也就是接收到一个日志消息,共识模块会把消息添加到本机日志中,并与其它的追随者的共识模块进行通信,发送当前的日志消息,当大部分的追随者都进行了消息的响应,那么共识模块就会把当前日志提交到状态机中,并返回客户端。一旦提交,就意味着此次通信完成。
3、领导者提交之后,追随者怎么知道这个日志已经提交了呢? 那就是领导者需要把当前消息已经提交的消息也要同步给追随者,这个同步会在下一次的AppendEntries RPC中携带最大的commit id(有可能是心跳,也有可能是下一次的客户端请求)
# 2、Raft的选举流程
每个节点内部有三个定时器,包括 选举定时器、心跳定时器、RPC响应定时器
节点刚开始都是【Follower】状态,随机选举定时器开始工作,如果定时器结束没有收到任何心跳,则最开始结束的【follower】节点转换状态变成后候选者【candidate】,变成候选者之后做了三件事,增加任期、为自己投票、发送RequestVote RPC,候选者会一直持续这种状态,直到发生了以下的其中一种情况
1、超过半数的【follower】进行投票响应,推选当前节点成为【leader】--->成为【leader】之后,发送心跳
2、收到其它节点的心跳,代表着当前集群已经存在【leader】,当前节点状态退回【follower】
3、一段时间过后没有任何反应,则代表发生了投票分割,则会继续重复进行投票
选举过程中可能出现的情况:Q&A
Q:【follower】节点收到了多个投票的请求(RequestVote RPC)?
A:每个follower节点在一个任期内只能投票一次,先到先得
Q:Raft如何保证选举出来的领导者包含最新的日志数据?
A:RequestVote RPC包含了候选人日志的信息,如果追随者收到了候选人的投票RPC,会比较自己的日志最大下标是否大于候选人的日志下标,如果大于,则代表自己的日志比候选人的日志还要更新,然后会拒绝投票,避免日志更少的节点成为领导者。
Q:【candidate】候选节点收到了来自其它节点的AppendEntries RPC,就一定会退出选举吗?
A: 候选节点收到AppendEntries RPC的时候,会先比较当前RPC的任期是否大于等于自己的任期,如果小于则拒绝当前RPC,继续候选状态; 如果大于等于当前任期,则退出选举,变为【Follower】状态,并重置自己的选举定时器。(AppendEntries RPC会重置follower的选举定时器
)
# 3、Raft的两个RPC都携带了哪些数据
Raft中只有两类RPC。AppendEntries RPC 和 RequestVote RPC
AppendEntries RPC携带的数据包括:
1、term 领导人的任期
2、leaderId 领导人 ID 因此跟随者可以对客户端进行重定向
3、prevLogIndex 紧邻新日志条目之前的那个日志条目的索引
4、prevLogTerm 紧邻新日志条目之前的那个日志条目的任期
5、entries[] 需要被保存的日志条目(被当做心跳使用时,则日志条目内容为空;为了提高效率可能一次性发送多个)
6、leaderCommit 领导人的已知已提交的最高的日志条目的索引
返回值:
1、term 当前任期,对于领导人而言 它会更新自己的任期
2、success 如果跟随者所含有的条目和 prevLogIndex 以及 prevLogTerm 匹配上了,则为 true
RequestVote RPC携带的数据包括:
1、term 候选人的任期号
2、candidateId 请求选票的候选人的 ID
3、lastLogIndex 候选人的最后日志条目的索引值
4、lastLogTerm 候选人最后日志条目的任期号
返回值:
1、term 当前任期号,以便于候选人去更新自己的任期号
2、voteGranted 候选人赢得了此张选票时为真
# 4、领导者崩溃了会发生什么?
客户端请求领导者之后,领导者会做出一下的动作:
1、保存本地日志,发送AppendEntries RPC,把日志复制给追随者
2、收到大部分的追随者响应之后,提交当前日志,响应客户端
3、在下次的AppendEntries RPC中包含了提交id,可以让追随者也提交本地的日志
在每个阶段,领导者都有可能出现崩溃,那么在每个阶段出现崩溃,Raft是如何处理的?
1、如果是数据到达领导者,保存了本地日志,但是还未发出AppendEntries RPC,领导者崩溃了,如何处理?
A:这个阶段挂掉,数据属于未提交状态,客户端就不会收到响应,那么整个消息无效。客户端会重新发起请求,集群重新选主后,可以重新接收当前请求并正常提交。原先的领导者恢复之后作为追随者重新从新的任期的领导者同步消息,保持一致
2、如果领导者已经发送了AppendEntries RPC,日志已经复制到了客户端,但是还未收到大部分的响应,这时候挂掉了,Raft是如何处理的?
A:这个时候领导者挂掉,但是追随者大部分已经有了日志记录,只是未提交状态。对于客户端来说,会进行重新发起请求,集群重新选主后,未提交的日志会重新提交,而接收到客户端相同的数据请求,也可以重复处理,(Raft要求RPC具备幂等性)
3、如果领导者已经发送了AppendEntries RPC,日志已经复制到了客户端,并且领导者已经提交了日志,但是还没有把提交RPC发送给追随者,这时候挂掉了,Raft是如何处理的?
A:如果此时领导者挂掉,跟随者拥有未提交的日志,进行重新选举,选举出新的领导者之后,如果没有新的请求进来,那么这些未提交的日志不会进行处理,如果有新的日志请求,那么会把这些未提交的日志统一全部提交,最终属于分布式一致性的状态
# 5、Java实现
网上有一个开源的实现 https://github.com/stateIs0/lu-raft-kv