TIKV 源码分析(一)raft-rs 组件

简介: Raft 是分布式领域中应用非常广泛的一种共识算法,相比于此类算法的鼻祖 Paxos,具有更简单、更容易理解和实现的特点。TiKV 依赖的周边库 raft-rs 是参照 ETCD 的 RAFT 库编写的 RUST 版本。本文不会详细介绍 RAFT 协议的原理或者实现,而是利用 raft-rs 的示例程序来讲解 raft-rs 如何使用。Public API 简述RawNode 结构体TIKV 的 

Raft 是分布式领域中应用非常广泛的一种共识算法,相比于此类算法的鼻祖 Paxos,具有更简单、更容易理解和实现的特点。TiKV 依赖的周边库 raft-rs 是参照 ETCD 的 RAFT 库编写的 RUST 版本。

本文不会详细介绍 RAFT 协议的原理或者实现,而是利用 raft-rs 的示例程序来讲解 raft-rs 如何使用。

Public API 简述

RawNode 结构体

TIKV 的 RAFT 对外接口是 RawNode 结构体:

pub struct RawNode<T: Storage> {
    /// The internal raft state.
    pub raft: Raft<T>,
    ...
}

这个结构体重要的接口有:

impl<T: Storage> RawNode<T> {
    pub fn propose(&mut self, context: Vec<u8>, data: Vec<u8>) -> Result<()>
    pub fn propose_conf_change(&mut self, context: Vec<u8>, cc: impl ConfChangeI) -> Result<()>
    pub fn step(&mut self, m: Message) -> Result<()>
    
    pub fn ready(&mut self) -> Ready
    
    pub fn advance(&mut self, rd: Ready) -> LightReady
    
    pub fn tick(&mut self) -> bool
    
}

Ready 结构体

我们知道,RAFT 中流转的 Log Entries 分为两种类型,一种是已经被大多数节点确认的 Log,叫做 committed entries,一种是暂时还未被大多数节点确认的 Log,就简单的叫做 Entries。两种 Log Entries 都可以通过 ready 函数接口从 RAFT 状态机中获取,这个就是 Ready 结构体:

pub struct Ready {
    ...

    // 发到 Raft 中,但尚未持久化的 Raft Log
    entries: Vec<Entry>,

    light: LightReady,
    
    ...
}

pub struct LightReady {
    // 已经持久化,并经过集群确认的 Raft Log。
    committed_entries: Vec<Entry>,
    
    // Raft 产生的消息,以便真正发给其他节点。
    messages: Vec<Message>,
}

RAFT 状态机流转

了解了 RAFT 的大概接口和 Ready 的大概作用,我们就可以了解使用 RAFT 的大概流程了

Leader 角度

  • 一阶段

在第一个阶段里,一份 Data 数据会被 RAFT 状态机转换为两份数据,一份数据转换为 Entries,然后落盘存储到 Disk,另一份数据转换为 Message,发送给其他 Follower 节点。

  • 应用接受到请求 Data 信息
  • 应用通过调用 RAFT 的 propose 接口将 Data 数据传递到 RAFT 状态机中去
  • 应用调用 Ready 函数等待 从 RAFT 中获取 Ready 结构体,从 Ready 结构体中拿出 Entries 和 Message,分别进行落盘和转化为 MsgAppend 信息传递给 Follower。
  • 应用还需要调用 advance 接口,来更新 RAFT 的内部状态,例如 Log index 信息,代表 Log Entries 已落盘。

  • 二阶段

  • Follower 收到 Message 进行处理后 (例如落盘) 会将 Entries 的确认信息 MsgAppend Response 发送回给 Leader,值得注意的是这个 Message 中含有 Follower 已接收的最新的 Log Entries Index。

  • 当 Leader 收到 Follower 节点的 Message 确认信息后,将会调用 step 函数将 Message 传递到 RAFT,RAFT 就会更新 Follower 的状态信息,尤其重要的是各个 Follower 的 Log Index 信息。

  • 应用调用 Ready 接口后,就会将大多数 Follower 确认的 Log Entries 放到 Ready 结构体,应用就会收到已确认的 Committed Entries,可以对其进行 Apply。

  • 之后依然还要调用 advance 接口,更新 RAFT 模块的状态,例如更新 Apply Index 信息,代表已提交。

  • 最后,Leader 在给 Follower 发送 HeartBeat Msg 的时候,会带着 Leader 的 Committed Index,以此来告知 Follower 对应的 Log Entries 已经被提交,Follower 可以进行对应的 Apply 流程了。

