分布式共识算法之Raft算法

26 篇文章 0 订阅
1 篇文章 0 订阅

在这里插入图片描述

一. 问题背景

研究完关于Redis的架构之后,想要了解一下哨兵集群是如何选出哨兵Leader的,因此来研究一下raft算法。笔者对Raft的研究大多数是以Redis哨兵集群作为切入点来阐述的,如果对Redis不了解,建议先阅读关于Redis的架构

参考:

  1. Raft官网
  2. 由浅入深理解Raft协议
  3. 白话讲解paxos&raft算法原理及实战_一点课堂(多岸学院&多岸教育)

二. 知识储备

2.1 共识算法

在分布式系统的环境下,有若干个服务器在不同地方同时运行,这些服务器需要共同使用一套规则来协商某些事情,这套规则就是共识算法,也可以叫一致性算法。Redis哨兵集群选举出Leader就需要共识算法,可简单了解关于Redis的架构共识涉及多个服务器就价值达成一致。一旦他们对价值做出决定,该决定就是最终决定

2.2 CAP定理

在分布式系统中,CAP定理是:

  1. C,Consistency,一致性。在分布式系统中所有数据副本的值,在同一时刻是否相同。
  2. A,Availability,可用性。集群中的部分节点故障后,剩余节点是否还能响应客户端的读写请求。
  3. P,Partition tolerance。系统如果不能在时限内达成数据一致性,就意味着发生了分区(比如A地的服务端数据无法与B地的服务端数据同步,存在数据差异)。必须对当前操作在C和A之间做出选择

2.3 Raft在Redis中起到了什么作用?

一直在说Raft算法,但都说得挺虚挺抽象,那么Raft到底在Redis中起到了什么作用?或者说Raft帮Redis做了些什么?以下几点给出解释:

  • Redis集群架构的演变:单机Redis->数据持久化(RDB、AOF)->主从复制(一主多从,从机作热备,或者主从实现读写分离)->哨兵集群(主从的基础上,添加若干个哨兵)->分片集群(最新版的Redis集群架构,多主多从,读写都在主机上,官方不推荐读写分离,推荐水平扩展主机)
  • 有兴趣研究Redis最新的集群设计架构的小伙伴,强烈推荐阅读Redis集群规范官方文档,因为市面上大多数都是在讲旧版的哨兵集群架构,这与新版的Redis集群有出入,旧版集群的架构理论甚至对我用新版集群做的实验结果造成很多疑惑,所以强烈推荐阅读Redis集群规范官方文档如果想动手搭建新版的Redis集群,可参考笔者的基于Docker搭建Redis集群
  • Raft的作用体现在哨兵集群(一主多从,还有若干个哨兵)上。通过Raft算法,若干个哨兵在master宕机的时候,选举出代表哨兵集群的Leader,由Leader决定提升哪个从机为主机。

三. 前言

!!!再次声明,Raft在Redis中的应用,是在哨兵集群选举出哨兵Leader这方面,而不是直接选举出提升哪个Redis slave为master!!!

四. Raft和Paxos的因缘?

由于Paxos过于难懂,因此有了Raft算法(大牛们实在觉得Paxos太难懂了,因此模仿Paxos设计出了一套简单易懂的共识算法),并得到了广泛的应用。这里就不讲Paxos,只研究Raft

五. Raft原理

5.1 节点个数

一个Raft集群包含若干个服务器节点,通常是奇数个节点(一般是5个,这样的系统可以容忍2个节点失效,因为挂了2个还剩3个,3个中每个人都必须投票给其他人且只能投一次,所以肯定会有一个人有最多票)

5.2 节点的角色

Raft集群中,有3种角色:

  1. Follower,跟随者 。每个节点最初都是跟随者,且都会有一个属于自己的随机超时时间(timeout),比如timeout为3s,3s内没有收到Leader的信息,节点则变成Candidate(候选者)。
  2. Candidate,候选者。成为候选者,可以向其他节点发出请求,请他们投自己一票,即选举(每个节点在每次选举中只能投一次票),最快得到最多票数的候选者将成为Leader(领导者)
  3. Leader,领导者。成为领导者后,定时发送消息给其他节点,其他节点也要发送响应报文给Leader。领导者可以代表整个raft集群内的所有节点,外部客户端是与领导者交互的。

5.3 多数派协议

为了保证选举的结果只产生一个Leader,选举的过程采用多数派协议(即少数服从多数)。当一个Candidate发出请求申请成为Leader时,只有获得raft集群中半数以上的Follower同意后,才能成为Leader。投票过程如下:

  1. Follower每响应完Leader的信息后,都会开始一个随机超时时间,最快超过这个超时时间的节点成为Candidate,Candidate向其他节点发送请求,申请成为Leader
  2. Follower们投票
  3. 如果获得超过半数的Follower投票,那么Candidate自动变成Leader,开始广播日志(即发送心跳包)

5.4 随机超时机制

Candidate也会发生多个Candidate同时发送投票请求,而导致谁都不能够得到多数赞成票的情况,有可能永远也选不出Leader。为了保证Leader选举的效率,Raft在投票选举中使用了随机超时的机制:

  1. 在每个Followers上设定的Leader超时时间是在一个范围内随机的。这样可以尽量让Followers不在同一时间发起Leader选举

  2. 每个Candidate发起投票后,如果在一段时间内没有任何Candidate成为Leader则,需要重新发起Leader选举。这段等待的时间,在每个Candidate上也是随机的。从而保证不会有多个Candidate同时重新发起Leader选举

