# Mit 6.824 Raft实验 2A 2B
Mit 6.824 Raft实验 2A 2B
Author: Minghao Zhou
这个项目写了好久,从一点也看不懂开始,到最后debug就和回家一样自然,成就感还是很足的哈哈。看大佬的架构和代码感觉学到了很多,故在此记录一下。
\src\raft> go
test -run 2A
Test (2A): initial election ...
... Passed -- 3.1 3 96 24384 0
Test (2A): election after network failure ...
... Passed -- 4.5 3 210 40306 0
Test (2A): multiple elections ...
... Passed -- 5.5 7 924 168376 0
PASS
ok 6.5840/raft 13.351s
\src\raft> go test -run 2B
Test (2B): basic agreement ...
... Passed -- 0.6 3 16 4058 3
Test (2B): RPC byte count ...
... Passed -- 1.6 3 48 112838 11
Test (2B): test progressive failure of followers ...
... Passed -- 4.9 3 202 41134 3
Test (2B): test failure of leaders ...
... Passed -- 4.9 3 298 64009 3
Test (2B): agreement after follower reconnects ...
labgob warning: Decoding into a non-default variable/field Term may not work
... Passed -- 4.2 3 139 33129 7
Test (2B): no agreement if too many followers disconnect ...
... Passed -- 3.5 5 322 61842 4
Test (2B): concurrent Start()s ...
... Passed -- 0.7 3 20 5070 6
Test (2B): rejoin of partitioned leader ...
... Passed -- 4.0 3 223 50502 4
Test (2B): leader backs up quickly over incorrect follower logs ...
... Passed -- 17.4 5 2500 1956440 102
Test (2B): RPC counts aren't too high ...
... Passed -- 2.3 3 70 17938 12
PASS
ok 6.5840/raft 44.311s
0. 参数介绍
论文中给出了很多必要的参数,每一个参数都需要使用。这里我给出对论文中说明的一些代码细节的补充
State
变量 | 说明 |
---|---|
currentTerm | 目前轮次 |
votedFor | 给谁投票,主要用于记录当前伦次是否已经投过票了 |
log | Entry结构的数组,用于存放当前Server的所有指令;log[0]存放nil |
commitIndex | 目前已提交的最新Entry的index,对于Follower来说,这个变量将和Leader的同步;对于Leader来说,将在大部分机器都同意后成功commit,更新至len(rf.log) - 1 |
lastApplied | 目前提交到状态机的最新Entry的index |
nextIndex[] | 一个只有当Leader的时候才会启用的数组,记录了每个机器的下一个指令放置的位置 |
matchIndex[] | 同上,记录了每个机器已经和leader同步了的最新的指令的坐标。 |
AppendEntries RPC
这个RPC用于同步指令,随时钟启动。其中的Entries[]变量给出了这次需要同步的指令,如果这个变量为空(nil),则这个包被称作心跳包,用于维持leader的身份,将阻止peer开始选举。
需要注意的是,RPC中所有的变量需要大写字母开头,和论文给出的变量名不同。
Arg
变量 | 说明 |
---|---|
Term | Leader的term,用于接收信号的peer识别这是不是一个靠谱的leader |
LeaderId | Leader的ID |
PrevLogIndex | Leader已有的最新的指令的位置(非本次传输的指令) |
PrevLogTerm | Leader已有的最新指令的Term,如果这个Term不匹配,说明本机和Leader的指令不是一个时期的。 |
Entries[] | Entry结构体的数组,存放若干个Entry,为本次需要同步的指令。 |
LeaderCommit | Leader已经认可的最新可以commit的位置,如果比自己的新就同步。 |
Reply
变量 | 说明 |
---|---|
Term | 返回给leader自己的term,如果比leader的新就说明leader该下台了 |
Success | 本次是否同步成功。如果不成功可能是日志冲突之类的 |
RequestVote RPC
这个RPC用于candidate向别的peer请求投票。里面的内容用于说明candidate的身份和水平(日志是否够新)
Arg
变量 | 说明 |
---|---|
Term | Candidate的term |
CandidateID | candidate的身份识别码 |
LastLogIndex | Candidate最新日志的index |
LastLogTerm | Candidate最新日志的Term |
Result (我觉得这个应该叫reply)
变量 | 说明 |
---|---|
Term | 收到request的peer的term |
VoteGranted | 是否投票给Candidate |
1. 流程介绍
- 这个流程图是简化后的、没有任何差错的情况下会发生的,实际上在各种情况下,server可能会在三种状态间互相转换。具体转换规则如下图:
- 所有服务器在任何情况都要遵守的规则如下
- (ApplyLoop)如果commitIndex大于lastApplied,就说明有一部分Leader已经认可的指令还没有apply,应用至状态机
- 任何时候,如果任何一个RPC中,对方的Term如果高于自己,就直接设置term为对方的term并转为Follower。
2. Ticker
在我写这个代码的过程中,我最迷茫的是这个ticker该怎么写,一开始写了一份很烂的代码就是源于一个不合理的ticker。后来网上看别人的代码,最后选了这个结构:
func (rf *Raft) ticker() {
for !rf.killed() {
// Your code here (2A)
// Check if a leader election should be started.
// pause for a random amount of time between 50 and 350
// milliseconds.
select {
case <-rf.heartbeatTimer.C:
DPrintf("server %d [%s] receive heartbeat timer\n", rf.me, rf.role)
if rf.role == Leader {
DPrintf("server %d [Leader] receive heartbeat timer, start heartbeat\n", rf.me)
rf.HeartBeat()
rf.heartbeatTimer.Reset(rf.heartBeatInterval)
}
case <-rf.electionTimer.C:
rf.startElection()
}
}
}
- 其中每次触发完heartbeat后就重置一下heartbeat计时器,达到心跳的效果;
- 每次变成candidate或者follower就重置一下election计时器,超时了就会开始选举。
3. Make
func Make(peers []*labrpc.ClientEnd, me int,
persister *Persister, applyCh chan ApplyMsg) *Raft {
rf := &Raft{}
rf.peers = peers
rf.persister = persister
rf.me = me
// Your initialization code here (2A, 2B, 2C).
rf.role = Follower
rf.term = 0
rf.voteFor = -1
rf.heartBeatInterval = 50 * time.Millisecond
rf.electionTimer = time.NewTimer(rf.randomTimeout())
rf.heartbeatTimer = time.NewTimer(rf.heartBeatInterval)
// 2B
rf.commitIndex = 0
rf.lastApplied = 0
rf.applyCh = applyCh
rf.log = make([]Entry, 1)
// initialize from state persisted before a crash
rf.readPersist(persister.ReadRaftState())
// start ticker goroutine to start elections
go rf.ticker()
go rf.applyLoop()
return rf
}
- Make函数中包含了初始化,和开启循环的过程。我的代码选择让ticker只负责前两个循环,applyloop手动再开一个循环。
- 一个比较困惑我的地方是这个lab的说明中提示最好不要使用timer,使用sleep来进行计时,但是每次转换成candidate或follower的时候都需要重置这个计时,想半天想不明白咋搞,所以我最后还是选择了timer哈哈。
4. 小结
- 感觉有了以上内容应该不难写出整个项目,加油吧!
5. 参考网址
https://www.zhihu.com/tardis/zm/art/103849249?source_id=1005
https://thesquareplanet.com/blog/students-guide-to-raft/
https://pdos.csail.mit.edu/6.824/labs/lab-raft.html
http://thesecretlivesofdata.com/raft/#replication
# Mit 6.824 Raft实验 2A 2B的更多相关文章
- MIT 6.824 Llab2B Raft之日志复制
书接上文Raft Part A | MIT 6.824 Lab2A Leader Election. 实验准备 实验代码:git://g.csail.mit.edu/6.824-golabs-2021 ...
- MIT 6.824 Lab2D Raft之日志压缩
书接上文Raft Part C | MIT 6.824 Lab2C Persistence. 实验准备 实验代码:git://g.csail.mit.edu/6.824-golabs-2021/src ...
- MIT 6.824 Lab2C Raft之持久化
书接上文Raft Part B | MIT 6.824 Lab2B Log Replication. 实验准备 实验代码:git://g.csail.mit.edu/6.824-golabs-2021 ...
- MIT 6.824(Spring 2020) Lab1: MapReduce 文档翻译
首发于公众号:努力学习的阿新 前言 大家好,这里是阿新. MIT 6.824 是麻省理工大学开设的一门关于分布式系统的明星课程,共包含四个配套实验,实验的含金量很高,十分适合作为校招生的项目经历,在文 ...
- MIT 6.824 lab1:mapreduce
这是 MIT 6.824 课程 lab1 的学习总结,记录我在学习过程中的收获和踩的坑. 我的实验环境是 windows 10,所以对lab的code 做了一些环境上的修改,如果你仅仅对code 感兴 ...
- MIT 6.824 Lab2A Raft之领导者选举
实验准备 实验代码:git://g.csail.mit.edu/6.824-golabs-2021/src/raft 如何测试:go test -run 2A -race 相关论文:Raft Exte ...
- MIT 6.824 : Spring 2015 lab3 训练笔记
摘要: 源代码参见我的github:https://github.com/YaoZengzeng/MIT-6.824 Lab3: Paxos-based Key/Value Service Intro ...
- MIT 6.824 : Spring 2015 lab2 训练笔记
源代码参见我的github:https://github.com/YaoZengzeng/MIT-6.824 Lab 2:Primary/Backup Key/Value Service Overvi ...
- MIT 6.824学习笔记4 Lab1
现在我们准备做第一个作业Lab1啦 wjk大神也在做6.824,可以参考大神的笔记https://github.com/zzzyyyxxxmmm/MIT6824_Distribute_System P ...
- MIT 6.824 : Spring 2015 lab1 训练笔记
源代码参见我的github: https://github.com/YaoZengzeng/MIT-6.824 Part I: Word count MapReduce操作实际上就是将一个输入文件拆分 ...
随机推荐
- vmware workstation 17 pro激活密钥
vmware workstation 17 pro激活密钥,通用批量永久激活许可 17:JU090-6039P-08409-8J0QH-2YR7F 16:ZF3R0-FHED2-M80TY-8QYGC ...
- Hadoop - [01] 概述
Hadoop官网:https://hadoop.apache.org/ Hadoop下载:https://archive.apache.org/dist/hadoop/common/ 一.Hadoop ...
- implicit和explicit求解器的一点比较
implicit procedure和explicit procedure的比较 abaqus有两个求解器:standard和 explicit求解器.两个求解器在很多方面都有所差异:单元类型/材料行 ...
- Dify 的核心技术栈
Dify 的技术栈涵盖多个层次,结合了前沿的 AI 框架.成熟的开发工具及高效的部署方案. 以下是其核心组成: 一.基础架构与后端技术 编程语言与框架 Python + Flask:后端服务主要基于 ...
- 一文速通Python并行计算:02 Python多线程编程-threading模块、线程的创建和查询与守护线程
一文速通 Python 并行计算:02 Python 多线程编程-threading 模块.线程的创建和查询与守护线程 摘要: 本文介绍了 Python threading 模块的核心功能,包括线程创 ...
- 关于项目中 "不能创建大小为 8190 的行,该大小大于所允许的最大行大小 8060 "的处理
由于产品底层设计的情况,sqlserver 列设置了 可以随用户 创建自动生成 项目特殊,设置的列过多,有三四百列(通常不会过多) 数据无法正常保存,报错"不能创建大小为 8190 的行, ...
- 【QT】解决生成的exe文件出现“无法定位程序入口”或“找不到xxx.dll”的问题
[QT]解决生成的exe文件出现"无法定位程序入口"或"找不到xxx.dll"的问题 零.问题 使用QT编译好项目后,想直接在文件资源管理器中运行exe程序或想 ...
- .NET多线程编程之CountdownEvent使用
简单来说,使用这个类可以让主线程等待子线程都完成任务之后才执行任务 1 static void Main(string[] args) 2 { 3 ///子任务的数量 4 CountdownEvent ...
- SQL语句(一)—— DDL
SQL 全称 Structured Query Language,结构化查询语言.操作关系型数据库的编程语言,定义了一套操作关系型数据库统一标准 . 一.SQL 基础知识 (一)SQL 通用语法 在学 ...
- MySQL获取周、月、天日期,生成排序号
常用MySQL生成时间序列 --生成最近七天的日期,不包括当天 SELECT @cdate := date_add(@cdate, interval - 1 day) as date FROM(SEL ...