目录
Raft算法是现代分布式系统开发首选的共识算法,Raft算法属于Multi-Paxos算法,是在兰伯特Multi-Paxos思想的基础上,做了一些简化和限制。
Raft算法的应用非常广泛
- Etcd:一个分布式、一致性的键值存储系统,用于服务发现、配置共享以及分布式锁等场景,Etcd 使用 Raft 一致性算法来保证在分布式系统中多个节点间的数据一致性和高可用性。
- Consul:服务发现与配置工具,它也使用了类似 Raft 共识算法来保证其KV存储的一致性。
- TiDB:TiKV 作为 TiDB 分布式数据库系统的存储引擎部分,TiKV 采用 Raft 协议来实现数据强一致性复制。
- InfluxDB:一个开源的时间序列数据库,其集群版本使用了基于Raft的领导者选举和日志复制机制。
- JRaft:阿里自研的Java版Raft一致性算法库,可以方便地嵌入到各种分布式中间件和微服务中。Nacos 中使用 JRaft 作为其一致性实现之一。
- RocketMQ:阿里巴巴的消息队列系统,在其高可用部署方案中,也利用了 Raft 协议确保元数据的强一致性。
掌握这个算法,可以得心应手的处理大部分场景的容错和一致性需求。关于 Raft 算法分三期讲解,分别介绍领导者选举、日志复制、成员变更,这篇文章首先讲解领导者选举。
一、领导者选举
如果要用一句话概括 Raft 算法,是这样的:从本质上说,Raft 算法是通过一切以领导者为准的方式,实现一系列值的共识和各节点日志一致。领导者就是 Raft 算法中的核心,通过的“一切以我为准”的方式,决定了日志中命令的值,也实现了各节点日志的一致。
先来看一个案例。
假设有一个由节点 A、B、C 组成的 Raft 集群,因为 Raft 算法一切以领导者为准,所以如果集群中出现了多个领导者,就会出现不知道谁来做主的问题。在这样一个有多个节点组成的集群中,在节点故障、分区错误等异常情况下,Raft 如何保证在同一时间,集群中只有一个领导者呢?
既然要选举领导者,那要从哪些成员中选举呢?除了领导者,Raft算法还支持哪些成员身份呢?
1、成员身份
成员身份,又叫做服务器节点状态,Raft 算法支持领导者(Leader)、跟随者(Follwer)和候选人(Candidate)三种状态。任何时候,节点的状态都处于三个中的一个。
- 跟随者:默默地接受和处理来自领导者的消息,当等待领导者心跳信息超时的时候,就主动站出来,推荐自己为候选人。
- 候选人:候选人将向其他节点发送请求投票(RequestVote)RPC 消息,通知其他节点来投票,如果赢得了大多数选票,就晋升为领导者。
- 领导者:一切以我为准,平常的主要工作内容有3部分,处理写请求、管理日志复制和不断地发送心跳信息,通知其他节点“我是领导者,我还活着,现在不要发起选举,找个新领导者来替代我”。
需要注意的是,Raft算法是强领导者模型,集群中只能有一个领导者。
2、领导者选举过程
首先,初始状态下,集群中所有的节点都是候选人的状态。
Raft 算法实现了随机超时时间的特性。也就是说,每个节点等待领导者心跳信息的超时时间间隔是随机的。上图集群中没有领导者,而节点 A 的等待超时时间最小(100ms),它会最先因为没有等到领导者的心跳信息,发生超时。
这个时候,节点A就增加自己的任期编号,并推举自己为候选人,先给自己投上一票,然后向其他节点发送请求投票RPC信息,请他们选举自己为领导者。
如果其他节点接受到候选人A的请求投票RPC信息,在编号为1的这届任期内,还没有进行过投票,那么将把选票投给节点A,并增加自己的任期编号。
如果候选人 A 在选举超时时间内赢得了大多数选票,那么他就会成为本届任期内新的领导者。节点 A 当选领导者后,将周期性的发送心跳信息,通知其他服务器我是领导者,阻止跟随者发起新的选举。
3、节点是如何通讯的?
在 Raft 算法中,服务器间的沟通联络采用的是远程过程调用(RPC),在领导者选举中,需要用到这样两类的RPC:
- 请求投票(RequestVote)RPC,是由候选人在选举期发起的,通知各节点进行投票;
- 日志复制(AppendEntries)RPC,是由领导者发起,用来复制日志和提供心跳信息。
日志复制RPC只能由领导者发起,这是实现强领导者模型的关键之一。
4、任期编号
Raft 算法中的领导者是有任期的,每个任期由单调递增的数字(任期编号)标识,比如节点A 的任期编号是1。任期编号是随着选举而变化的,这里说下面几点:
- 跟随者在等待领导者心跳超时后,推举自己为候选人时,会增加自己的任期编号,比如节点A的当前任期编号为0,那么在推荐自己为候选人时,会将自己的任期编号增加为1。
- 如果一个服务器节点,发现自己的任期编号比其他节点小,那么它会更新自己的任期编号为较大的编号值。比如节点 B 的任期编号为0,当收到来自A节点的请求投票 RPC 消失时,因为消息中包含了 A 的任期编号,且编号为1,那么节点 B 把自己的任期编号更新为1。
5、选举规则
在 Raft 算法中,约定了选举规则,主要有这样几点。
- 领导者周期性地向所有跟随者发送心跳消息(即不包含日志项的日志复制 RPC 消息),通知大家我是领导者,阻止跟随者发起新的选举。
- 如果在指定时间内,跟随者没有接收到来自领导者的消息,那么它就认为当前没有领导者,推举自己为候选人,发起领导者选举。
- 在一次选举中,赢得大多数选票的候选人,将晋升为领导者。
- 在一个任期内,领导者一直都会是领导者,直到它自身出现问题(比如宕机),或者因为网络延迟,其他节点发起一轮新的选举。
- 在一次选举中,每一个服务器节点最多会对一个任期编号投出一张选票,并且按照“先来先服务”的原则进行投票。比如节点 C 的任期编号为 3,先收到了 1 个包含任期编号为 4 的投票请求(来自节点 A),然后又收到了 1 个包含任期编号为 4 的投票请求(来自节点 B)。那么节点 C 将会把唯一一张选票投给节点 A,当再收到节点 B 的投票请求 RPC 消息时,对于编号为 4 的任期,已没有选票可投了。
- 日志完整性高的跟随者(也就是最后一条日志项对应的任期编号值更大,索引号更大),拒绝投票给日志完整性低的候选人。比如节点 B 的任期编号为3,节点C的任期编号为4,节点 B 的最后一条日志项对应的任期编号为 3,而节点 C 为2,那么当节点 C 请求节点 B投票给自己时,节点B将拒绝投票。
选举是跟随者发起的,推举自己为候选人,大多数选票是指集群成员半数以上的选票,大多数选票规则的目标,是为了保证在一个给定的任期内最多只有一个领导者。
其实在选举中,除了选举规则外,我们还需要避免一些会导致选举失败的情况,比如同一任期内,多个候选人同时发起选举,导致选票被瓜分,选举失败。那么在 Raft 算法中,如何避免这个问题呢?答案就是随机超时时间。
6、随机时间
Raft 算法使用随机时间,把超时时间分散开来,在大多数情况下只有一个服务器节点先发起选举,而不是同时发起选举,这样就能减少因选票瓜分导致选举失败的情况。
二、强领导者存在的问题
Raft 算法是强领导者模型,一切以我为准,那这样会存在什么问题呢?
首先写请求肯定都会落到领导者身上,领导者压力会大很多;其实,如果集群规模很大,有很多跟随者,那么领导者需要承担大量的心跳通知成本来维护元数据;另外,领导者还存在单点故障,虽然有领导者选举机制,但在选举期间,集群是不可用的;最后,选举过程中,如果跟随者数量很大,那收集选票的时间会变长,选举领导者的耗时会增加。
关于 Raft 选举领导者的问题就介绍到这里,欢迎留言讨论。
往期经典推荐
揭秘Elasticsearch:一文读懂分布式搜索与分析引擎的核心概念-CSDN博客
深入浅出 Kafka 消费者:解密分布式消息流的幕后英雄_kafka消费-CSDN博客