赞
踩
Raft一致性算法诞生的背景
著名的CAP原则又称CAP定理的提出,真正奠基了分布式系统的诞生,CAP定理指的是在一个分布式系 统中,一致性(Consistency)、可用性(Availability)、分区容错性(Partition tolerance),这三个要素最多只能同时实现两点,不可能三者兼顾。
分布式系统为了提高系统的可靠性,一般都会选择使用多副本的方式来进行实现,例如hdfs当中数据的 多副本,kafka集群当中分区的多副本等,但是一旦有了多副本的话,那么就面临副本之间一致性的问题,而一致性算法就是用于解决分布式环境下多副本的数据一致性的问题。业界最著名的一致性算法就是大名鼎鼎的Paxos,但是Paxos比较晦涩难懂,不太容易理解,所以还有一种叫做Raft的算法,更加 简单容易理解的实现了一致性算法。目前数据一致性协议有Paxos、ZAB、Raft、Gossip、KRaft、JRaft、Sofa-JRaft、BRaft等。
Raft算法中文版本翻译介绍:https://github.com/maemual/raft-zh_cn/blob/master/raft-zh_cn.md
为什么需要一致性?
一致性算法的定义
一致性就是数据保持一致,在分布式系统中,可以理解为多个节点中数据的值是一致的。
一致性的分类
强一致性
说明:保证系统改变提交以后立即改变集群的状态。
模型:
Paxos
Raft(muti-paxos)
ZAB(muti-paxos)
弱一致性
说明:也叫最终一致性,系统不保证改变提交以后立即改变集群的状态,但是随着时间的推移最终状态是一致的。
模型:
DNS系统
Gossip协议
一致性算法实现举例
KRaft
kafka3当中可以不用再依赖于zk来保存kafka当中的元数据了,转而使用Kafka Raft来实现元数据的一致性,简称KRaft,并且将元数据保存在kafka自己的服务器当中,大大提高了kafka的元数据管理的性能。
KRaft运行模式的Kafka集群,不会将元数据存储在 Apache ZooKeeper中。即部署新集群的时候,无需部署ZooKeeper集群,因为Kafka将元数据存储在 Controller 节点的 KRaft Quorum中。KRaft可以带来很多好处,比如可以支持更多的分区,更快速的切换Controller,也可以避免Controller缓存的元数据和Zookeeper存储的数据不一致带来的一系列问题。
在新的版本当中,控制器Controller节点我们可以自己进行指定,这样最大的好处就是我们可以自己选择 一些配置比较好的机器成为Controller节点,而不像在之前的版本当中,我们无法指定哪台机器成为Controller节点,而且controller节点与broker节点可以运行在同一台机器上,并且控制器controller节点不再向broker推送更新消息,而是让Broker从这个Controller Leader节点进行拉去元数据的更新。
JRaft
GitHub上找到了一个看起来还不错的开源项目,基于Netty的Raft项目的实现,是百度的工程师开源的。基于raft-java可以自己手动写一套Raft代码。后续我们手写一个基于raft-java的Raft实现。
https://github.com/wenweihu86/raft-java
Sofa-JRaft
Sofa-JRaft 是一个蚂蚁金服开源的基于 Raft 一致性算法的生产级高性能 Java 实现,支持 MULTI-RAFT-GROUP,适用于高负载低延迟的场景。Sofa-JRaft 是从百度的 braft 移植而来,做了一些优化和改进,基于百度 braft 团队开源了如此优秀的 C++ Raft 实现。
GitHub 地址:https://github.com/alipay/sofa-jraft
Raft协议的工作原理
Raft协议当中的角色分布
Raft协议将分布式系统当中的角色分为Leader(领导者),Follower(跟从者)以及Candidate(候选者)
Leader:主节点的角色,主要是接收客户端请求,并向Follower同步日志,当日志同步到过半及
以上节点之后,告诉follower进行提交日志
Follower:从节点的角色,接受并持久化Leader同步的日志,在Leader通知可以提交日志之后,
进行提交保存的日志
Candidate:Leader选举过程中的临时角色。
Raft协议当中的底层原理
Raft协议当中会选举出Leader节点,Leader作为主节点,完全负责replicate log的管理。Leader负责接受所有客户端的请求,然后复制到Follower节点,如果leader故障,那么follower会重新选举leader,Raft协议的一致性,概括主要可以分为以下三个重要部分
Leader选举
日志复制
安全性
其中Leader选举和日志复制是Raft协议当中最为重要的。
Raft协议要求系统当中,任意一个时刻,只有一个leader,正常工作期间,只有Leader和Follower角色,并且Raft协议采用了类似网络租期的方式来进行管理维护整个集群,Raft协议将时间分为一个个的时间段(term),也叫作任期,每一个任期都会选举一个Leader来管理维护整个集群,如果这个时间段的Leader宕机,那么这一个任期结束,继续重新选举leader。
Raft 算法将时间划分成为任意不同长度的任期(term)。任期用连续的数字进行表示。每一个任期的开始都是一次选举(election),一个或多个候选人会试图成为领导人。如果一个候选人赢得了选举,它就会在该任期的剩余时间担任领导人。在某些情况下,选票会被瓜分,有可能没有选出领导人,那么,将会开始另一个任期,并且立刻开始下一次选举。Raft 算法保证在给定的一个任期最多只有一个领导人。
Leader选举的过程
Raft使用心跳来进行触发leader选举,当服务器启动时,初始化为follower角色。leader向所有
Follower发送周期性心跳,如果Follower在选举超时间内没有收到Leader的心跳,就会认为leader宕机,稍后发起leader的选举。
每个Follower都会有一个倒计时时钟,是一个随机的值,表示的是Follower等待成为Leader的时间,倒计时时钟先跑完,就会当选成为Lader,这样做得好处就是每一个节点都有机会成为Leader。
具体选举详细过程实现描述如下:
整个过程中的投票过程可以用下图进行表述
数据一致性保证(日志复制机制)
前面通过选举机制之后,选举出来了leader节点,然后leader节点对外提供服务,所有的客户端的请求都会发送到leader节点,由leader节点来调度这些并发请求的处理顺序,保证所有节点的状态一致,leader会把请求作为日志条目(Log entries)加入到他的日志当中,然后并行的向其他服务器发起AppendEntries RPC复制日志条目。当这条请求日志被成功复制到大多数服务器上面之后,Leader将这条日志应用到它的状态机并向客户端返回执行结果。
状态机说明:
要让所有节点达成一致性的状态,大部分都是基于复制状态机来实现的(Replicated state machine)
简单来说就是:初始相同的状态 + 相同的输入过程 = 相同的结束状态,这个其实也好理解,就类似于一对双胞胎,出生时候就长得一样,然后吃的喝的用的穿的都一样,你自然很难分辨。其中最重要的就是一定要注意中间的相同输入过程,各个不同节点要以相同且确定性的函数来处理输入,而不要引入一个不确定的值。使用replicated log来实现每个节点都顺序的写入客户端请求,然后顺序的处理客户端请求,最终就一定能够达到最终一致性。
日志格式说明:
所有节点持久化保存在本地的日志,大概就是类似于这个样子
如何保证日志的正常复制
如果出现了上述leader宕机,导致follower与leader日志不一致的情况,那么就需要进行处理,保证
follower上的日志与leader上的日志保持一致,leader通过强制follower复制它的日志来处理不一致的问题,follower与leader不一致的日志会被强制覆盖。leader为了最大程度的保证日志的一致性,且保证日志最大量,leader会寻找follower与他日志一致的地方,然后覆盖follower之后的所有日志条目,从而实现日志数据的一致性。
具体的操作就是:leader会从后往前不断对比,每次Append Entries失败后尝试前一个日志条目,直到成功找到每个Follower的日志一致的位置点,然后向该Follower所在位置之后的条目进行覆盖。
Raft协议算法代码实现
项目代码结构:
前面我们已经大致了解了Raft协议算法的实现原理,如果我们要自己实现一个Raft协议的算法,其实就是将我们讲到的理论知识给翻译成为代码的过程,具体的开发需要考虑的细节比较多,代码量肯定也比 较大,好在有人已经实现了Raft协议的算法了,我们可以直接拿过来使用一探究竟。基于JRaft实现。
步骤如下:
1.创建maven工程并导入jar包地址如下
2.定义Server端代码实现:
3.将Server端代码定义Server2以及Server3然后运行三个Server端
4.定义客户端代码实现如下:
5.状态机代码:
全部完成了。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。