美文网首页
RAFT分布式K/V缓存 C++实现

RAFT分布式K/V缓存 C++实现

作者: JimmyPan | 来源:发表于2020-06-20 09:50 被阅读0次

学习MIT6.824时,想从socket开始写起,构建网络层,RPC层,Raft层,到应用层的k/v缓存。想自己实现去感受一下各个部分到底是如何运作的,以学习和练习为主,并且为之后的学习打好基础。

参考资料:
网络库:主要参考muduo,事件驱动的非阻塞网络库
RPC : protobuf https://developers.google.com/protocol-buffers/docs/reference/cpp/google.protobuf.service
可视化:https://raft.github.io/
可视化: http://thesecretlivesofdata.com/raft/
论文原版:https://ramcloud.atlassian.net/wiki/download/attachments/6586375/raft.pdf
论文中文翻译: https://github.com/maemual/raft-zh_cn/blob/master/raft-zh_cn.md
MIT6.824 Lab2 :https://pdos.csail.mit.edu/6.824/labs/lab-raft.html

github:https://github.com/JiaMing5139/DistributedSystem

一.Raft中数据结构的定义,对应论文中Figure2

1.RPC消息格式 (protobuf)

message LogEntry{
int64 index = 1;
int64 term = 2;
string commandName =3 ;
bytes command = 4;
}

message AppendEntriesRequest {
int64 term = 1;
string leaderId = 2;
int64 prevLogIndex = 3;
int64 prevLogTerm =4;
repeated LogEntry LogEntries = 5;
int64 leaderCommit;
}

message AppendEntriesResponse {
int64 Term = 1;
int64 Index = 2;
int64 CommitIndex =3;
bool Success = 4;
}
论文Figure写 Response中包括 Success 和term
但只靠success 分别不出来一种状态,就是对端是否确认有一个新的log被match,原地不动和有新logMatch都返回true,所以需要本地再维护一个状态判断是否又新log被发出
加上Index,对端直接返回自己match的序号,更简单

message RequestVoteRequest {
uint64 Term=1;
uint64 LastLogIndex=2;
uint64 LastLogTerm=3;
string CandidateName=4;
}

message RequestVoteResponse {
uint64 Term=1;
bool VoteGranted=2;
}

service RaftService {
rpc AppendEntries(AppendEntriesRequest) returns(AppendEntriesResponse);
rpc Vote(RequestVoteRequest) returns(RequestVoteResponse);
}

2. 每个Raft实例需要维护的变量

class Client{
int64_t nextIndex_ = 0;
int64_t matchIndex_ = 0;
bool voteGranted_ = false;
std::string ClientName;
}
class Raft{
int64_t currentTerm_; // 当先raft的term
State currentState_; //当前状态
int votedNum_;//收集到的投票
string votedFor_; //当前term给谁投的票
vector<Client> clients_//其他节点
}

一.领导人选择的限制条件

- Raft为了保证同一Term只有一个Leader被选出,选举时需要什么条件?

过半结点投票同意,每个任期只能投票一次。如果出现等票情况
则electiontimeout过期自动开启新一轮Term的领导人选举
成为leader后立马发出appendEntry 限制新的candidatate出现

- 为了保证安全性,选举时需要条件?

安全性就是指提交后的日志,不会被再次覆盖
因为必须超过半数才能当领导人,两个任期内必定会有一个完整的结点是重合的。
所以不论怎么发生网络划分,都必须让那个两个任期中重复的,也是日志最完整的结点当Leader,才能保证不会被其他的日志覆盖,所以要进行限制
一种临界情况:
S1 S5 结点的Term为45
S2 S3 S4 的Term为2,已经有三个日志确认提交,返回给客户端成功
此时RAFT必须保证三条日志不会丢失


屏幕快照 2020-06-22 上午5.47.56.png

为了保证RAFT正常工作,必须有3个及以上的结点正在运行,才能选出Leader正常运行
这时候S3 S4两个保证完整结点死掉,S1 S5重启成功,这时候仅剩1个S2保存着正常结点
为了让这个运行着的RAFT返回正确的结果,必须让S2当结点Leader,保证日志的完整性


屏幕快照 2020-06-22 上午5.48.11.png

状态转移表:

event\state follower
electionTimeout 清空上一个任期内的记录
this.currentTerm++
this.currentState=Candidate
votedFor = selfname
for c in clients to c.requestRpc()
RequestVoteRpc 如果日志不是最新的,返回false
Term的三种情况:
1.localterm大于peerterm,说明收到的请求是过期的candidate返回false
2.localterm等于peerterm,说明同一时间有两个candidate再发送投票请求,只投一个
3.localterm小于peerterm,说明这是一个新Term的请求,且是第一个到的,将之设置为自己的Term,返回true
VoteResponseRpc 如果peertrm > localterm localterm = peerterm
event\state candidate
electionTimeout 清空上一个任期内的记录
this.currentTerm++
this.currentState=Candidate
votedFor = selfname
for c in clients to c.requestRpc()
(有人瓜分选票,没有出现leader,再次重复给没有voteGranted为false的client发送s)
RequestVoteRpc 处理同follower,但肯定反回false,因为成为candidate必然已经投票给了自己
VoteResponseRpc if(RequestVoteResponse.VoteGranted == true){
client.voteGranted=true
查看有多少client的voteGranted为true
if 超过一半
this.currentState=Leader
start AppendEntriesTimer
让所有client中的nextinex = commitedIndex + 1
}
if (RequestVoteResponse.Term > currentTerm) current =Term
event\state Leader
electionTimeout Leader不会有electionout
RequestVoteRpc 如果peerTerm比较大,说明自己是一个过期的Leader,将状态变为Follower,然后像一半的follower投同意票
VoteResponseRpc 丢弃?

