要解决的问题

期望达成下面目录
* 1. 深入理解raft 的工作原理;
* 2. 熟悉raft和业务模块的各个接口;

原则

  • 先理解逻辑,然后结合代码,确认
  • 结合代码,理解之前的各个接口

功能分解

leader election

log replication

log compaction

configuration change

相关接口分解

Node (Replcia)

功能

管理单个副本、选主、且主等;

重要的数据结构

Timer

  • ElectionTimer
    follower 状态的部分在超过一定时间没有收到主的心跳之后,会触发Election Timeout, 自己发起PreVote/Vote
  • Vote Timer
    主动发起选主的时候会用到,设置后表示多久发起选举

  • SnapshotTimer
    周期dump 内存中的索引、映射等关系到磁盘

  • Stepdown timer
    表示主多久没有收到从的心跳确认之后,开始step down (为了防止脑裂?)

相关ID

node_id, rg_id

配置管理相关的数据结构

 ConfigurationCtx _conf_ctx;
 ConfigurationManager* _config_manager;
 ReplicatorGroup _replicator_group;

LogEntry相关的数据结构

LogStorage* _log_storage;
StableStorage* _stable_storage;
LogManager* _log_manager;

Snapshot/checkpoint 相关的数据结构

SnapshotExecutor* _snapshot_executor;

仔细看下SnapshotExecutor 相关的数据结构,如下:

raft_mutex_t _mutex;
    int64_t _last_snapshot_term;
    int64_t _last_snapshot_index;
    int64_t _term;
    bool _saving_snapshot;
    bool _loading_snapshot;
    bool _stopped;
    bool _usercode_in_pthread;
    SnapshotStorage* _snapshot_storage;
    SnapshotCopier* _cur_copier;
    FSMCaller* _fsm_caller;
    NodeImpl* _node;
    LogManager* _log_manager;
    // The ownership of _downloading_snapshot is a little messy:
    // - Before we start to replace the FSM with the downloaded one. The
    //   ownership belongs with the downloding thread
    // - After we push the load task to FSMCaller, the ownership belongs to the
    //   closure which is called after the Snapshot replaces FSM
    base::atomic<DownloadingSnapshot*> _downloading_snapshot;
    SnapshotMeta _loading_snapshot_meta;
    bthread::CountdownEvent _running_jobs;
    scoped_refptr<SnapshotThrottle> _snapshot_throttle;

这里关键的数据结构式SnapshotStorage, 看SanpshotStorage 可以看到,这个是一个抽象接口类,里面主要定义了snapshot相关的底层file/filesystem 操作、snapshot 读读写拷贝相关的操作接口。 特别地,在下面接口中关联了底层的文件系统操作:

“`
virtual int set_file_system_adaptor(FileSystemAdaptor* fs) {
(void)fs;
CHECK(false) << base::class_name_str(*this)
<< " doesn't support file system adaptor";
return -1;
}
“`
上面这个函数在raft Node 初始化的时候会被用到.从业务层到raft 层,它的初始化过程如下:
* 1. 业务层继承FileAdaptor 基类,实现适配特定IO模式的Adaptor基本操作;
* 2. 业务层继承raft node/replicaion 基类,这个类里有个FileAdaptor 指针成员;
* 3. 业务层创建1个上面步骤1中类的实例,将其指针赋值給上面步骤2中的FileAdapotor 成员;
* 4. 在业务层的复制组成员replica 初始化的过程中,会最终执行到raft node的初始化;
* 5. 在上面步骤4的执行过程中,会去执行snapshot storge 的初始化,指定checkpoint/snapshot 的访问方式;
* 6. 在上面5的执行过程中:SnapshotExecutorOptions会用上面步骤3中传入的FileAdaptor 指针,来初始化snapshot_executor;
* 7. snapshot_executor 初始化过程中:会创建1个_snapshot_storage, 然后用步骤6中传入的FileAdaptor 指针来初始化_snapshot_storage 中的FileAdapor 成员;

至此,就完成了raft checkpoint 中抽象的读写接口接入。

LogStorage

管理log entry 的append, apply, replicate

Snapshotor /checkpointer

负责做快照、加载快照

FileAdator, FilesystemAdaptor

管理checkpointer 的拷贝

控制节点副本状态的管理

相关元数据

副本管理

业务场景

均衡,掉电坏盘自动补充副本

总结

通过上面的分析可以看到,要深入理解分布式系统中的各个模块的接口和逻辑,就首先需要对实现分布式一致性协议的实现及其接口有深入的了解才行。

参考

论文

一篇翻译不错的raft论文(中文):
https://github.com/maemual/raft-zh_cn/blob/master/raft-zh_cn.md
英文原文论文:

PDF

官方网站(小竹筏):https://raft.github.io/ (有了这个基本上可以帮助你加深很多的理解)
[Raft和它的三个子问题] http://catkang.github.io/2017/06/30/raft-subproblem.html
aft 官网:https://raft.github.io/
简单易懂RAFT动画: http://thesecretlivesofdata.com/raft/