MIT6.824(lab2C-日志持久化)

这篇博客详细介绍了 Raft 分布式一致性算法中关于日志持久化、心跳应答冲突处理及日志同步的优化。优化主要包括在 follower 回复中增加 ConflictIndex 和 ConflictTerm 字段,以及在 leader 处理心跳应答时如何更智能地回退 nextIndex。当 follower 的 log 缺失或 term 不匹配时,leader 能更精确地确定同步起始点,减少无效同步,提高系统效率。

枯木逢春不在茂
年少且惜镜边人

写在前面

lab2B已经完成的raft的大部分内容,2C主要是对currentterm,[]log,voteFor 进行持久化存储,后边3B也会修改这一部分的代码,还有就是2B中提到的心跳被拒后回退机制太慢,需要优化。

具体实现

首先是持久化函数,lab中给出的代码可以直接套用

// save Raft's persistent state to stable storage,
// where it can later be retrieved after a crash and restart.
// see paper's Figure 2 for a description of what should be persistent.
//
func (rf *Raft) persist() {
   
   
	// Your code here (2C).
	// 不用加锁,外层逻辑会锁
	w := new(bytes.Buffer)
	e := labgob.NewEncoder(w)
	e.Encode(rf.currentTerm)
	e.Encode(rf.votedFor)
	e.Encode(rf.log)
	data := w.Bytes()
	DPrintf("RaftNode[%d] persist starts, currentTerm[%d] voteFor[%d] log[%v]", rf.me, rf.currentTerm, rf.votedFor, rf.log)
	rf.persister.SaveRaftState(data)
}
 
//
// restore previously persisted state.
//
func (rf *Raft) readPersist(data []byte) {
   
   
	if data == nil || len(data) < 1 {
   
    // bootstrap without any state?
		return
	}
	// Your code here (2C).
	r := bytes.NewBuffer(data)
	d := labgob.NewDecoder(r)
	rf.mu.Lock()
	defer rf.mu.Unlock()
	d.Decode(&rf.currentTerm)
	d.Decode(&rf.votedFor)
	d.Decode(&rf.log)
}

type AppendEntriesArgs struct {
   
   
	Term         int
	LeaderId     int
	PrevLogIndex int
	PrevLogTerm  int
	Entries      []LogEntry
	LeaderCommit int
}
 
type AppendEntriesReply struct {
   
   
	Term    int
	Success bool
	ConflictIndex int
	ConflictTerm int
}

也就是增加了ConflictIndex和ConflictTerm这两个字段

func (rf *Raft) AppendEntries(args *AppendEntriesArgs, reply *AppendEntriesReply) {
   
   
	rf.mu.Lock()
	defer rf.mu.Unlock()
 
	DPrintf("RaftNode[%d] Handle AppendEntries, LeaderId[%d] Term[%d] CurrentTerm[%d] role=[%s] logIndex[%d] prevLogIndex[%d] prevLogTerm[%d] commitIndex[%d] Entries[%v]",
		rf.me, rf.leaderId, args.Term, rf.currentTerm, rf.role, len(rf.log), args.PrevLogIndex, args.PrevLogTerm, rf.commitIndex, args.Entries)
 
	reply.Term = rf.currentTerm
	reply.Success = false
	reply.ConflictIndex = -1
	reply.ConflictTerm = - 1
 
	defer func() {
   
   
		DPrintf("RaftNode[%d] Return AppendEntries, LeaderId[%d] Term[
### MIT 6.824 Lab2 实验指南和参考资料 #### 分布式系统Raft实验室概述 MIT 6.824课程中的Lab2专注于实现Raft一致性算法,该算法用于管理复制日志。通过此实验,学生可以深入了解如何构建可靠的分布式存储系统[^1]。 #### 领导者选举与心跳机制 在Raft协议中,服务器节点之间会定期发送心跳消息来维持领导者的地位。如果跟随者一段时间内未接收到领导者的心跳,则转换状态成为候选人并发起新的选举过程。这一过程中涉及到了`sendAllRequestVote`函数的调用来请求其他节点投票支持自己作为潜在的新领导者[^3]。 #### 日志追加流程 当一个客户端提交命令给集群时,当前任期的领导者负责接收这些指令并将它们记录到自己的日志里;之后再向其余成员广播这条新增的日志项以便同步副本数据。这里涉及到几个重要变量如`commitIndex`表示已知安全可应用的最大索引位置以及`lastApplied`标记实际已经应用于状态机上的最高序号。 #### 处理日志复制信息 为了确保所有机器上保存着相同版本的历史操作序列,在每次成功写入本地磁盘后都需要通知其它参与者更新其各自的状态。这通常是在`AppendEntriesHandler`方法内部完成的工作之一——它不仅承担了转发待确认的日志片段的任务,还肩负起维护诸如`nextIndex`(下一个要发送给某特定peer的日志条目的下标)、`matchIndex`(对于某个peer而言最后一条匹配成功的log entry index)等元数据的责任。 ```go func (rf *Raft) AppendEntries(args *AppendEntriesArgs, reply *AppendEntriesReply) { rf.mu.Lock() defer rf.mu.Unlock() // ...省略无关逻辑... for i := args.PrevLogIndex + 1; i < len(rf.log); i++ { if !entriesMatch(&rf.log[i], &args.Entries[i-args.PrevLogIndex]) { // 检查现有日志是否冲突 rf.log = rf.log[:i] break } } // 添加新日志条目... } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值