MIT 6.824 Lab2 Raft实现

本文详细介绍了在Go语言中实现Raft分布式一致性算法的过程,包括节点选举、心跳机制、日志追加、持久化等关键部分。通过实验,作者探讨了锁的使用、超时选择和并发程序调试的挑战,强调了Raft相对于Paxos的易理解和实现,但实现过程中依然面临复杂性。
摘要由CSDN通过智能技术生成

1. 综述

在本次实验中,我使用Go语言在给定框架上实现了简单的Raft协议,通过实现包括节点选举、心跳机制、日志追加和持久化等内容,完成了Part1,Part2和Part3。项目托管在github上,地址为NJU-DisSys-2020。当实验DDL过了后我将开源。

2. 相关定义

根据Raft的原始论文以及扩展版论文,我定义了如下常量和数据结构。

2.1 常量定义

2.1.1 状态常量

首先是定义Raft中Sever的三种状态,分别是

  • 领导者(Leader)
  • 跟随者(Follower)
  • 候选者(Candidate)
// Raft中Server的三种状态
const (
	LEADER = iota
	FOLLOWER
	CANDIDATE
)
2.1.2 时间常量

根据PPT,论文以及课程实验来源MIT6.824中lab2的要求,指定如下的时钟相关常量。

  • 选举超时间隔 100-500ms
  • 心跳间隔100ms
// 时钟相关的
const (
	ElectionTimeoutMin = 100
	ElectionTimeoutMax = 500
	HeartBeatsInterval = 100
)
2.1.3 日志常量

最后,额外定义一个特殊值Backoff用于日志的追加发生异常时的处理

// AppendLog失败后是否需要回退一个Term
const (
	BackOff = -100
)

2.2 数据结构定义

2.2.1 日志定义

首先,根据论文要求,定义如下的LogEntry表示在Raft中各个节点中的log entry

type LogEntry struct {
   
	Command interface{
   } // 状态机的命令
	Term    int         // log entry的term
	Index   int         // log entry的index
}

为了方便使用,我们额外记录了一个Index域,而不是使用数组的下标来表示Index。但是在最后实际系统实现中,由于往log数组中加入了一个空log,使得log数组的下标与Index值相等应该是一个需要被满足的循环不变量。那么可以用这个性质来Debug。

2.2.2 Raft结构体定义

根据实验需要,我定义了如下Raft的结构体。除了框架代码给出了的内容,Raft结构体中主要还有如下内容:

所有服务器中的持久化状态

这部分内容是在Part3中将被持久化的状态,即使节点Crash后,也应该从持久化的设置中恢复。

// Persistent state on all servers
currentTerm int        // server已知的最新term,初始化为0,单调递增
votedFor    int        // 当前term中所投票的id,如果没有投票,则为null
log         []LogEntry // 实际日志的下标从1开始

所有服务器中的不稳定状态

// Volatile state on all servers
commitIndex int // committed的最大的log entry index,初始化为0,单调递增
lastApplied int // 应用到状态机的最大的log entry index,初始化为0,单调递增

Leader中的不稳定状态

nextIndex  []int // 对每个server下一个要发送的log entry的序号,初始化为 leader last log index+1 
matchIndex []int // To replicated,对每个server,已知的最高的已经复制成功的序号

实验相关的辅助字段

除了论文Figure2所给内容外,为了实现选举,心跳机制和日志追加等内容,我还给Raft结构体增加了如下字段。

其中,通过Go语言中的time.Timer提供的计时超时功能,实现选举以及心跳(日志追加)。

// Self defined
role           int         // 服务器状态
leaderID       int         // Follower的Leader
electionTimer  *time.Timer // Leader Election的定时器
heartBeatTimer *time.Timer // Heart Beat的定时器
applyCh        chan ApplyMsg // 日志Apply的通道
2.2.3 Request Vote 相关定义

这个主要根据论文内容来,没有什么特别。需要注意的一点就是首字母得大写,否则会报错。

type RequestVoteArgs struct {
   
	Term         int // 候选人的term
	CandidatedId int // 候选人的ID
	LastLogIndex int // 候选人日志中最后一条的序号
	LastLogTerm  int // 候选人日志中最后一条的term
}

type RequestVoteReply struct {
   
	Term        int  // 当前的term,用于使候选人更新状态
	VoteGranted bool // 若为真,则表示候选人接受了投票
}
2.2.4 Append Entries 相关定义

这个也主要根据论文内容来,但是为了实现的方便,在AppendEntriesReply中增加了一个NextIndex的字段,直接标识若向某节点日志追加失败后,该节点的nextIndex应该被置为何值。

type AppendEntriesArgs struct {
   
	Term         int        // 领导者的term
	LeaderId     int        // 领导者的ID,
	PrevLogIndex int        // 在append新log entry前的log index
	PrevLogTerm  int        // 在append新log entry前的log index下的term
	Entries      []LogEntry // 要append log entries
	LeaderCommit int        // 领导者的commitIndex

}

type AppendEntriesReply struct {
   
	Term      int  //
	Success   bool 
  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值