mit6.824-lab2b日志一致性

主要内容:client调用start()添加日志后,raft如何保证日志如何被安全、可靠、快速的添加到raft集群中。

Start函数

客户端调用Start函数后应立即返回,无论raft添加结果如何。如果当前节点是leader的话,则更新自己的日志。

//客户端发送command消息
func (rf *Raft) Start(command interface{
    
    }) (int, int, bool) 
...
//leader: 更新日志
		log := Log{
    
    term, command, index}
		rf.entries = append(rf.entries, log)
		rf.matchIndex[rf.me] = len(rf.entries) - 1
		rf.cond.Signal() //通知发送更改包(心跳)

AppendEntries函数

leader通过调用AppendEntries rpc来更新follower的日志。

leader由nextIndex[i]可以知道要发送给该服务器的下一条日志索引,并将前一条日志的索引(prevLogIndex)、任期(prevLogTerm)、Entries[nextIndex[i]:]作为参数发送给follower,follower凭借上列参数更新自己的日志。
接收者(follower)具体流程

  1. 检测leader合法性
  2. 重置选举计时器
  3. 检测日志中是否包含与leader一致的Entries[prevLogIndex]
  4. 根据参数Entries更新自己的日志。
  5. 更新commitIndex

过程很简单,可是raft论文中有很多隐晦的地方没有讲清楚,算是有不少坑在这里。

快重传:在前三步的检测失败时,告知leader失败的原因,并且告知leader下一次发送的nextIndex[i]。
在reply中加入三个变量:Xterm(冲突的任期)、Xlen(日志空位)、Xindex(下次发送的index)。

如果follower存在prevLogIndex的日志,并且发生冲突的话,返回follower冲突日志所对应任期的第一条日志Index。
https://mit-public-courses-cn-translatio.gitbook.io/mit6-824/lecture-07-raft2/7.3-hui-fu-jia-su-backup-acceleration

第四步中,需要注意:添加follower中没有的日志,需要处理已经包含、冲突、缺少的问题
如果leader的nextIndex=[2,2,2],follower的日志在更新完后必须为1234。

server 当前日志 接收的日志
leader 1234
follower1 123 [3,4]
follower2 1256 [3,4]
follower3 12 [3,4]

整体架构&goroutine

	go rf.ticker()
	go rf.UpdateOtherLogsLoop()
	go rf.applier()
	go rf.UpdateOtherLogSignal()

ticker:lab2a选举相关
UpdateOtherLogs*:发送心跳包,发送心跳的时候顺带更新日志
applier:周期检查日志是否被提交,如果已经提交,则应用到状态机,<-Applych

发送心跳包用了两个goroutine:Loop和Signal。
Loop为周期性发送心跳,每隔100ms发送一次。
Signal为响应式发送心跳,包阻塞在sync.cond上,每当Client调用Start()时,leader成功添加command到自己的日志,就会调用cond.signal(),立即发送更新日志的请求。
如果只用Loop发送心跳包,运行lab2b:90s+,Loop+Signal:60s+。

修改:在goroutine中,SendAppendEntries更新成功后,立马进行commitIndex更新操作。

lab2b 50s+
在这里插入图片描述

分布式技巧:

当存在以下形式时:

for{
    
     
	go fun(){
    
    
	}()
} 
...go func()执行结果进行操作..

修改成下列形式:

for{
    
     
	go fun(){
    
    
		...
	    ...go func()执行结果进行操作...
	}()
} 

猜你喜欢

转载自blog.csdn.net/weixin_44866921/article/details/129762952
今日推荐