Raft基础知识

Raft本意是木筏,寓意着集群管理的简单和便捷。

一、概述

Raft在工程上是使用较为广泛的算法,但在学术理论研究上,最出名的是Paxos。

Raft是一个一致性算法,所谓一致性,就是即使在部分节点故障、网络延时、网络分割的情况下,多个节点也能对某个事物达成一致,Raft设计得非常容易理解,在容错性和性能上与Paxos相当。

Raft为了达到易于理解的目标,主要做了两件事情:

  1. 算法分解:主要被分成了领导人选举,日志复制和安全三个模块。
  2. 减少状态机的状态:减少了非确定性和服务器相处非一致性的方式。

二、应用场景

Raft算法常应用于分布式系统,比如分布式存储中的复制集,加密货币(比特币、区块链)等。

三、原理

Raft通过选举一个领导人,然后给予他全部的管理复制日志的责任来实现一致性。领导人从客户端接收日志条目(log entries),把日志条目复制到其他服务器上,并告诉其他的服务器什么时候可以安全地将日志条目应用到他们的状态机中。

通过领导人的方式,Raft将一致性问题分解成了三个相对独立的子问题:

  1. 领导选举: 当现存的领导人发生故障的时候,一个新的领导人需要被选举出来;
  2. 日志复制: 领导人必须从客户端接收日志条目(log entries)然后复制到集群中的其他节点,并强制要求其他节点的日志和自己保持一致。
  3. 安全性: 主要是状态机安全,如果有任何的服务器节点已经应用了一个确定的日志条目到它的状态机中,那么其他服务器节点不能再同一日志索引位置应用一个不同的指令。 BRAFT 通过 BRPC 框架实现了强一致性和数据安全性。在日志复制过程中,BRAFT 会确保每个日志条目被大多数节点写入并提交,从而避免了数据不一致的问题。同时,BRAFT 还提供了数据校验机制,确保数据的完整性和正确性。

节点的状态:

  1. 领导人Leader: 接收客户端的请求,并向Follower同步请求日志,当日志同步到大多数节点上后告诉Follower提交日志。
  2. 跟随者Follower: 接受并持久化Leader同步的日志,在leader说日志可以提交之后,提交日志。
  3. 候选人Candidate: leader选举过程中的临时角色。

3.1、状态转换

Follower只响应其他服务器的请求,如果Follower超时没有收到leader 的消息,它会成为一个Candidate并且开始一次leader选举,收到大多数服务器投票的Candidate会成为新的leader,leader在宕机之前会一直保持leader的状态。

在这里插入图片描述

总结几种状态转换:

  • Follower -> Candidate:当开始选举,或者“选举超时”时
  • Candidate -> Candidate:当“选举超时”,或者开始新的“任期”
  • Candidate -> leader:获取大多数投票时
  • Candidate -> Follower :其它节点成为领袖,或者开始新的“任期”
  • leader -> Follower :发现自己的任期ID比其它节点分任期ID小时,会自动放弃领袖位置

3.2、任期

Raft把时间分割成任意长度的任期,一个任期内只允许有一个leader,正常工作期间只有leader和Followers,每一个term 的开始都是leader选举。

在成功选举leader之后,leader会在整个term内管理整个集群,如果leader选举失败,该term就会因为没有leader而结束。

3.3、raft的三个子问题

3.3.1、Leader选举

raft使用 **心跳(heartbeat)**触发Leader选举,当服务器启动时,初始化为Follower。Leader向所有Followers 周期性发送heartbeat,如果Follower在选举超时时间内没有收到Leader的heartbeat,就会等待一段随机的时间后发起一次Leader选举。

Follower将其当前term加一然后转换为Candidate,它首先给自己投票并且给集群汇总的其他服务器发送requestvoterpc。结果有以下三种情况:

  1. 赢得了多数的选票,成功选举为leader;
  2. 收到了leader 的消息,表示有其他服务器已经抢先当选了leader;
  3. 没有服务器赢得多数的选票,leader选举失败,等待选举时间超时后发起下一次选举;

在这里插入图片描述

选举出leader后,leader通过定期向所有Followers发送心跳信息维持其统治。若Follower一段时间未收到leader的心跳则认为leader可能已经挂了,再次发起leader选举过程。