二.日志提交的限制条件

日志提交指的是,已经给客户端返回成功,保证该日志永远不会丢失。

  • 如何判断多数结点都已经在N处保存了log?

多数client的matchIndex >= N

  • 为了保证安全性,需要什么限制条件?

不提交之前任期的日志

  • 日志复制中需要用到的各个参数的变化时机
  • client.nextindex 用于推测client中需要下一个日志的位置
    1.初始化为最后一个日志的坐标+1
    2.当AppendEntry返回True时,nextIndex可能+1
    3.当 AppendEntry返回false时, nextIndex - 1
  • client.matchindex 保证matchIndex已经被该客户端所保存
    1.初始化为0
    2.当AppendEntry返回时,matchIndex=peer.matchIndex

用来保证安全,用来保守的表示从头数已经共享到第几个log给字节点,

event\state Leader
heartBeatTimeout 1.设置preIndex和preTer
(preLogindex,preTerm = locallogs[client.nextIndex - 1].index,term)
2. 如果nextIndex在log的范围内,且matchIndex + 1 = nextindex?{
则说明已经匹配上位置,并添加nextIndex位置的log到rpc中
}
3.重设heartBeat定时器
AppendEntryRpc 产生leader后,同一个term只有一个leader,所以leader不可能会收到另一个leader发来同一任期的AppendEntryRpc
if peerterm>localterm leader变为follower,设置自己term]
AppendResponseRpc 1.如果peerTerm>localTerm 则一定返回的是false,自己是一个过期Leader,状态变为follower
2.如果peerTerm<=localTerm ,且成功则可以开始继续处理,否则对应client的nextIndex-1,matchIndex = response.matchIndex(0)
3.如果是一个新match的log,则让client.match++ client.nextIndex = client.match+1
4.如果最新match的日志是在自己任期内,则开始进行commited判断
event\state Follower
heartBeatTimeout
AppendEntryRpc 0. 重置electionTimer
1.term检查,失败返回false
2.if preIndex preTerm匹配检查,response.matchIndex = 0失败返回false
3.else 匹配成功,如果日志不为空,则将日志添加,且进行commindex更新(leadercommit和logs.back().index中的较小值)
AppendResponseRpc
event\state Candidate
heartBeatTimeout
AppendEntryRpc 0. 重置electionTimer,设置状态为follower
1.term检查,失败返回false
2.if preIndex preTerm匹配检查,response.matchIndex = 0失败返回false
3.else 匹配成功,如果日志不为空,则将日志添加,且进行commindex更新(leadercommit和logs.back().index中的较小值)
AppendResponseRpc

三.与客户端的交互

相关文章

  • 总览

    分布式算法,cap、paxos、raft、zk等分布式缓存,redis分布式消息队列,kafkarpc,高性能ni...

  • etcd实现-全流程分析

    etcd 通过raft实现分布式一致性,实现参照raft的论文并做了很少的修改(优化), 本次文章整理raft的基...

  • 【raft】分布式一致性算法raft

    Raft目前是最著名的分布式共识性算法,被广泛的应用在etcd、k8s中。 根据Raft论文,可将Raft拆分为如...

  • Raft

    https://raft.github.io/https://www.youtube.com/watch?v=6K...

  • 用go语言实现LFU缓存,两种方法实现_leetcode

    LFU Cache用go语言实现LFU缓存,两种方法实现 题目: 思路: 双哈希实现,mapKvc存K-[V,Cn...

  • 分布式系统-一致性算法-Raft算法

    Raft算法介绍 raft是一个协议,可以用来实现分布式系统的一致性。 raft算法中节点的三种状态 Leader...

  • 理解分布式一致性:Paxos协议之Basic Paxos

    在理解分布式一致性:Raft协议中,我们详细分析了什么是分布式一致性和实现分布式一致性的Raft协议,本文我们主要...

  • 1.redis的基础数据结构

    分布式缓存技术的使用 redis的魅力 数据结构 k-v key key是二进制安全的,可以用任何二进制序列作为k...

  • 每周阅读(3/13/2017)

    缓存那些事本地缓存:编程实现和一些开源实现比如ehcache和guava cache;分布式缓存:memcache...

  • 分布式锁实现

    基于数据库实现分布式锁基于缓存(redis,memcached)实现分布式锁基于Zookeeper实现分布式锁 s...

网友评论

      本文标题:RAFT分布式K/V缓存 C++实现

      本文链接:https://www.haomeiwen.com/subject/mvhctktx.html