raft算法_Raft算法与实现

强一致性、高可用的存储组件是构建现代分布式系统的必要条件,广泛应用于注册中心、配置中心等平台设施中,分布式锁、协调器等等各类场景需求也有相关需求,在该领域有众多知名的开源组件,如etcd、zookeeper、Tikv等等。

共识算法是实现这类组件的关键算法。简单的说共识算法是协调多个节点达成共识的算法,这是构建高可用系统的基石。大部分典型的共识算法都是基于状态复制机来实现的,每个节点都有一个状态机以及一个日志,状态机用于保持一个共识的结果,这样客户端只要连接到任意节点上就可以获取到状态机的内容。而日志是用于保证输入的顺序,只要保证所有日志的提交顺序是一致的则可以保证各个节点的状态机是一直的。

bb3793466830a69880c7ac1758087b7e.png

共识算法有很多种,就我知道的有paxos、raft、zab、VR(Viewstamped Replication)等等。paxos以复杂和难以理解著称,通常来说raft是更容易理解的算法,也更容易在工程实践中采用,而且在性能上并没有损失,所以下面我们讨论一下raft的的基本原理和一些要点。 下面的文章主要基于raft的paper整理而成,相关资料可以从https://raft.github.io/获取。

raft算法原理与实现

raft最主要的贡献在于2点,一个是将复杂的分布式算法分解成几个独立的解释和解决的子问题,并且。将状态进行简化,最小程度的考虑必要的问题,这些工作使得raft共识算法成为一种有效的新的方法。广泛的应用于教学和工程实践中: raft算法的基本流程是首先选举出leader,由leader完成日志复刻的功能。如果follower仅仅接受来自leader的日志复制请求,而没有反向的日志流。raft的leader是一个强大的leader。当follower的日志和leader的日志不同时会强制覆盖自己的日志给follower。当leader宕机时,会选出新的leader,来继续任务。

raft讲上述的基本流程拆分成3个部分:选主过程(leader election)、日志复制(log replication)、安全性(safety)加上成员变更(membership changing)。不过在具体讨论这4个部分的内容的过程中我们还是简单的讨论下raft的一些关键模型和概念:

角色和周期

节点的角色

raft的典型节点可以分为3种:follower、candidate、leader。

  • follower:所有节点在初始化之后都是follower。follower的职责仅仅是接受来自leader的复制日志的请求,或是在leader宕机时(未在超时时间内接受到心跳信息)成candidate尝试成为leader,亦或是接受到candidate的投票请求时响应改请求
  • candidate:当leader宕机时,超过超时时间的follower会转变成candidate,candidater会向集群内的所有机器发送请求投票给自己的请求。
  • leader:leader负责日志复制的过程。并且将自己的日志通过心跳发送给其他机器同步日志,所有来自客户端的写请求都会由leader处理,在过半数的机器提交之后就可以返回成功标志给客户端。

任期周期

e8b2ddcae88e3e4e5fef45f411ffced2.png

raft工作流程由一个个任期组成,通常来说任期由2部分组成一部分是选举阶段,在该阶段集群会尝试进行选主,选主如果成功则进入了常规操作阶段。在选举阶段是没办法响应客户端请求的,只有进入常规操作阶段才能正常的提供服务。 当然也会存在没有选出主节点的情况,这时,会开启一个新的任期。此外还有一点需要说明,一个集群内的节点可能在同一时刻处于不同的任期中的,因此任期本身是有编号的,如果节点接受到更高任期节点发送过来的请求则会更新自己的任期并转变成follower。 在raft中,实施上只需要2中rpc,一种是由候选人发出的请求投票信息,用于在选主阶段将请求其他服务器投票给自己,另一种是附加条目请求,由领导人发起,用来复制日志并提供一种心跳机制。

Leader election

