分布式一致性算法-Raft学习笔记

分布式一致性算法-Raft学习笔记

版权声明:本文为博主原创文章,未经博主允许不得转载。

手动码字不易,请大家尊重劳动成果,谢谢

作者:http://blog.csdn.net/wang_wbq

Raft在Leader选举阶段使用term编号作为提案编号来执行paxos算法进行leader选举,为了防止活锁出现,Raft算法使用了随机定时器的策略避开了同时竞争Leader的可能。在日至提交阶段,算法保证了Leader要拥有全部日志,并且Client只能与Leader交流,提交日志。Leader利用两阶段提交和大多数集合确认的方式来确保日志被成功保存。其算法和Zookeeper的ZAB算法有一定相似性。

Raft算法中角色和存储

不同于Paxos算法,Raft算法中只有一种角色。这个角色可以有三种状态:
1、Follower
2、Candidate
3、Leader

在每台机器上都会存储:
1、currentTerm:当前节点所能看到的最大的term值,该值单调增加
2、votedFor:当前term里将票投给的对象,如果尚未投票则为空
3、log[]:日志条目,会按顺序作用于状态机
4、commitIndex:当前节点最后一个被提交的日志序号
5、lastApplied:当前节点最后一条被应用于状态机的日志序号,如果发现当前机器commitIndex > lastApplied则应该将本机log[]中序号为(lastApplied, commitIndex]的部分应用到状态机
6、snapshot:如果做了日志快照则会存储快照镜像

在状态为Leader机器上会额外存储:
1、nextIndex[]:针对每个其他节点,下一个需要发送的日志的序号
2、matchIndex[]:针对每个其他节点,当前所知的和Leader匹配的最大日志编号

Raft算法中的相关概念

1、Leader:当前集群中的领导者(干活最多的),一个Raft集群只能有一个Leader持久存在
2、Candidate:Leader候选人,当接收到多数投票后会成为Leader
3、Follower:跟随者,接受Leader的日志存储、应用请求,拥护Leader的地位
4、term:Leader的选举周期(假设所有机器都是从0开始),在一个Raft集群内单调递增。在一个term周期内,只能有一个Leader当选。这个概念和paxos算法中的提案号一致,在选举过程中使用paxos算法,将term作为提案号申请自己作为Leader。
5、State machine replication:Raft使用状态机复制来实现日志的一致和容错,对多个相同的状态机施以相同的事件序列,所得到的最终状态也是一致的
6、Client:客户端,Raft集群的服务对象,客户端会请求Raft集群去存储自己的信息来保证分布式一致性。

Raft算法中的消息种类

1、RequestVote:请求其他节点投票给自己
请求参数:
term:当前节点选举周期term值
candidateId:当前节点编号
lastLogIndex:当前节点最后一个日志的索引
lastLogTerm:当前节点最后一个日志的term周期

返回值:
term:接收投票节点的term值
voteGranted:是否投票给该申请节点

2、AppendEntries:Leader节点使用该消息向其他节点同步日志,并且发送空消息作为心跳包以维持Leader的统治地位。
请求参数:
term:此Leader所在的选举周期term值
leaderId:此Leader的节点编号
prevLogIndex:当前发送的日志的前面一个日志的索引
prevLogTerm:当前发送的日志的前面一个日志的term值
entries[]:需要个节点存储的日志序列
leaderCommit:此Leader已经提交给状态机的最大日志索引号

返回值:
term:接收日志节点的term值
success:如果接收日志节点的log[]结构中prevLogIndex索引处含有日志并且该日志的term等于prevLogTerm则返回true,否则false

3、InstallSnapshot:用来向节点安装快照的消息。快照可代表状态机依次应用了从0到第n个日志后的状态,发送快照可能会减少新节点的同步周期。
参数等请参考论文。

4、以上为论文中提到的RPC消息,除了这些,一个实际应用的集群还会有很对其他的消息,如:添加、删除节点,获取状态机状态等。大多都是Client与Leader的交互请求,可以根据需要在实现算法时添加。

Raft算法的事件和状态转换

在Raft集群启动时各个节点:
1、可以获取整个Raft集群的所有节点连接信息
2、currentTerm初始为0、votedFor初始为空
3、初始状态为Follower
4、如果是重新启动则有快照和日志序列,如果为新集群则全部为空
5、启动随机定时器,定时器超时时间在[m, n]范围内,保证请求传输时间 << [m, n] << 平均一个服务器两次出现宕机的时间间隔


一旦一个定时器超时,其会转换为Candidate状态。转换时执行:
1、重置随机定时器
2、currentTerm自增1
3、给自己投一票(votedFor设置为当前节点)
4、向所有其他节点发送RequestVote消息。


在Candidate状态下接收到RequestVote的返回结果:
1、得到了超过半数节点的同意(voteGranted为true)则该节点当选Leader,转换自己的状态到Leader,并关闭随机定时器,周期发送AppendEntries给其他机器以保持领导地位。

