首页 > 编程语言 >深度解析RocketMq源码-高可用存储组件(一) raft协议详解

深度解析RocketMq源码-高可用存储组件(一) raft协议详解

时间:2024-06-23 18:31:40浏览次数:21  
标签:term leader follower 源码 raft 日志 节点 RocketMq

1.绪论

前面的文章已经分析过,以前rocketmq通过主从复制的思想实现系统的高可用,即在搭建集群的时候会手动的设置一个主节点和从节点,在写入数据的时候,会先写入到主broker,然后再同步到从节点中。但是这样会有一个问题,就是主节点宕机过后,需要手动的修改从节点成为新的主节点。在rocketmq的最新的版本中,已经引入了dledger的架构,它利用raft共识算法,当从节点宕机过后,能够自动的选举主节点,并完成主节点和从节点的数据一致。

2.raft算法

2.1  raft算法简述

在1990年,Leslie Lamport提出来Paxos协议,它是基于多leader的协议,所以对于整个分布式系统而言,数据写入有多个入口,导致多个主节点实现强一致性变得异常困难,很难进行实现。2013年,Diego ongrao和John Ousterhout提出了raft算法,它是一个强leader的算法,即整个分布式系统只有一个leader和多个follower,leader负责数据写入,leader和follower都可以进行数据读取,这样整个系统的入口便只有一个。业界有很多对raft算法的实现,比如nacos保证高可用的jRaft框架,和rocketmq保证高可用的dledger框架等。

2.2 raft算法问题拆分

1. 主节点选举

当主节点宕机,如何从从节点当中选择一个新的主节点?

2.日志复制

如何将主节点中的数据同步到从节点中?

3.安全性

分布式系统存在多个情况,算法需要设置哪些限制来为系统的可用性和安全性提供保障?

2.3 leader选举

2.3.1  选举的目的

raft算法有如此复杂的选举流程,这样做的目的是什么呢,其实本质上是为了选择出整个集群中拥有最新一条数据的follower来作为leader,并且同步给其他的follower,这样才能保证整个集群拥有follower宕机过后最新的一条数据,从而保证数据不丢失。为什么这样做能够保证数据不丢失呢,后面我们分析完raft协议的日志复制,再来分析这个问题。

2.3.2 raft算法中的角色

raft算法中有三种角色,分别是leader,follower和candidate,leaderf负责写数据,并且写数据过后,会将数据复制给follower。leader会给每个follower发送心跳数据,如果某个follower超过某个时间没有收到心跳数据,便会认为leader已经宕机,便会将自己设置为candidate,并会将当前的term加1,并且发起选举。在选举成功过后,设置自己为follower,并且给其他follower发送心跳数据,防止发起新的一轮选举。这就是raft算法的大概步骤。

2.3.3 raft算法中的任期号term

1.什么是term

term是一个单调递增的数字,每次candidate在发起选举的时候,便会将当前的term增加1。并且raft协议规定在每个term中,只能有一次选举。

2.为什么需要任期号

比如在candiate发起新的一轮选举过后,并且成功当选为follower,老的leader恢复,当时发现自己的term已经小于当前的leader的term,所以便会自动将自己更新为follower。

2.3.4 raft算法中需要交换的数据报文

RequestVotes Rpc:用来进行投票的协议

AppendEntries Rpc:leader向follower同步日志的报文,并且也会作为心跳报文发送给follower,当follower收到心跳过后,会重置选举的时钟。

2.3.5 raft算法选举的具体流程

我们现在假设有A,B,C,D,E五个节点搭建了集群。

1.leader宕机

假设在最开始的时候,A节点是leader节点,但是A节点突然宕机,假设B节点的超时时钟最先到期,发现没有收到leader的心跳包过后,开始发起选举。

在选举的时候,为了防止选举风暴,也就是每个节点都同时发起选举,这样会导致很难选举出新的leader,所以可以没有收到leader的心跳包的时间设置为随机时间,这样他们选举就会分隔开。

2.出现新的candidate并且发起投票

B节点将自己的term加1,同时将自己的id,term和最新一条日志索引的Id封装成RequestVotes Rpc发送给其他节点。

3.其他follower开始投票

其他节点收到投票请求过后,开始进行投票,投票规则如下:

1.如果当前节点在当前term内没有投过票,才开始投票,否则,跳过。
2.如果当前follower节点发现term比收到的投票请求中的term要低,便将自己的term更新为最新的term。

3.如果leader或者candidate发现自己的term比当前的term要小,便立刻恢复为follower。这个主要解决leader宕机或者candiate宕机过后,已经过去几轮投票了,然后恢复的场景。