raft使用一种从leader到follower的心跳机制来探活机器,如果在一个心跳周期内follower如果没有收到来自leader的心跳,则follower会认为集群内没有leader节点,自动转成candidate并发起投票请求。 在选举过程中follwer会将任期加1,并且转换成candidate持续发送请求,直到下面3个情况发生: (1)赢得半数以上的票数赢得选举。每个服务器会对一个任期投出一张选票,会投票给最先到来的请求。 (2)其他服务器成为领导者,候选人也会接收到其他机器的投票请求rpc,如果接收到投票请求所属的任期小于当前任期,候选人则会承认该请求发送者的领导者地位,并转换成follower,如果rpc所属的任期比自己小就拒绝掉该请求。 (3)一段时间后没有,这种情况下是可能有同时存在多个候选人,因此没有一个候选人获取了大多数人的支持,这种情况下则需要结束当前任期开始一个新的任期。为了避免重复这种现象,rafr使用随机选举超时时间的方法来确保很少发生选票瓜分的情况。 其实这里还涉及到一个选举超时时间的设定。基本上来说raft的算法要求系统满足下面的时间要求: 广播时间(broadcastTime) << 选举超时时间(electionTimeout) << 平均故障间隔时间(MTBF) 广播时间是节点发送rpc的品能根据rt时间,故障时间通常至少有几个月。因此选项时间大概在几十或者几百毫秒比较合适。

Log replication

领导者接收到的来自客户端的请求后会给在本地日志附加一套新的记录,并且并发的发送该记录给其他机器中。当这条日志条目被安全的复制,领导人会应用这条日志条目到他的状态机中,并且把结果返回给客户端。如果有follower没有返回请求,则leader会不断重试。

8e8232a85b12d2bf368eb4f64806655c.png

每一条日志条目存储一个条状态机指令和从领导人收到这条指令时的任期号。日志中的任期号用来检测是否出现不一致的情况。每条日志也有一个整数索引值表明他的位置。 当一个日志对应的rpc接收到大多数机器的响应则说明该日志已经被提交了,raft会保证已提交的日志会被可用的状态机执行。一旦当前的日志被提交,该条日志之前的所有日志都会被提交。包括有其他领导人创建的、以及此前任期创建的日志。领导人会保持当前被提交的日志的最大索引号,该索引会跟随追加条目的rpc,因此该索引号会被其他服务器提交。 raft的日志具有日志匹配的特性:

  • 如果在不同的日志中的两个条目拥有相同的索引和任期号,那么他们存储了相同的指令。领导人最多在一个任期里在指定的一个日志索引位置创建一条日志条目,同时日志条目在日志中的位置也从来不会改变。
  • 如果在不同的日志中的两个条目拥有相同的索引和任期号,那么他们之前的所有日志条目也全部相同。因为在发送附加日志 RPC 的时候,领导人会把新的日志条目紧接着之前的条目的索引位置和任期号包含在里面。如果跟随者在它的日志中找不到包含相同索引位置和任期号的条目,那么他就会拒绝接收新的日志条目。
1e918ecb46782d80572b6ca3868b3bf5.png

然而在leader宕机的情况下,附加日志的rpc会不一致。如上图所示,follower的日志可能比leader多或者少,这种情况下leader会强制复制自己的日志,从而简化操作。 为了使得follower和自己的日志一致,领导人必须找到最后2者一致的地方。然后删除从这个点那之后的所有日志。为了找到这个不一致的其实日志,leader在发送追加日志的rpc的时候回针对每个follwer维护一个nextIndex。每当leader进入常规阶段时,会将nextIndex初始化为leader的最后一条日志,会将这个nextIndex给发送给其他follwer。如果follwer和当前机器不一致则会返回失败,而这时leader会把该follwer对应的nextIndex-1,跟随下一次追加日志的rpc发送,直到找到不一致的日志起始点。

Safety

前面的章节里描述了 Raft 算法是如何选举和复制日志的。然而,到目前为止描述的机制并不能充分的保证每一个状态机会按照相同的顺序执行相同的指令。这里需要讨论一些特殊的限制:

选举限制

在基于leader 的一致性算法中,领导人必须存储已经提交的所有日志。为了保证这一点raft的策略是在投票阶段杜绝这种情况。RPC 中包含了候选人的日志信息,然后投票人会拒绝掉那些日志没有自己新的投票请求。 Raft 通过比较两份日志中最后一条日志条目的索引值和任期号定义谁的日志比较新。如果两份日志最后的条目的任期号不同,那么任期号大的日志更加新。如果两份日志最后的条目任期号相同,那么日志比较长的那个就更加新。

