要解决的问题
期望达成下面目录
* 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
英文原文论文:
官方网站(小竹筏):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/