MIT 6.824 Lab2C 2D Raft

MIT 6.824 Lab2C 2D Raft

一、前期准备

相较于前两个实验,后面两个实验主要是按照实验要求来进行。

同时还要多多注意config和test两个文件

实验地址introduction部分有一些资料可以学习一下

课堂翻译强烈推荐(里面还有相应论文),虽然可能没有上课听老师讲那么好,但是对于英语渣渣的我还是非常有用的。

这篇博客也详细介绍了raft算法

代码主要分为测试部分和编写部分,可以先去大致看一下config和test文件中的内容,毕竟程序准确性依赖测试文件,同时也能知道实验各个部分的运行过程。

论文中已经把算法基本上都已经实现好了,先把需求和代码看清楚再下手(课上老师好像也这么说了)

在这里插入图片描述

二、结构体
type Persister struct {
	mu        sync.Mutex
	raftstate []byte
	snapshot  []byte
}


type InstallSnapshotArgs struct {
	Term     int
	LeaderId int
	//快照中最后的日志条目对应的索引
	LastIncludedIndex int
	//快照中最后的日志条目对应的任期
	LastIncludedTerm int
	//快照原始数据
	Data []byte
}

type InstallSnapshotReply struct {
	//用于leader更新自己
	Term int
}
三、实验2C

该实验需要实现的就两个函数,在课上也说明了需要保存的信息以及为什么要保存该信息,7.4 持久化(Persistence) - MIT6.824 (gitbook.io)。在make中我们执行read,在make函数中他直接为我们提供了Persist的数据,所以可以直接使用(config文件中,具体实现应该留到实验3了),之后在每个修改了log currentterm votefor数据后我们都执行persist()

func (rf *Raft) readPersist(data []byte) {
	if data == nil || len(data) < 1 { // bootstrap without any state?
		return
	}
	r := bytes.NewBuffer(data)
	d := gob.NewDecoder(r)
	d.Decode(&rf.currentTerm)
	d.Decode(&rf.votedFor)
	d.Decode(&rf.log)
}


func (rf *Raft) persist() {
	// Your code here.
	// Example:
	w := new(bytes.Buffer)
	e := gob.NewEncoder(w)
	e.Encode(rf.currentTerm)
	e.Encode(rf.votedFor)
	e.Encode(rf.log)
	data := w.Bytes()
	rf.persister.Save(data,nil)
}
四、实验2D

根据实验要求,我们需要完成一个Snapshot(index int,data []byte)函数,以及同步其他节点的快照。

4.1 Snapshot(index int,data []byte)

该函数不需要我们调用,在config中,有一个实验在接收十条command后会直接运行这个函数。(对应之前把锁分开),根据题意我们只需要截断log,再修改一些数据即可。

func (rf *Raft) Snapshot(index int, snapshot []byte) {
	rf.mu.Lock()
	defer rf.mu.Unlock()
	baseIndex := rf.log[0].LogIndex
	lastIndex := rf.getLastIndex()
	if index <= baseIndex || index > lastIndex {
		return
	}

	var newLogEntries []LogEntry // log[0]是哨兵
	newLogEntries = append(newLogEntries, LogEntry{LogIndex: index, LogTerm: rf.log[index-baseIndex].LogTerm})

	for i := index + 1; i <= lastIndex; i++ {
		newLogEntries = append(newLogEntries, rf.log[i-baseIndex])
	}

	rf.log = newLogEntries
	rf.persist()
	rf.persister.Save(nil, snapshot)
}

4.2 leader发送函数

func (rf *Raft) sendInstallSnapshot(server int, args InstallSnapshotArgs, reply *InstallSnapshotReply) bool {
	ok := rf.peers[server].Call("Raft.InstallSnapshot", args, reply)
	if ok {
		if reply.Term > rf.currentTerm {
			rf.currentTerm = reply.Term
			rf.state = FLLOWER
			rf.votedFor = -1
			return ok
		}

		rf.nextIndex[server] = args.LastIncludedIndex + 1
		rf.matchIndex[server] = args.LastIncludedIndex
	}
	return ok
}

4.3 server响应函数

func (rf *Raft) InstallSnapshot(args InstallSnapshotArgs, reply *InstallSnapshotReply) {
	rf.mu.Lock()
	defer rf.mu.Unlock()
	//当前任期比该leader节点大 说明已经不是leader
	
	//收到心跳,重置定时器
	rf.chanHeartbeat <- true
	
	//持久化保存leader发送的快照
	rf.persister.Save(nil, args.Data)

	//根据args中的信息截断当前log

	msg := ApplyMsg{SnapshotValid: true, Snapshot: args.Data, SnapshotIndex: args.LastIncludedIndex, SnapshotTerm: args.LastIncludedTerm}

	// 更新raft中的一些数据

	rf.persist()

	rf.chanApply <- msg
}

4.4 broad函数

该函数和前面函数的功能一样,负责将快照发送给其他节点,我们将它和实验2B的broad函数写在一起。

func (rf *Raft) broadcastAppendEntries() {
	// 前面一样省略
	for i := range rf.peers {
		if i != rf.me && rf.state == LEADER {
			if rf.nextIndex[i] > baseIndex {  //baseIndex代表上一次快照的最后一个logindex
				//发送日志
			} else {  
				//发送快照
			}
		}
	}

}
五、总结

实验2比实验1难好多,实验2中有好多细节,论文中的内容和课上的内容都要搞清楚在开始动笔写,不然可能会被推到重写(很痛苦)。

调试全程都是用的fmt,前面还想搞远程调试(浪费了好多时间,最后也没搞好),多线程也不好打断点调试。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值