Raft原理
Raft简单介绍
Raft共识算法是为了解决多个机器之间状态一致问题
想象一个服务集群,该集群有若干个机器,每个机器存储key value键值对,客户端(client)每次发送读写请求,读请求是获取key对应的value,写请求是设置key对应的value
写请求会更新机器的状态,为了简单说明,我们称有状态的机器叫状态机(state machine),为了保证集群内部机器之间状态一致与正确,因此需要引入分布式共识算法,因此接下来我们要讨论Raft
Raft相比Paxos要易于理解,因此6.824课程教授Raft
Raft记录追加
Raft算法里,将写请求视为一条记录(log),每条记录按照先后顺序追加(append)到机器保存的记录列表,每个记录都有在列表里独一无二的索引值(index),也就是说不会有不同的记录共享索引值,同时追加的记录的索引值是前一条记录的索引值加一,也就是说索引值单调递增
在这里我们要引入回合(Term),一般网上翻译成任期,但为了易于理解,本文一律称为回合,Raft可以想象成回合制游戏,每当一个回合结束,回合值会自增
机器会保存回合值(机器认为当前的回合是自己保存的回合),前面我们我们提到记录,记录不仅保存索引值,还要保存回合值,记录的回合值表示该记录在哪一个回合追加到记录列表里的,回合值很重要,后面会提到
Raft选举
机器集群里,每个机器有不同角色,领导(leader),马仔(follower),领导接班候选人(candidate),每个回合最多一个领导,因为领导是负责处理写请求的,也就是说客户端发送的写请求必须先交给领导批示,领导会按照之前提到的方式,将写请求作为一条记录,加上索引值、回合值,然后追加到领导自己的记录列表,随后通过网络告知所有机器,也就是领导认为的马仔们,告诉他们要追加这条写记录
有时候在初始情况,或者领导挂机,没有领导指挥,这时候需要一次选举,从所有在线机器中选举出新一任领导。因此需要一个超时机制,当领导在世的时候,领导会给所有机器定时发送心跳消息,当马仔收到心跳消息时会重置自己计时器,当马仔计时器超时,马仔就会成为领导接班候选人,该候选人会将回合值加一,然后告诉所有机器发起新一轮选举,当还沉浸在上一回合的马仔们收到新一回合拉票消息时,会更新自己的回合值,重置计时器,然后还是选择做一个马仔,但是会根据候选人的最后一条记录决定是否投票,最后一条记录的索引值和回合值将决定马仔是否投票给接班人
马仔首先根据拉票请求携带的最后一条记录信息,决定是否投票,逻辑是如果最后一条记录比马仔自己的最后一条记录还新,就投给这个候选人,首先是比较记录之间回合值,然后比较索引值,如果接班人回合值比自己大,马仔就投票给他,比自己小就不投给他,如果相同就比较索引值,索引值大就投票给他,相同也投,小就绝对不投
但是马仔每回合只能投一次票,如果投过了,后面又一个接班人来拉票一律拒绝。胜选规则是如果票数大于总机器数的一半,也就是票数过半,候选人才能成功接班成为领导,从选举规则不难看出,故障机器数不能超过机器数一半
当候选人当选,首先要做的是巩固地位,要立即给所有机器发出通告,收到消息的机器,不论是马仔、候选人、旧领导,都得变成马仔
Raft机器间通信
集群内布机器间通信会检查请求发送过来的回合值,如果回合值小于本机保存的回合值,则一律拒绝请求,并告知最新回合值,如果请求回合值大于本机保存的回合值,则本机退化为马仔,并更新本机回合值
请求发起方会检查返回的回合值,如果回合值大于本机保存的回合值,则认为请求失败,退化为马仔,并更新本机回合值
领导为每个马仔维护有两个变量:匹配索引(matchIndex)、后继索引(nextIndex),匹配索引告诉领导,马仔第几个索引往前记录是匹配的,后继索引就比较复杂,后继索引一方面可以用来帮助领导试探马仔哪些索引是匹配的,另一方面也决定从后继索引以后的记录发送给马仔
领导在刚成为领导前,会重置匹配索引为0,0意味着没有匹配索引,Raft索引值从1开始,同时重置后继索引为领导最后一条记录的索引值加一,也就是领导下一条要追加记录的索引值。领导当要发送心跳或者追加索引时,会发送前继记录索引(prevLogIndex)和前继记录回合值(prevLogTerm),前继记录索引是通过后继索引减一得到,前继记录回合值则是通过前继记录索引指向记录得到
当马仔收到心跳消息或者追加记录消息,会通过前继记录索引和前继记录回合值,来检查是否有冲突,当发生冲突时,则不追加记录,检测冲突规则是首先查看是否有记录包含前继记录索引,如果没有则说明马仔自己的记录落后于领导。如果存在有相同索引的记录,则检查回合值,如果回合值不同,则认为存在冲突
当马仔通过前继记录索引和前继记录回合值没有检测到冲突时,则会将领导发送过来的记录逐个与自己记录比较,如果发现冲突记录,则丢弃冲突记录及其之后的记录,并应用领导发送过来的记录
领导根据马仔返回的消息,决定更新匹配索引和后继索引,如果返回成功的消息,则更新匹配索引,因为这时候我们知道在哪个索引之前记录是与领导的匹配,如果返回失败的消息,通常策略是后继索引减一,后面实战文章我们会讲到在网络质量不佳的优化策略
Raft提交记录策略
以上只是讲解记录追加和记录同步,但还没有涉及到记录提交,记录提交则是将写请求应用到状态机,规则是如果一个记录过半机器都有,则将该记录的写请求应用到机器。领导会根据匹配索引,检查哪些记录过半机器都有,如果过半机器都有,则更新自己提交索引(CommitIndex),同时将提交索引在下次心跳消息或者追加消息发送给机器,其他机器收到时,有该记录的机器会应用该记录到状态机。但是raft论文提到一种情况会导致提交冲突,因此需要为提交策略添加额外约束,领导只能提交在自己回合追加的记录,也就是说只有在自己回合追加的记录被大多数机器包含的才能提交,后面实战文章会解释为什么需要这样的约束
Raft应用
Raft在应用上很明显可以提高读效率,客户端可以向任意机器发起读请求,虽然返回值不一定最新的,但一定是在某个时间正确的,但在写上面存在不足,瓶颈在于所有机器状态同步的时间