raft保证选举出leader上一定具有最新的已提交的日志

3.3.2、日志同步

leader选出后,就开始接收客户端的请求。leader把请求作为日志条目(log entries)加入到它的日志中,然后并行的向其他服务器发起append entries rpc复制的日志条目,当这条日志被复制到大多数服务器上,leader将这条日志应用到它的状态机并向客户端返回执行结果。

某些Followers可能没有成功的复制日志,Leader会无限的重试 AppendEntries RPC直到所有的Followers最终存储了所有的日志条目。

日志由有序编号(log index)的日志条目组成。每个日志条目包含它被创建时的任期号(term),和用于状态机执行的命令。如果一个日志条目被复制到大多数服务器上,就被认为可以提交(commit)了。

Raft日志同步保证如下两点:

  1. 如果不同日志中的两个条目有着相同的索引和任期号,则它们所存储的命令是相同的。
  2. 如果不同日志中的两个条目有着相同的索引和任期号,则它们之前的所有条目都是完全一样的。

第一条特性源于Leader在一个term内在给定的一个log index最多创建一条日志条目,同时该条目在日志中的位置也从来不会改变。

第二条特性源于 AppendEntries 的一个简单的一致性检查。当发送一个 AppendEntries RPC 时,Leader会把新日志条目紧接着之前的条目的log index和term都包含在里面。如果Follower没有在它的日志中找到log index和term都相同的日志,它就会拒绝新的日志条目。

一般情况下,Leader和Followers的日志保持一致,因此 AppendEntries 一致性检查通常不会失败。然而,Leader崩溃可能会导致日志不一致:旧的Leader可能没有完全复制完日志中的所有条目。

在这里插入图片描述

上图是一些Followers可能和新的Leader日志不同的情况。一个Follower可能会丢失掉Leader上的一些条目,也有可能包含一些Leader没有的条目,也有可能两者都会发生。丢失的或者多出来的条目可能会持续多个任期。

Leader通过强制Followers复制它的日志来处理日志的不一致,Followers上的不一致的日志会被Leader的日志覆盖。

Leader为了使Followers的日志同自己的一致,Leader需要找到Followers同它的日志一致的地方,然后覆盖Followers在该位置之后的条目。

Leader会从后往前试,每次AppendEntries失败后尝试前一个日志条目,直到成功找到每个Follower的日志一致位点,然后向后逐条覆盖Followers在该位置之后的条目。

3.3.3、安全性

BRAFT 通过 BRPC 框架实现了强一致性和数据安全性。在日志复制过程中,BRAFT 会确保每个日志条目被大多数节点写入并提交,从而避免了数据不一致的问题。同时,BRAFT 还提供了数据校验机制,确保数据的完整性和正确性。

数据的单向流动,即只从领导人传给跟踪者,并且领导人从不会覆盖自身本地日志已经存在的日志条目。

Raft增加了如下两条限制以保证安全性:

  1. 拥有最新的已提交的log entry的Follower才有资格成为Leader。

    • 这个保证是在RequestVote RPC中做的,Candidate在发送RequestVote RPC时,要带上自己的最后一条日志的term和log index,其他节点收到消息时,如果发现自己的日志比请求中携带的更新,则拒绝投票。日志比较的原则是,如果本地的最后一条log entry的term更大,则term大的更新,如果term一样大,则log index更大的更新。
  2. Leader只能推进commit index来提交当前term的已经复制到大多数服务器上的日志,旧term日志的提交要等到提交当前term的日志来间接提交(log index 小于 commit index的日志被间接提交)。

之所以要这样,是因为可能会出现已提交的日志又被覆盖的情况:

(Term是Raft算法中用来标记时间段的一个计数器,每当系统进入一个新的选举周期时,term就会递增。每个term在整个集群中是唯一的。)

在这里插入图片描述

阶段a

  • S1是Leader,term为2,日志(term, index)为(2, 2)被写入并同步到S2。
  • 此时,S1和S2的日志状态为:1(1,1), 2(2,2)。

阶段b

  • S1离线,触发新的选主,S5成为新的Leader,term为3,写入日志(3, 2)。
  • 此时,S5的日志状态为:1(1,1), 3(3,2)。