提交之前任期内的日志条目

下图描述了一种之前未讨论过的情况

b3be53de96cd09b6580b7acc2bc4cbbe.png

如图的时间序列展示了为什么领导人无法决定对老任期号的日志条目进行提交。在 (a) 中,S1 是leader,部分的复制了索引位置 2 的日志条目。在 (b) 中,S1 崩溃了,然后 S5 在任期 3 里通过 S3、S4 和自己的选票赢得选举,然后从客户端接收了一条不一样的日志条目放在了索引 2 处。然后到 (c),S5 又崩溃了;S1 重新启动,选举成功,开始复制日志。在这时,来自任期 2 的那条日志已经被复制到了集群中的大多数机器上,但是还没有被提交。如果 S1 在 (d) 中又崩溃了,S5 可以重新被选举成功(通过来自 S2,S3 和 S4 的选票),然后覆盖了他们在索引 2 处的日志。反之,如果在崩溃之前,S1 把自己主导的新任期里产生的日志条目复制到了大多数机器上,就如 (e) 中那样,那么在后面任期里面这些新的日志条目就会被提交(因为S5 就不可能选举成功)。 这样在同一时刻就同时保证了,之前的所有老的日志条目就会被提交。 为了消除这种场景,Raft 永远不会通过计算副本数目的方式去提交一个之前任期内的日志条目。只有当前任期里的日志条目通过计算副本数目可以被提交;一旦当前任期的日志条目以这种方式被提交,那么由于日志匹配特性,之前的日志条目也都会被间接的提交。 Cluster membership change

多节点的成员变更

398e396634d07f2a2497528604e81fea.png

前3个小节的内容基本描述了整个raft的主要内容,但是在工程实践中还有一个问题需要讨论,如果完成节点的配置替换,或者希望新增或者删除节点改如何处理呢?在如上图的情况中,在新旧2个配置的集群中可能会同时存在2个组,分别产生2个leader的情况,违反了safety。 raft的原文中提出了一种2阶段提交的方式:

ed09668279e2134acc21f88915768e82.png

基本思路是不允许旧配置和新配置同时进行决策,而是在2者之间加入一段Joint Consensus的过渡时期。具体的做法来说:向leader发送一个集群配置变更的请求Cold,new,leader将改日志复制给其他节点,尝试commit,如果失败了就重试,如果commit成功说明大部分节点都有该日志了,这时即使leader宕机了,重新启动的节点也是拥有该节点的。这时leader发送一个Cnew状态变更指令,带到这个指令commit之后就可以只使用新配置来选主了。

Single Cluster MemberShip Change

这里描述的方式虽然可行,但是实践中大部分不会采用这么麻烦的方式,一种策略也是Diego在其博士论文中提出的Single Cluster MemberShip Change,其基本思路就是上问中出现两个leader的根本原因是2个集群的节点不存在重叠,也就是说无法存在一个仲裁者来觉得采用哪个配置。

03fcacf0bc6c14dbfabec3f2660ce263.png

如果所示,实际上如果每次只变更一个节点,要获得大多数集群的投票新旧集群就必定有相交,Single Cluster MemberShip Change定了一种configuration LogEntry添加到日志,configuration LogEntry代表了集群配置的更新。Diego还论证了configuration LogEntry只要追加到日志就行了,因为如果configuration LogEntry没有被提交其影响也仅仅是系统配置回滚到原始配置而已。 当然这种方式也有问题在于如果前一次的configuration LogEntry还没有完全提交,新一次的configuration LogEntry就被写入了也可能导致老配置被回滚而配置错误,不过一般情况下,配置变更是人为可控的,完全可以等到新配置生效后再进行下一个节点的配置。 不过即使是这样实践中还是有更简单的方式比如etcd就是等到configuration LogEntry完全应用之后再进行下一次配置变更。

raft 语义与概念