到此为止,Leader 和 Follower 已全部接受到最新的 Data 信息。 

Follower 角度

  • 第一阶段

  • Follower 收到 Leader 的 Message 信息后,应用会调用 step 函数将 MsgAppend 传递到 RAFT。这个 MsgAppend 中含有 Follower 需要落盘的 Log Entries 信息

  • 当用户调用 Ready 后,RAFT 就会将加工好的 Ready 结构体传递给应用,应用拿到 Log Entries 后进行落盘,然后将确认信息传递回 Leader。值得注意的是,RAFT 的 pipeline 要求 Leader 的落盘和 Message 的传递两个步骤是并行的,但是 Follower 必须落盘后才能调用 Transport Send,防止发送成功后,Follower 落盘失败。

  • 最后依然需要调用 advance 接口,更新 RAFT 状态。

  • 第二阶段

  • Follower 接受到 MsgHeartbeat 或者 MsgAppend 信息后,会从信息中获取 Leader 的 commit index

  • 应用调用 Ready 后,Follower 会根据 Leader 的 Commit Index,计算出 Committed Entries,从而对这些信息进行 Apply

至此,Leader 和大多数 Follower 都将 Log Entries 落盘,并对其数据进行 Apply

相关文章
TIKV 源码分析(二)raft-rs 示例程序
前言raft-rs 的 5 节点示例程序稍微比较复杂一些,但是看懂的话,就会对 raft 的使用得心应手。示例程序Node 结构体struct Node {     // None if the raft is not initialized.     raft_group: Option<RawNode<MemStorage>>,     my_mailbox: Rece
|
存储 前端开发 C++
通过Consul Raft库打造自己的分布式系统
通用的CP系统有etcd和consul, 通用的对立面就是专用系统. 所以在某些场合是有这种需求的. 然而etcd embed的可用性极差, Windows上面跑会出现各种问题, 而且不能定制协议, 你必须得用etcd定义好的协议和客户端来和etcd集群通讯. 所以这时候的选择: 忍着 自己实现一个raft算法库, 在这上面做应用 有一定的可能性, 起码MIT 6.824可以做出来, 但是和工业应用还是有很大的差距 找一个工业级raft库, 然后在这上面做应用
|
算法 Apache 文件存储
zookeeper的leader选举原理和底层源码实现超级详解 1
zookeeper的leader选举原理和底层源码实现超级详解
170 1
|
存储
zookeeper的leader选举原理和底层源码实现超级详解 2
zookeeper的leader选举原理和底层源码实现超级详解
105 1
|
存储 JSON 算法
etcd-raft 模块如何实现分布式一致性?
etcd-raft 模块如何实现分布式一致性?
208 0
|
监控 索引
Etcd中raft协议源码分析
以etcd或者docker中的raft协议为列子,来解析raft协议的实际落地 server通信 概述 server之前的消息传递并不是简单的request-response模型,而是读写分离模型, 即每两个server之间会建立两条链路,对于每一个server来说,一条链路专门用来发送数据,另一条链路专门用来接收数据. 在代码实现中,通过streamWriter发送数据,通过streamReader接收数据。
3924 0
|
存储 算法 调度
SOFAJRaft-RheaKV 是如何使用 Raft 的 | SOFAJRaft 实现原理
Scalable Open Financial Architecture Stack 是蚂蚁金服自主研发的金融级分布式架构,包含了构建金融级云原生架构所需的各个组件,是在金融场景里锤炼出来的最佳实践。
1941 0
|
7月前
|
索引
Etcd/Raft 原理问题之follower会进入StateReplicate状态时的问题如何解决
Etcd/Raft 原理问题之follower会进入StateReplicate状态时的问题如何解决
Etcd/Raft 原理问题之follower会进入StateReplicate状态时的问题如何解决
|
算法 Go vr&ar
Raft简单实现小结
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/feilengcui008/article/details/64135653 上一周花了大部分时间重新拾起了之前落下的MIT6.824 2016的分布式课程,实现和调试了下Raft协议,虽然Raft协议相对其他容错分布式一致性协议如Paxos/Multi-Paxos/VR/Zab等来说更容易理解,但是在实现和调试过程中也遇到不少细节问题。
1348 0

热门文章

最新文章