2、无论是否已经成为Leader,接收到的RequestVote返回消息中的term > currentTerm则设置currentTerm = term,并将自己转换为Follower状态,并重置随机定时器

3、定时器超时前没收到大多数投票则返回上述定时器超时逻辑开始下一轮投票。


在切换到Leader状态时,执行:
1、关闭随机定时器
2、将nextIndex[]中的值全部设置为自己最后一条日志的Index + 1
3、定期向其他机器发送AppendEntries(发送周期小于随机定时器最小超时时间,最好超时周期为发送周期的三倍左右):
(1)term等于此Leader的currentTerm
(2)leaderId等于此Leader的节点编号
(3)prevLogIndex等于nextIndex[]中该节点对应的值-1
(4)prevLogTerm等于此Leader的log[]中prevLogIndex对应日志的term值
(5)如果prevLogIndex+1不是此Leader的log[]中最后一条日志,则 entries[]取log[]中prevLogIndex之后紧接着的部分日志。
(6)leaderCommit等于此Leader的commitIndex


在Leader状态下接收到AppendEntries的返回结果:
1、返回消息中的term > currentTerm则设置currentTerm = term,并将自己转换为Follower状态,并重置随机定时器

2、返回消息中success为false则将该节点在nextIndex[]对应的值减1

3、如果接收到大多数Follower成功反馈,则可以提交该条AppendEntries所同步的所有日志,和此之前的所有日志。Leader只允许提交当前term的日志,不允许提交之前term的日志,但是可以通过提交当前term的日志达到间接提交之前term的日志的目的。

因为Leader不允许提交之前term的日志,因此在Leader被选举成功时可以发送一条无意义日志给其他机器,以更新日志列表中的最大term编号,当接收到大多数返回时提交该日志,以达到提交之前已被大多数节点接受的日志的目的


一个节点(无论当前是什么状态)接收到RequestVote(term, candidateId, lastLogIndex, lastLogTerm)消息,其会做如下判断(条件依次判断,不满足上一条才会进入下一条):
1、如果这条消息携带的term < currentTerm则返回当前的term周期并拒绝投票请求:(currentTerm, false),并保持当前节点状态不变

2、如果term == currentTerm并且该节点的votedFor不为空并且不等于candidateId则返回当前的term周期并拒绝投票请求:(currentTerm, false),并保持当前节点状态不变,如果voteFor等于candidateId则给该节点返回投票:(currentTerm, true)并转换当前节点为Follower状态,并重置随机定时器

3、如果term > currentTerm则设置currentTerm = term, voteFor置为空并进入下一步。

4、如果该节点最后一条日志的term > lastLogTerm则返回当前的term周期并拒绝投票请求:(currentTerm, false),并保持当前节点状态不变

5、如果该节点最后一条日志的term == lastLogTerm,则比较该节点最后一条日志的IndexlastLogIndex的大小。如果该节点最后一条日志的Index > lastLogIndex则返回当前的term周期并拒绝投票请求:(currentTerm, false),并保持当前节点状态不变。否则令votedFor = candidateId给该节点返回投票:(currentTerm, true),并转换当前节点为Follower状态,并重置随机定时器

6、如果该节点最后一条日志的term < lastLogTerm,令votedFor = candidateId给该节点返回投票:(currentTerm, true)并转换当前节点为Follower状态,并重置随机定时器


一个节点(无论当前是什么状态)接收到AppendEntries(term, leaderId, prevLogIndex, prevLogTerm, entries[], leaderCommit)消息,其会做如下判断(条件依次判断,不满足上一条才会进入下一条):
1、如果这条消息携带的term < currentTerm则返回当前的term周期并返回:(currentTerm, false),并保持当前节点状态不变

2、如果term == currentTerm,设置voteFor = leaderId。如果term > currentTerm则设置currentTerm = term, voteFor = leaderId**。转换当前节点为Follower状态,重置随机定时器**并进入下一步。

3、如果当前节点log[]结构中prevLogIndex索引处含有日志并且该日志的term等于prevLogTerm则先执行以下日志存储然后返回(currentTerm, true),否则返回(currentTerm, false)

日志存储:
1、将prevLogIndex之后的日志全部删除,并将entries[]中的日志依次放入log[]中prevLogIndex之后的位置里。
2、如果leaderCommit(参数里) > commitIndex(每个节点存储里)则设置commitIndex = leaderCommit并将(commitIndex, leaderCommit]区间的日志应用到状态机上(更新commitIndex后机器会自动应用该操作)。


公共要求:
1、所有节点在所有状态下,只要通过消息看到了消息中的term > currentTerm则设置currentTerm = term,并将自己转换为Follower状态,并重置随机定时器。

2、如果发现当前机器commitIndex > lastApplied则应该将本机log[]中序号为(lastApplied, commitIndex]的部分应用到状态机


参考资料:
In Search of an Understandable Consensus Algorithm

转载于:https://www.cnblogs.com/wangbinquan/p/9061223.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值