最后我们总结一下raft的实现要点

状态

状态所有服务器上持久化currentTerm服务器最新任期号(初始化为 0,持续递增)votedFor在当前获得选票的candidateIdlog[]日志条目集;每一个条目包含一个用户状态机执行的指令,和收到时的任期号

状态所有服务器上非持久化commitIndex已知的最大的已经被提交的日志条目的索引值lastApplied最后被应用到状态机的日志条目索引值(初始化为 0,持续递增)

状态所有服务器上非持久化nextIndex[]对于每一个服务器,需要发送给他的下一个日志条目的索引值(初始化为leader最后索引值加一)matchIndex[]对于每一个服务器,已经复制给他的日志的最高索引值

rpc协议

追加日志

请求参数解释termleader的任期号leaderIdleader的 Id,以便于跟随者重定向请求prevLogIndex新的日志条目紧随之前的索引值prevLogTermprevLogIndex 条目的任期号entries[]准备存储的日志条目(表示心跳时为空;一次性发送多个是为了提高效率)leaderCommit领导人已经提交的日志的索引值

返回参数解释term当前的任期号,用于领导人去更新自己success跟随者包含了匹配上 prevLogIndex 和 prevLogTerm 的日志时为真

接收者实现:

  1. 如果 term < currentTerm 就返回 false
  2. 如果日志在 prevLogIndex 位置处的日志条目的任期号和 prevLogTerm 不匹配,则返回 false
  3. 如果已经存在的日志条目和新的产生冲突(索引值相同但是任期号不同),删除这一条和之后所有的
  4. 附加日志中尚未存在的任何新条目
  5. 如果 leaderCommit > commitIndex,令 commitIndex 等于 leaderCommit 和 新日志条目索引值中较小的一个

请求投票

请求参数解释termleader的任期号candidateId请求选票的候选人的 IdlastLogIndex候选人的最后日志条目的索引值lastLogTerm候选人最后日志条目的任期号

返回参数解释term当前的任期号,用于领导人去更新自己voteGranted候选人赢得了此张选票时为真

接收者实现:

  1. 如果term < currentTerm返回 false
  2. 如果 votedFor 为空或者为 candidateId,并且候选人的日志至少和自己一样新,那么就投票给他

规则

所有服务器:

  • 如果commitIndex > lastApplied,那么就 lastApplied 加一,并把log[lastApplied]应用到状态机中
  • 如果接收到的 RPC 请求或响应中,任期号T > currentTerm,那么就令 currentTerm 等于 T,并切换状态为跟随者 follower:
  • 响应来自候选人和领导者的请求
  • 如果在超过选举超时时间的情况之前都没有收到领导人的心跳,或者是候选人请求投票的,就自己变成候选人 candidate:
  • 在转变成候选人后就立即开始选举过程自增当前的任期号(currentTerm)给自己投票重置选举超时计时器发送请求投票的 RPC 给其他所有服务器
  • 如果接收到大多数服务器的选票,那么就变成领导人
  • 如果接收到来自新的领导人的附加日志 RPC,转变成跟随者
  • 如果选举过程超时,再次发起一轮选举 leader:
  • 一旦成为领导人:发送空的附加日志 RPC(心跳)给其他所有的服务器;在一定的空余时间之后不停的重复发送,以阻止跟随者超时
  • 如果接收到来自客户端的请求:附加条目到本地日志中,在条目被应用到状态机后响应客户端
  • 如果对于一个跟随者,最后日志条目的索引值大于等于 nextIndex,那么:发送从 nextIndex 开始的所有日志条目:如果成功:更新相应跟随者的 nextIndex 和 matchIndex如果因为日志不一致而失败,减少 nextIndex 重试
  • 如果存在一个满足N > commitIndex的 N,并且大多数的matchIndex[i] ≥ N成立,并且log[N].term == currentTerm成立,那么令 commitIndex 等于这个 N

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/503978.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

python桌面翻译_Python实现桌面翻译工具【新手必学】