4.follower会将投票的最新的日志索引和自身的最新一条索引比较,如果不是则拒绝投票。这样做的目的是为了保证最终投票出来的leader一定拥有整个集群最新的一条日志。raft协议在进行日志复制的时候,一定是保证超过半数以上follower节点复制日志成功过后,才会返回成功。所以在leader宕机过后,一定能够保证整个集群超过一半的节点拥有最新的日志,再结合投票时一定是半数以上的支持,才能当选leader,也即他的索引一定是大于等于集群半数以上节点的,所以一定有用最新的日志。

综上,选举投票其实就是,首先判断当前节点在当前的term内没有投过票,如果投过,便跳过;然后比较term,如果自己的term小于当前candidate节点的term,便更新自己的term,否则,便跳过;然后再比较自己的索引,如果当前节点的索引文件小于等于candidate的索引文件。上述三个条件都满足过后,才会投票给当前candidate。

4.比较投票结果

投票结果分为3种情况:

1.选举成功

如果或者整个集群N/2+1个节点的支持,便当选该节点为leader,并且发送心跳包给其他节点。candidate或者leader节点收到过后,直接设置自己状态为follower,而follower则会重置选举时钟。

2.选举失败

如果在选举过程中,收到其他节点已经成为leader,并且term大于等于自己,便更新自己状态为follower。

3.选举超时

如果在选举过程可能因为有其他节点在进行选举,所以会分散自己的票数,这个时候便会重新选举。由于每个节点的选举时钟是不一样的,所以可以尽量避免这种情况的发生。

2.4 日志复制

2.4.1 日志同步

在leader选举成功过后,数据便会从leader写入,然后leader同步给follower,此时会采用两阶段的提交方式,leader收到客户端的写入操作后,将其封装成一条日志文件,同步给follower;follower将给leader返回ack。leader收到半数以上ack过后,便将日志应用到状态机。如果存在follower超过过一定时间也没有返回ack,leader会一直重试。

那leader是什么时候将日志文件应用到状态机中的呢,每次leader会将自己最新一条已经应用到状态机的日志的索引通过心跳包发送给follower,这样follower在收到过后,会将小于该索引的日志全部应用到状态机中。

注:什么是状态机呢,即leader向follower同步的是一系列命令,每个分布式节点都存储有一些列的日志命令和一个状态机,只要按照顺序将这些命令应用到状态机中,最后所有节点的状态一定是一致的。比如更新节点的主节点为新的主节点就是状态机需要进行的操作。

2.4.2 一致性检查

存在两种异常情况:

1.如果某个从节点宕机过后,在恢复,这个时候,从节点的日志已经落后主节点一大截,如何保证主节点和从节点数据一致呢?
2.新的主节点当选过后,可能某些从节点中存在一些没有被应用的日志,这个时候从节点的索引比主节点大。

针对上面两个问题,如何解决呢?坚持一个原则,也即主节点只会增加索引,不会删除索引。

leader会为每个follower维护一个next Index,在新leader当选过后,会初始化每个follower的next index为自己最后一条日志的Index+1 ,然后进行一致性检查。如果某个follower的日志和自己的日志不一致,便会将next Indedx进行减1,然后进行重试,直到找到最后一条与follwer相同的日志,然后进行重试,最后达成统一。

2.5 安全限制

为了保证raft算法的安全性,所以需要增加一些限制:

2.5.1. 选举限制

前面已经说过,选举的时候为了保证leader一定是拥有整个集群最新一条日志的节点,所以在投票的时候,会将candidate的最新一条索引与自身最新一条索引进行比较,只有索引大于等于自己,才会投票给它。

2.5.2 提交限制

每个leader只会commit当前leader term的条目。这样就会避免某些并没有给用户返回成功的数据被应用到系统中。比如比如A节点在当选leader过后,只复制了少于一半的数据,就宕机了,这个时候并且没有给客户端返回成功,这部分数据就是脏数据,如果A节点恢复并成功当选leader过后,又有可能将该日志复制到其他follower,最后这条日志被成功应用。所以为了解决该问题,直接将这部分数据淘汰掉,只能commit当前leader term的条目。

2.6 日志压缩

raft算法存储的日志其实是一些列的命令,但是如果节点宕机过后,再进行重启,会将所有的命令加载一遍,这样会耗费很长的时间。所以可以定期的将状态机应用完命令的结果快照一份并存储起来。其实就是类似于redis的Aof和rdb。

3.参考文献

1.https://blog.csdn.net/weixin_63566550/article/details/134794739

2.https://juejin.cn/post/6907151199141625870

3.Raft协议详解--背景+概念介绍+算法剖析-CSDN博客

A节点突然宕机

raft算法是基于强leader的共识算法

标签:term,leader,follower,源码,raft,日志,节点,RocketMq
From: https://blog.csdn.net/zhifou123456/article/details/139903993

相关文章