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. vmware workstation 17 pro激活密钥

    vmware workstation 17 pro激活密钥,通用批量永久激活许可 17:JU090-6039P-08409-8J0QH-2YR7F 16:ZF3R0-FHED2-M80TY-8QYGC ...

  2. Hadoop - [01] 概述

    Hadoop官网:https://hadoop.apache.org/ Hadoop下载:https://archive.apache.org/dist/hadoop/common/ 一.Hadoop ...

  3. implicit和explicit求解器的一点比较

    implicit procedure和explicit procedure的比较 abaqus有两个求解器:standard和 explicit求解器.两个求解器在很多方面都有所差异:单元类型/材料行 ...

  4. Dify 的核心技术栈

    Dify 的技术栈涵盖多个层次,结合了前沿的 AI 框架.成熟的开发工具及高效的部署方案. 以下是其核心组成: 一.基础架构与后端技术 编程语言与框架 Python + Flask:后端服务主要基于 ...

  5. 一文速通Python并行计算:02 Python多线程编程-threading模块、线程的创建和查询与守护线程

    一文速通 Python 并行计算:02 Python 多线程编程-threading 模块.线程的创建和查询与守护线程 摘要: 本文介绍了 Python threading 模块的核心功能,包括线程创 ...

  6. 关于项目中 "不能创建大小为 8190 的行,该大小大于所允许的最大行大小 8060 "的处理

    由于产品底层设计的情况,sqlserver  列设置了 可以随用户 创建自动生成 项目特殊,设置的列过多,有三四百列(通常不会过多) 数据无法正常保存,报错"不能创建大小为 8190 的行, ...

  7. 【QT】解决生成的exe文件出现“无法定位程序入口”或“找不到xxx.dll”的问题

    [QT]解决生成的exe文件出现"无法定位程序入口"或"找不到xxx.dll"的问题 零.问题 使用QT编译好项目后,想直接在文件资源管理器中运行exe程序或想 ...

  8. .NET多线程编程之CountdownEvent使用

    简单来说,使用这个类可以让主线程等待子线程都完成任务之后才执行任务 1 static void Main(string[] args) 2 { 3 ///子任务的数量 4 CountdownEvent ...

  9. SQL语句(一)—— DDL

    SQL 全称 Structured Query Language,结构化查询语言.操作关系型数据库的编程语言,定义了一套操作关系型数据库统一标准 . 一.SQL 基础知识 (一)SQL 通用语法 在学 ...

  10. MySQL获取周、月、天日期,生成排序号

    常用MySQL生成时间序列 --生成最近七天的日期,不包括当天 SELECT @cdate := date_add(@cdate, interval - 1 day) as date FROM(SEL ...