Python 用了好长一段时间了&#xff0c;起初是基于对爬虫的兴趣而接触到的。随着不断的深入&#xff0c;慢慢的转了其它语言&#xff0c;毕竟工作机会真的太少了。很多技能长时间不去用&#xff0c;就会出现遗忘&#xff0c;也就有了整理一下&#xff0c;供初学者学习和讨论。相…

python 环境管理工具_再见 virtualenv!K神教你轻松管理多个Python环境

原标题&#xff1a;再见 virtualenv&#xff01;K神教你轻松管理多个Python环境超级无敌干货第一时间推给你&#xff01;&#xff01;&#xff01;小编电脑上有多个 Python 开发环境&#xff0c;每次都是用 virtualenv 创建一个 Python 虚拟环境&#xff0c;pip 安装第三方库&a…

mysql选取最小值_MySQL:选择x最小值

是否希望此工作(未测试):SELECT moname, MIN(updatetime) FROM amoreAgentTST01GROUP BY moname HAVING COUNT(moname)>1编辑-上面的意思只是作为现有代码的替换,所以它不会直接回答您的问题。我认为这样的事情应该适用于你的主要问题:SELECT moname, updatetime FROM amore…

python22起作业答案_python第22天作业

今日作业&#xff1a;1、检索文件夹大小的程序要求执行方式如下python3.8 run.py 文件夹import osimport sysfile_list os.listdir(sys.argv[1])def file_size(file_list,size 0):for file in file_list:if not os.path.isfile(file):file_list os.listdir(file)if not file…

python字典导入mongodb_Python语言生成内嵌式字典(dict)-案例从python提取内嵌json写入mongodb...

本文主要向大家介绍了Python语言生成内嵌式字典(dict)-案例从python提取内嵌json写入mongodb&#xff0c;通过具体的内容向大家展示&#xff0c;希望对大家学习Python语言有所帮助。从mongo查询利用python 读写如新的集合import traceback,from gaode_hotel.conn_mongodb impor…

python把浮点数转换成16进制_Python将colorsys RGB坐标转换为十六进制

从this answer开始,我在Python中生成一些均匀间隔的颜色,如下所示&#xff1a;>>> import colorsys>>> num_colors 22>>> hsv_tuples [(x*1.0/num_colors, 0.5, 0.5) for x in range(num_colors)]>>> rgb_tuples map(lambda x: colorsy…

静态ip ssh无法登录_识别动静态IP的技巧

动态IP&#xff0c;又称DHCP上网&#xff0c;即自动获取IP上网。动态IP这种上网方式&#xff0c;连接网络时即可自动获取IP地址来正常上网。在未使用路由器的情况下&#xff0c;只需要把宽带网线连接到电脑上&#xff0c;电脑上的IP地址设置为自动获得&#xff0c;电脑就可以实…

python集合数据结构_Python数据结构-集合