阶段c

  • S1重新上线并成为新的Leader,term为4,写入日志(4, 3)。
  • S1将自己的日志(2, 2)同步到S3,状态变为:1(1,1), 2(2,2)。

阶段d

  • S1下线,触发新的选主
  • S5可能成为新的Leader,因为它的日志最新(1(1,1), 3(3,2)),term为5。

阶段e

  • S5成为Leader,并将自己的日志(1, 3)同步到S2和S3,覆盖之前提交的日志(2, 2)。

详细说明

  1. 在阶段d,S1的term为4,S5的term为3。S1下线后,触发新一轮选举。
  2. 在阶段d的选举中,S5可以成为新的Leader,因为它的term为5,比S1的term 4更高。
  3. term = 5 > 4表示,S5的term为5,而其他节点的term为4,所以S5可以成为新的Leader

3.4、braft实现细节

为了实现 BRAFT,百度对 BRPC 框架进行了定制化开发。以下是一些关键的实现细节:

  1. 通信协议:BRAFT 利用 BRPC 框架提供的通信机制,实现了节点间的信息交换和状态同步。通过定义特定的协议和消息格式,BRAFT 能够高效地传输日志条目、投票请求和确认消息等关键信息。
  2. 集群管理:BRAFT 需要管理整个分布式系统的节点状态和集群配置。为此,BRAFT 设计了一套集群管理机制,能够动态地添加、删除节点,并根据需要进行负载均衡和故障转移。
  3. 容错机制:在分布式系统中,节点可能会发生故障或网络延迟。为了确保系统的可用性和一致性,BRAFT 实现了多种容错机制,如故障检测、自动恢复和数据校验等。
  4. 性能优化:为了满足大规模分布式系统的需求,BRAFT 在性能方面进行了充分优化。例如,通过缓存技术减少不必要的网络通信,通过批量处理减少 I/O 操作等。

四、实验步骤

4.1、安装cmake

tar -zxvf cmake-3.30.1.tar.gz
cd cmake-3.30.1
./configure
make
sudo make install

4.2、git brpc

git clone https://github.com/apache/incubator-brpc.git
cd incubator-brpc
sh config_brpc.sh --headers=/usr/include --libs=/usr/lib
make

在这里插入图片描述

克隆并构建 brpc。

使用 vcpkg 安装 braft 或手动编译 braft。

运行 braft 示例以验证编译和安装是否成功。

4.3、硬盘分区

sudo apt-get install gparted -y
sudo gparted

在这里插入图片描述

在这里插入图片描述

braft是基于brpc的Raft协议工业级C++实现,设计之初就是考虑 高性能和低延迟

由百度云分布式存储团队打造

  • Leader election.(领导人选举)
  • Replication and recovery.(复制与恢复)
  • Snapshot and log compaction.(快照和日志压缩)
  • Membership management.(成员管理)
  • Fully concurrent replication.(全并发复制)
  • Fault tolerance.(容错)
  • Asymmetric network partition tolerance.(非对称网络分区容忍)
  • Workaround when quorate peers are dead.(当有足够投票权的对等实体死亡时的临时解决方案)
src/braft/log_manager.h/cpp` 和 `src/braft/raft_node.h/cpp

五、源码分析

braft/src/braft at master · baidu/braft (github.com)

  • Leader election.(领导人选举)
  • Replication and recovery.(复制与恢复)
  • Snapshot and log compaction.(快照和日志压缩)
  • Membership management.(成员管理)
  • Fully concurrent replication.(全并发复制)
  • Fault tolerance.(容错)
  • Asymmetric network partition tolerance.(非对称网络分区容忍)
  • Workaround when quorate peers are dead.(当有足够投票权的对等实体死亡时的临时解决方案)

5.1、ballot:

ballot,ballot_box

选票类,raft的选举leader步骤

5.2、cli(命令行接口):

cli,cli_service

客户端,cli用来修改或者获取复制状态机组节点状态,cli_service继承cli

节点的增加和删除:

add_peer

remove_peer

5.3、closure:

closure_helper,closure_queue

管理返回信息和报告内容,通常用于异步操作的回调

5.4、configuration:

configuration,configuration_manager

configuration包含复制状态机的配置信息,peerid类,nodeid类

configuration_manager用来记录和管理configuration条目历史变更

5.5、file:

file_reader:读取文件内容

