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可能会在三种状态间互相转换。具体转换规则如下图:

  • 所有服务器在任何情况都要遵守的规则如下

  1. (ApplyLoop)如果commitIndex大于lastApplied,就说明有一部分Leader已经认可的指令还没有apply,应用至状态机
  2. 任何时候,如果任何一个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的更多相关文章

  1. MIT 6.824 Llab2B Raft之日志复制

    书接上文Raft Part A | MIT 6.824 Lab2A Leader Election. 实验准备 实验代码:git://g.csail.mit.edu/6.824-golabs-2021 ...

  2. MIT 6.824 Lab2D Raft之日志压缩

    书接上文Raft Part C | MIT 6.824 Lab2C Persistence. 实验准备 实验代码:git://g.csail.mit.edu/6.824-golabs-2021/src ...

  3. MIT 6.824 Lab2C Raft之持久化

    书接上文Raft Part B | MIT 6.824 Lab2B Log Replication. 实验准备 实验代码:git://g.csail.mit.edu/6.824-golabs-2021 ...

  4. MIT 6.824(Spring 2020) Lab1: MapReduce 文档翻译

    首发于公众号:努力学习的阿新 前言 大家好,这里是阿新. MIT 6.824 是麻省理工大学开设的一门关于分布式系统的明星课程,共包含四个配套实验,实验的含金量很高,十分适合作为校招生的项目经历,在文 ...

  5. MIT 6.824 lab1:mapreduce

    这是 MIT 6.824 课程 lab1 的学习总结,记录我在学习过程中的收获和踩的坑. 我的实验环境是 windows 10,所以对lab的code 做了一些环境上的修改,如果你仅仅对code 感兴 ...

  6. MIT 6.824 Lab2A Raft之领导者选举

    实验准备 实验代码:git://g.csail.mit.edu/6.824-golabs-2021/src/raft 如何测试:go test -run 2A -race 相关论文:Raft Exte ...

  7. MIT 6.824 : Spring 2015 lab3 训练笔记

    摘要: 源代码参见我的github:https://github.com/YaoZengzeng/MIT-6.824 Lab3: Paxos-based Key/Value Service Intro ...

  8. MIT 6.824 : Spring 2015 lab2 训练笔记

    源代码参见我的github:https://github.com/YaoZengzeng/MIT-6.824 Lab 2:Primary/Backup Key/Value Service Overvi ...

  9. MIT 6.824学习笔记4 Lab1

    现在我们准备做第一个作业Lab1啦 wjk大神也在做6.824,可以参考大神的笔记https://github.com/zzzyyyxxxmmm/MIT6824_Distribute_System P ...

  10. MIT 6.824 : Spring 2015 lab1 训练笔记

    源代码参见我的github: https://github.com/YaoZengzeng/MIT-6.824 Part I: Word count MapReduce操作实际上就是将一个输入文件拆分 ...

随机推荐

  1. Feedalyze - 让你听得见、听得清用户的反馈

    满足用户需求,解决用户问题,获得适当报酬是商业成功最为重要的因素.然而扪心自问,当您推出新产品后,您真的在听.听得见.听得清用户的反馈么? 当今信息传播迅猛,渠道繁多,优秀产品随口碑效应供不应求,劣质 ...

  2. 厦门大学3篇DeepSeek报告pdf汇总(附下载地址)

    最近,厦门大学大数据教学团队发布了3份DeepSeek大模相关报告.其中<DeepSeek大模型企业应用实践,全景解读与技术演进>这份报告足足有150页,把国产大模型在企业里怎么用.技术上 ...

  3. 移动端 cordova vue videojs 全屏播放后退出全屏返回后退出app问题

    问题描述 移动端上面使用了videojs 播放视频,同时也监听了手机返回事件document.addEventListener('backbutton',.接着我们点击全屏播放后在退出全屏在返回后直接 ...

  4. 本地如何访问vue2 生成的dist代码

    前言 当你使用 Vue CLI 或其他构建工具构建 Vue 2 项目时,它会生成一个 dist 文件夹,这个文件夹包含了你项目的生产环境版本的静态资源文件(HTML.JavaScript 和 CSS) ...

  5. 解决排查 mongodb cpu使用率过高

    前言 通过 top 命令,可以看到 MongoDB 的 CPU 使用率过高,CPU 过高会导致数据读写.处理异常缓慢,还会出现被系统抹杀进程的风险,这个问题 99.9999% 的可能性是用户使用上不合 ...

  6. nginx: [error] open() "/usr/local/nginx/nginx.pid" failed (2: No such file or directory)

    nginx 启动出现错误 nginx: [error] open() "/usr/local/nginx/nginx.pid" failed (2: No such file or ...

  7. 如何写自己的springboot starter?自动装配原理是什么?

    如何写自己的springboot starter?自动装配原理是什么? 官方文档地址:https://docs.spring.io/spring-boot/docs/2.6.13/reference/ ...

  8. PVE常用命令

    1.查看集群下的节点信息 root@pve63-node172:~# pvecm nodes Membership information ---------------------- Nodeid ...

  9. Git提交历史优化指南:两步合并本地Commit,代码审查更高效!

    在开发过程中,频繁的本地Commit可能导致提交历史冗杂,增加代码审查和维护的复杂度.通过合并连续的Commit,不仅能简化历史记录,还能提升代码可读性和团队协作效率,以下是合并两次本地Commit的 ...

  10. BUUCTF---rot

    题目 破解下面的密文: 83 89 78 84 45 86 96 45 115 121 110 116 136 132 132 132 108 128 117 118 134 110 123 111 ...