1.集合"""集合(set)&#xff1a;没有重复元素且没有顺序的数据结构定义语法&#xff1a;s set({}) #空集合s set({1, 2, 3, 4, 5})增加&#xff1a;add() 往集合添加一条数据update() 合并&#xff0c;支持传入列表、字典、元组、集合&#xff0c;不支持传入单…

18awg线材最大电流_小米生态链拉车线:2.4A大电流,苹果MFi认证,高速充电不断裂...

对于经常使用苹果手机的用户来说&#xff0c;不随时准备几根充电线好像总感觉差点什么&#xff0c;苹果官方的电源线不耐用早已是公认的事实&#xff0c;其实最主要的还是因为苹果手机电池容量低&#xff0c;相对来说充电次数要比安卓手机多一些&#xff0c;电源线使用频率也就…

method java_解析Java中的Field类和Method类

Field类Field类中定义了一些方法&#xff0c;可以用来查询字段的类型以及设置或读取字段的值。将这些方法与继承而来的member方法结合在一起.就可以使我们能够找出有关字段声明的全部信息&#xff0c;并且能够操纵某个特定对象或类的字段。getGenericType方法返回表示字段的声明…

a股历史30年的大盘价_2020年7月30日大盘走势分析

2020年7月30日大盘走势分析严正声明&#xff1a;分析下面小程序炒股广告与本公众号zyh218642无关&#xff0c;纯属第三方平台自然生成&#xff0c;不要点开&#xff0c;谨防上当受骗。7月份大盘走势分析7月份大盘的多空压力与支撑位置&#xff1a;…第二压力&#xff1a;3139.0…

java 做ui_【原创】JavaApplication的UI也可以做的很美

最近在做Java Application的项目&#xff0c;对于ui的美观&#xff0c;做了一些尝试。有幸看到了JGoodIdes的LookAndFeel包。效果大家可以看看如下连接http://www.jgoodies.com经过试验&#xff0c;解决了中文乱码问题&#xff0c;下面将经验和大家共分享。LookAndFeel如同css一…

python低代码_几行代码搞定ML模型,低代码机器学习Python库正式开源

PyCaret 库支持在「低代码」环境中训练和部署有监督以及无监督的机器学习模型&#xff0c;提升机器学习实验的效率。想提高机器学习实验的效率&#xff0c;把更多精力放在解决业务问题而不是写代码上&#xff1f;低代码平台或许是个不错的选择。最近&#xff0c;机器之心发现了…

java if else重构_java – 如何重构这个有多个if / else语句的方法

我有一种感觉,这个if / else应该被重构,但我不确定我能做什么,或者我是否应该让它像它一样……private String someReportUrl(HttpServletRequest request, HttpServletResponse response) {String url;if (isBackToReportsSummary(request)) {url SUMMARY_PAGE;getReportsSum…

vue2.0 唤起百度地图app_开车选高德,出门靠百度,高德百度地图APP对比

高德和百度是在电子地图领域竞争的对手&#xff0c;但是&#xff0c;在同一领域他们的发展方向的侧重也存在差异。那么&#xff0c;他们究竟有什么不同呢&#xff1f;当然&#xff0c;他们的开发人员必须是不同的&#xff0c;肯定不用考虑。此外&#xff0c;在某些数据和功能上…

java beanutil 工具类_实现BeanFactoryAware来达到Spring静态方法获取Bean对象的BeanUtil工具类...

在容器初始化时注入Bean工厂&#xff0c;并提供一些列静态方法&#xff0c;用于运行期间任何地方都可以用过他来获许对应Beanpackage com.idaima.util;import org.springframework.beans.BeansException;import org.springframework.beans.factory.BeanFactory;import org.spri…

火力发电厂与变电站设计防火标准_真题—火力发电厂1

做真题&#xff0c;遇真题&#xff0c;解真题1、某燃煤火力发电厂&#xff0c;单机容量200MW&#xff0c;该发电厂火灾自动报警系统的下列设计方案中&#xff0c;正确的是()。A.运煤系统内的火灾探测器防护等级为IP65B.厂区设置集中报警系统C.消防控制室与集中控制室分别独立设…

bigdecimal 判断是否为数字_C语言判断字符串是否为回文

回文就是字符串中心对称&#xff0c;如“abcba”、“abccba”是回文&#xff0c;“abcdba”不是回文。/*判断字符串是否为回文*/ #include <stdio.h> int main(void) {int i,k;char line[10];/*输入字符串*/printf("Enter a string:");k0;while((line[k]getcha…

java文件学生_文件存储学生信息(JavaIO流)

package com;import java.io.File;import java.io.FileNotFoundException;import java.io.FileOutputStream;import java.io.IOException;import java.util.Scanner;/*** author Administrator*1.要求&#xff1a;有五个学生&#xff0c;每个学生有姓名、年龄 、成绩三个属性&a…

捷波朗STORM耳机设置中文_2020年 除了Airpods pro以外无线降噪蓝牙耳机如何选?五款热门入耳式蓝牙降噪耳机推荐...

双十二红包&#xff0c;每日三次&#xff0c;手慢无2020 年除了Airpods pro 以外&#xff0c;五款热门入耳式无线蓝牙降噪耳机简评近期&#xff0c;Apple推出的新款无线耳机Air pods pro引起了一波数码控的热议&#xff0c;大致分为两个立场&#xff0c;我个人专门去苹果店试听…