file_service:为状态机增加或者移除一个file_reader

file_system_adaptor:文件系统适配

5.6、protobuf_file:

remote_file_copier:远程文件复制,存储使用快照

5.7、fsm_caller:

复制状态机,发生事件时的操作

5.8、fsync:

参数选择内存数据落到磁盘的方式,直接调用mingw函数

5.9、lease:

作为leader节点和follower节点的状态

5.10、log:

log和log_entry定义了日志条目和日志的存储结构

log:日志类,log定义和LogStorage定义

log_entry:每条日志条目

log_manager:用于管理日志条目,确保他们被复制和应用

memory_log:日志条目索引相关操作

5.11、node:

node:节点类,每一个node是一个复制状态机的节点实例,负责处理日志复制、状态转换等。

node_manager:node节点管理

5.12、raft(核心):

raft:定义task,状态机操作,节点参数,状态机参数等

raft_meta:

raft_service

5.13、repeated_timer_task:

重复任务计时器

5.14、replicator(复制器):

leader,follower之间的信息复制,确保日志的一致性。

5.15、route_table(路由表):

一张通信表,存多个复制组,可获取每个复制组leader,更新组配置等

5.16、snapshot:

snapshot:快照类,管理快照的创建和恢复

snapshot_executor:快照执行操作

snapshot_throttle:大容量磁盘读/写

5.17、storage:

存储类,包括元数据存储,log存储,快照存储,稳定存储等

5.18、util:

放一些常用的公用方法,可能用于辅助共识过程中的各种操作

六、具体分析

当raft算法中的leader收到一条新的日志条目时,它需要将这个条目复制给其他服务器,并确保其他服务器的日志与自己本地的日志保持一致。

1、领导人必须有关于被提交日志的最新信息

即在它的任期里必须马上提交一条空白的日志条目,即心跳;

这段话的意思是在一个节点成为Leader之前,至少向多数节点发送一次心跳来进行确认日志情况,在没收到心跳响应之前是不能响应客户端的;

2、领导人在处理只读的请求之前必须检查自己是否已经被废除了

具体实现是Leader在响应只读请求之前,先和集群中的大多数节点交换一次心跳信息来处理这个问题,即发送一次心跳的RPC,收到响应无误之后才能返回给客户端,即每次读请求要和多数成员做一次心跳以确认自己仍然是 Leader。

  • AppendEntries RPC

leader使用AppendEntries RPC同步日志数据给其它follower, (心跳是没有日志记录的

  • InstallSnapshot RPC

正常情况下leader是使用AppendEntries RPC同步日志数据,但是当一个follower落后leader太多时,raft会使用InstallSnapshot RPC 来使其快速补充数据

在这里插入图片描述

一次完整的请求的过程:

1、客户端提交到请求至Leader:

客户端将一个新的日志条目提交给Leader节点。

2、Leader自己先保存日志,注意这里不提交:

Leader将新的日志条目追加到自己的日志中,但此时并不提交。

3、Leader将日志同步给Follower,这里只画了1条线,即只同步给C,实际上是都会同步:

Leader向集群中所有Follower节点发送AppendEntries RPC请求,包含新的日志条目。

4、Follower收到日志后保存日志并响应给Leade:

Follower收到AppendEntries RPC请求后,将日志条目追加到自己的日志中,并发送响应给Leader,响应中包含是否成功追加日志条目。

5、Leader处理响应:

Leader只要收到一个Follower的响应后马上将这条日志提交并应用到状态机中:

Leader处理Follower的响应,并根据以下情况进行操作:

  • 成功追加(响应同意):如果超过半数(包含Leader本身)节点成功追加日志条目,Leader将日志条目标记为已提交,并更新已提交的索引(commitIndex)。然后,Leader将日志条目应用到状态机中,并向客户端发送成功响应。
  • 追加失败(响应反对):如果有Follower节点响应失败,Leader将重试发送AppendEntries RPC,直到日志条目复制成功或发生领导者选举。

6、应用日志条目:

一旦日志条目被标记为已提交,Leader和Follower节点会将已提交的日志条目应用到它们的状态机中(即执行日志条目对应的操作)。

7、响应客户端:

Leader在日志条目被提交后,向客户端发送响应,表示操作已成功完成。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值