You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

160 lines
13 KiB
Markdown

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# 07 | Raft算法如何选举领导者
你好,我是韩健。
通过前两节课我带你打卡了Paxos算法今天我想和你聊聊最常用的共识算法Raft算法。
Raft算法属于Multi-Paxos算法它是在兰伯特Multi-Paxos思想的基础上做了一些简化和限制比如增加了日志必须是连续的只支持领导者、跟随者和候选人三种状态在理解和算法实现上都相对容易许多。
**除此之外Raft算法是现在分布式系统开发首选的共识算法。**绝大多数选用Paxos算法的系统比如Cubby、Spanner都是在Raft算法发布前开发的当时没得选而全新的系统大多选择了Raft算法比如Etcd、Consul、CockroachDB
对你来说掌握这个算法可以得心应手地处理绝大部分场景的容错和一致性需求比如分布式配置系统、分布式NoSQL存储等等轻松突破系统的单机限制。
**如果要用一句话概括Raft算法我觉得是这样的从本质上说Raft算法是通过一切以领导者为准的方式实现一系列值的共识和各节点日志的一致。**这句话比较抽象我来做个比喻领导者就是Raft算法中的霸道总裁通过霸道的“一切以我为准”的方式决定了日志中命令的值也实现了各节点日志的一致。
我会用三讲的时间分别以领导者选举、日志复制、成员变更为核心讲解Raft算法的原理在实战篇中会带你进一步剖析Raft算法的实现介绍基于Raft算法的分布式系统开发实战。那么我希望从原理到实战在帮助你掌握分布式系统架构设计技巧和开发实战能力的同时加深你对Raft算法的理解。
在课程开始之前,我们先来看一道思考题。
假设我们有一个由节点A、B、C组成的Raft集群如图所示因为Raft算法一切以领导者为准所以如果集群中出现了多个领导者就会出现不知道谁来做主的问题。在这样一个有多个节点的集群中在节点故障、分区错误等异常情况下Raft算法如何保证在同一个时间集群中只有一个领导者呢带着这个问题我们正式进入今天的学习。
![](https://static001.geekbang.org/resource/image/b9/33/b99084301cf7b944af0490aa0297ff33.jpg)
既然要选举领导者那要从哪些成员中选举呢除了领导者Raft算法还支持哪些成员身份呢这部分内容是你需要掌握的最基础的背景知识。
## 有哪些成员身份?
成员身份,又叫做服务器节点状态,**Raft算法支持领导者Leader、跟随者Follower和候选人Candidate 3种状态。**为了方便讲解我们使用不同的图形表示不同的状态。在任何时候每一个服务器节点都处于这3个状态中的1个。
![](https://static001.geekbang.org/resource/image/28/03/280eff8a48c50fc3566ddbde9a97ee03.jpg)
* 跟随者:就相当于普通群众,默默地接收和处理来自领导者的消息,当等待领导者心跳信息超时的时候,就主动站出来,推荐自己当候选人。
* 候选人候选人将向其他节点发送请求投票RequestVoteRPC消息通知其他节点来投票如果赢得了大多数选票就晋升当领导者。
* 领导者蛮不讲理的霸道总裁一切以我为准平常的主要工作内容就是3部分处理写请求、管理日志复制和不断地发送心跳信息通知其他节点“我是领导者我还活着你们现在不要发起新的选举找个新领导者来替代我。”
**需要你注意的是Raft算法是强领导者模型集群中只能有一个“霸道总裁”。**
## 选举领导者的过程
那么这三个成员是怎么选出来领导者的呢?为了方便你理解,我以图例的形式演示一个典型的领导者选举过程。
首先,在初始状态下,集群中所有的节点都是跟随者的状态。
![](https://static001.geekbang.org/resource/image/5b/a2/5b391fd6cb9ed54ba77b0b96efed75a2.jpg)
Raft算法实现了随机超时时间的特性。也就是说每个节点等待领导者节点心跳信息的超时时间间隔是随机的。通过上面的图片你可以看到集群中没有领导者而节点A的等待超时时间最小150ms它会最先因为没有等到领导者的心跳信息发生超时。
这个时候节点A就增加自己的任期编号并推举自己为候选人先给自己投上一张选票然后向其他节点发送请求投票RPC消息请它们选举自己为领导者。
![](https://static001.geekbang.org/resource/image/aa/9c/aac5704d69f142ead5e92d33f893a69c.jpg)
如果其他节点接收到候选人A的请求投票RPC消息在编号为1的这届任期内也还没有进行过投票那么它将把选票投给节点A并增加自己的任期编号。
![](https://static001.geekbang.org/resource/image/a4/95/a4bb6d1fa7c8c48106a4cf040b7b1095.jpg)
如果候选人在选举超时时间内赢得了大多数的选票,那么它就会成为本届任期内新的领导者。
![](https://static001.geekbang.org/resource/image/ff/2c/ffaa3f6e9e87d6cea2a3bfc29647e22c.jpg)
节点A当选领导者后他将周期性地发送心跳消息通知其他服务器我是领导者阻止跟随者发起新的选举篡权。
![](https://static001.geekbang.org/resource/image/0a/91/0a626f52c2e2a147c59c862b148be691.jpg)
讲到这儿,你是不是发现领导者选举很容易理解?与现实中的议会选举也蛮类似?当然,你可能还是对一些细节产生一些疑问:
* 节点间是如何通讯的呢?
* 什么是任期呢?
* 选举有哪些规则?
* 随机超时时间又是什么?
## 选举过程四连问
老话说细节是魔鬼。这些细节也是很多同学在学习Raft算法的时候比较难掌握的所以我认为有必要具体分析一下。咱们一步步来先来看第一个问题。
#### 节点间如何通讯?
在Raft算法中服务器节点间的沟通联络采用的是远程过程调用RPC在领导者选举中需要用到这样两类的RPC
1.请求投票RequestVoteRPC是由候选人在选举期间发起通知各节点进行投票
2.日志复制AppendEntriesRPC是由领导者发起用来复制日志和提供心跳消息。
我想强调的是日志复制RPC只能由领导者发起这是实现强领导者模型的关键之一希望你能注意这一点后续能更好地理解日志复制理解日志的一致是怎么实现的。
#### 什么是任期?
我们知道议会选举中的领导者是有任期的领导者任命到期后要重新开会再次选举。Raft算法中的领导者也是有任期的每个任期由单调递增的数字任期编号标识比如节点A的任期编号是1。任期编号是随着选举的举行而变化的这是在说下面几点。
1. 跟随者在等待领导者心跳信息超时后推举自己为候选人时会增加自己的任期号比如节点A的当前任期编号为0那么在推举自己为候选人时会将自己的任期编号增加为1。
2. 如果一个服务器节点发现自己的任期编号比其他节点小那么它会更新自己的编号到较大的编号值。比如节点B的任期编号是0当收到来自节点A的请求投票RPC消息时因为消息中包含了节点A的任期编号且编号为1那么节点B将把自己的任期编号更新为1。
我想强调的是与现实议会选举中的领导者的任期不同Raft算法中的任期不只是时间段而且任期编号的大小会影响领导者选举和请求的处理。
1. 在Raft算法中约定如果一个候选人或者领导者发现自己的任期编号比其他节点小那么它会立即恢复成跟随者状态。比如分区错误恢复后任期编号为3的领导者节点B收到来自新领导者的包含任期编号为4的心跳消息那么节点B将立即恢复成跟随者状态。
2. 还约定如果一个节点接收到一个包含较小的任期编号值的请求那么它会直接拒绝这个请求。比如节点C的任期编号为4收到包含任期编号为3的请求投票RPC消息那么它将拒绝这个消息。
在这里你可以看到Raft算法中的任期比议会选举中的任期要复杂。同样在Raft算法中选举规则的内容也会比较多。
#### 选举有哪些规则
在议会选举中比成员的身份、领导者的任期还要重要的就是选举的规则比如一人一票、弹劾制度等。“无规矩不成方圆”在Raft算法中也约定了选举规则主要有这样几点。
1. 领导者周期性地向所有跟随者发送心跳消息即不包含日志项的日志复制RPC消息通知大家我是领导者阻止跟随者发起新的选举。
2. 如果在指定时间内,跟随者没有接收到来自领导者的消息,那么它就认为当前没有领导者,推举自己为候选人,发起领导者选举。
3. 在一次选举中,赢得大多数选票的候选人,将晋升为领导者。
4. 在一个任期内,领导者一直都会是领导者,直到它自身出现问题(比如宕机),或者因为网络延迟,其他节点发起一轮新的选举。
5. 在一次选举中每一个服务器节点最多会对一个任期编号投出一张选票并且按照“先来先服务”的原则进行投票。比如节点C的任期编号为3先收到了1个包含任期编号为4的投票请求来自节点A然后又收到了1个包含任期编号为4的投票请求来自节点B。那么节点C将会把唯一一张选票投给节点A当再收到节点B的投票请求RPC消息时对于编号为4的任期已没有选票可投了。
![](https://static001.geekbang.org/resource/image/33/84/3373232d5c10813c7fc87f2fd4a12d84.jpg)
6. 日志完整性高的跟随者也就是最后一条日志项对应的任期编号值更大索引号更大拒绝投票给日志完整性低的候选人。比如节点B的任期编号为3节点C的任期编号是4节点B的最后一条日志项对应的任期编号为3而节点C为2那么当节点C请求节点B投票给自己时节点B将拒绝投票。
![](https://static001.geekbang.org/resource/image/99/6d/9932935b415e37c2ca758ab99b34f66d.jpg)
我想强调的是,选举是跟随者发起的,推举自己为候选人;大多数选票是指集群成员半数以上的选票;大多数选票规则的目标,是为了保证在一个给定的任期内最多只有一个领导者。
其实在选举中除了选举规则外我们还需要避免一些会导致选举失败的情况比如同一任期内多个候选人同时发起选举导致选票被瓜分选举失败。那么在Raft算法中如何避免这个问题呢答案就是随机超时时间。
#### 如何理解随机超时时间
在议会选举中常出现未达到指定票数选举无效需要重新选举的情况。在Raft算法的选举中也存在类似的问题那它是如何处理选举无效的问题呢
其实Raft算法巧妙地使用随机选举超时时间的方法把超时时间都分散开来在大多数情况下只有一个服务器节点先发起选举而不是同时发起选举这样就能减少因选票瓜分导致选举失败的情况。
我想强调的是,**在Raft算法中随机超时时间是有2种含义的这里是很多同学容易理解出错的地方需要你注意一下**
1.跟随者等待领导者心跳信息超时的时间间隔,是随机的;
2.如果候选人在一个随机时间间隔内,没有赢得过半票数,那么选举无效了,然后候选人发起新一轮的选举,也就是说,等待选举超时的时间间隔,是随机的。
## 内容小结
以上就是本节课的全部内容了本节课我主要带你了解了Raft算法的特点、领导者选举等。我希望你明确这样几个重点。
* Raft算法和兰伯特的Multi-Paxos不同之处主要有2点。首先在Raft中不是所有节点都能当选领导者只有日志较完整的节点也就是日志完整度不比半数节点低的节点才能当选领导者其次在Raft中日志必须是连续的。
* Raft算法通过任期、领导者心跳消息、随机选举超时时间、先来先服务的投票原则、大多数选票原则等保证了一个任期只有一位领导也极大地减少了选举失败的情况。
* 本质上Raft算法以领导者为中心选举出的领导者以“一切以我为准”的方式达成值的共识和实现各节点日志的一致。
在本讲我们使用Raft算法在集群中选出了领导者节点A那么选完领导者之后领导者需要处理来自客户的写请求并通过日志复制实现各节点日志的一致下节课我会重点带你了解这一部分内容
## 课堂思考
既然我提到Raft算法实现了“一切以我为准”的强领导者模型那么你不妨思考这个设计有什么限制和局限呢欢迎在留言区分享你的看法与我一同讨论。
最后,感谢你的阅读,如果这篇文章让你有所收获,也欢迎你将它分享给更多的朋友。