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. Flume - [05] Hbase sink

    一.概述   此接收器将数据写入Hbase.Hbase配置是从类路径中遇到的第一个Hbase-site.xml获取的.由配置指定的实现 HbaseEventSerializer 的类用于将事件转换为 ...

  2. SSM:Spring整合Mybatis时,连接池和SQLSessionFactory的联系!

  3. STM32实战——DHT11温湿度获取并展示

    介绍 DHT11数字温湿度传感器是一款含有已校准数字信号输出的温湿度复合传感器,包括一个电阻式感湿元件和一个NTC测温元件,可以用来测量温度和湿度. 硬件连线 注意 本实验使用STM32F103C8T ...

  4. ERROR: Unexpected bus error encountered in worker. This might be caused by insufficient shared memory (shm).

    报错 ERROR: Unexpected bus error encountered in worker. This might be caused by insufficient shared me ...

  5. Laravel 配置连接多个数据库以及如何使用

    目录 配置连接 配置 .env 文件 配置 \config\database.php 文件 使用 Schema Query Eloquent 配置连接 配置 .env 文件 /* 这部分是默认的数据库 ...

  6. 泛型--java进阶day10

    1.泛型 2.泛型--统一数据类型 如下图,当我们在泛型中添加不同的数据类型,add方法需要的数据类型也随之改变 [1] [2] 泛型--默认类型object 当我们不指定泛型时,泛型的默认类型为ob ...

  7. 干货分享!MCP 实现原理,小白也能看懂

    不知道大家有没有发现?对于添加到 MCP 服务市场的成千上万个 MCP 服务(而且这个数字每天还在增加),我们可以不写一行代码,轻松实现调用,但背后的原因究竟是啥呢? MCP 虽然用起来很方便,但搞不 ...

  8. ShardingSphere 解决关联表查询问题的详细方案

    一.基础概念 在分库分表场景下,关联表(JOIN)查询的复杂性主要源于数据分布在不同的数据库或表中.ShardingSphere 通过 绑定表(Binding Table) 和 广播表(Broadca ...

  9. Debug调试(使用IDEA的断点调试功能,查看程序的运行过程)

    一. 1. 在有效代码行,点击行号右边的空白区域,设置断点,程序执行到断点将停止,我们可以手动来运行程序 2. 点击Debug运行模式 3. 程序停止在断点上不再执行,而IDEA最下方打开了Debug ...

  10. AI浏览器自动化实战

    只需一句话,AI 即可自动操作浏览器: 搜索商品: 下单支付: 甚至还能进行深度研究(Deep Research),自动生成完整的攻略报告: Browser use 是一个开源项目,使 AI 大模型能 ...