5.5 正常情况下,Raft集群是怎么样的?

正常情况下:

  1. Raft的集群只有一个Leader,其余节点都是Follower。
  2. Follower都是被动的,他们不会发送任何请求,只是简单地响应Leader和Candidate的请求。
  3. Leader处理所有的客户端请求。如果客户端与Follower通信,Follower会将请求重定向给Leader。

在这里插入图片描述

虽说是随机的超时时间,但是也有个范围,太小或者太大都会影响系统的可用性。太小会导致过多的选举冲突,太大又会影响系统的平滑运行。在Raft的论文中,作者将这个超时时间称为electionTimeout,并给出了合理的范围,公式如下:

broadcastTime ≪ electionTimeout ≪ MTBF

“≪”代表数量级上的差异(10倍以上)。

5.6 Candidate的日志长度要等于或者超过半数节点才能选为Leader

当Leader故障时,Followers上日志的状态很可能是不一致的。有的多有的少,而且Commit Index也不尽相同。
在这里插入图片描述

我们知道已经提交的日志是不能够丢弃的,必须要最终复制到所有的节点上才行。假如在选Leader时,图中Candidate A变成了Leader,就必须要首先从Candidate B上将日志4复制过来,然后才能开始处理新的日志。为了减少复杂性,raft就规定,只有包含了所有已提交日志的Candidate才能当选为Leader。

  • 当发现Leader无响应后(一段时间内没有数据或心跳),Candidate发送投票请求,请求中包含自己日志队列的长度(或者说最大日志的Index)

  • Followers检查Candidate的日志长度,只有Candidate的日志等于或者长于自己才投票

  • 如果超过半数的Followers投了票,则Candidate自动变成Leader,开始广播数据

因为已经提交的日志一定被复制到了多数节点上,所以日志长度等于或者长于多数节点的Candidate一定包含了所有已经提交的日志

5.7 为什么不是检查Commit Index?

因为Leader故障时,很有可能只有Leader的Commit Index是最大的。

在这里插入图片描述

如果图中的Candidate A被选举为Leader,那么日志4就会被丢弃。但是日志4已经在原来的Leader上提交了,因此必须被保留才行。所以只能让日志长度更长的Candidate B选为Leader。这种做法有可能把原来Leader没广播完成的日志(图中的日志5)接着广播完成,这没有什么关系。

5.8 Followers日志补齐

当Leader故障时,Followers上的日志状态是不一样的,有长有短。因此新的Leader选出后,首先要将所有Followers的日志补齐才行。因此Leader要询问Followers的日志长度,从最小的日志位置开始补齐。

5.9 Followers未提交日志的更新

新Leader的日志一定包含所有已经提交的日志。但新Leader的日志不一定是最长的,那些新Leader没有的日志,一定是未提交的日志,因此可以被更新,没有关系的。Leader只需要从自己的当前位置开始插入日志并广播出去就可以了。Followers会用新的日志去更新指定位置上的日志。

5.10 新旧Leader的交替

新的Leader选出后,开始广播日志。这时如果旧的Leader故障恢复了(比如网络临时中断),并且还认为自己是Leader,也会广播日志。这不就导致了同时有两个Leader出现吗?是的,Raft也没办法让旧的Leader不发日志,但是Raft有办法让Followers拒绝旧Leader的日志

5.10.1 Term

Raft将时间划分为连续的时间段,称为Term。 Term是指从一次Leader选举开始到下一次Leader选举的一段时间。这段时间内只能有一个Leader被选举成功,并负责管理系统或者没有Leader选出。

在这里插入图片描述

每个Term都有一个唯一的数字编号。所有Term的数字编号是从小到大连续排列的。

5.10.2 作废旧Leader

Term编号在作废旧Leader的过程中至关重要,但却十分简单。过程如下:

  1. 发送日志到所有Followers,Leader的Term编号随日志一起发送
  2. Followers收到日志后,检查Leader的Term编号。如果Leader的Term编号等于或者大于自己的当前Term(Current Term)编号,则存储日志到队列并且应答收到日志。否则发送失败消息给Leader,消息中包含自己的当前Term编号。
  3. 当Leader收到任何Term编号比自己的Term编号大的消息时,则将自己变成Follower。收到的消息包括:Follower给自己的回复消息、新Leader的日志广播消息、Leader的选举消息。

5.11 Raft的实现

论文中作者仅用了两个RPC就实现了Raft的功能,它们分别是:

  1. RequestVote(): Candidate发起的投票请求

  2. AppendEntries(): 将日志广播到Followers上

AppendEntries()除了广播日志外,作者还巧妙的用它实现了以下的功能:

  • 发送心跳(heartbeat): 没有客户日志时,通过AppendEntries()广播空日志,当做心跳。
  • 发送Commit Index:当Commit Index更新后,可以随着当前的日志通过AppendEntries()广播到Followers上。如果没有客户端日志,则可以随着心跳广播出去。
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值