本人现在在美本CS,由于疫情在家上网课,研究一下分布式。目前youtube看视频、读论文、做lab。顺便每天晚上更新今天新增的知识,欢迎交流。
网上mit6.824的教程已经很多了,大多数是把一个正确代码分段加注释,弄清楚一段代码的作用和原理。这样固然没错,但是任何一个算法或者协议,都有不同场景下的实现,更加重要的是把握算法背后的思想。
这个目前跟着mit的lab在做。前段时间产生了一个误区,认为Raft里的log就是用户的请求。实际不是,raft内部的log只是维护一致性和选举所交换的信息
个人感觉和经验,写完这个算法,重点是要理解go语言的特性,以及项目想传递的思想,test过没过,并不重要,那我来记录一下思想方面的问题。
1. paper方面一定要读懂的,非常精华,特别是section2的figure
2. 类似于CS537的scheduler。如果要我说一句话总结raft,那就是:raft是一群节点、三个状态,一直循环各司其职。这就是raft的核心。工业界并不关心具体的log,重点是如何election以及timeout (下图我们在后文详细讲解)
func (rf *Raft) runServer() {
for {
switch rf.status {
case Leader:
AppendEntry()
case Follower:
// 1. Check HeartBeat
// 2. Check if itself has voted
// 3. After timeout, become a Condidate
case Candidate:
// 1. Ask other people to vote
}
}
}
func (rf *Raft) sendAppendEntries(server int, args *AppendEntriesArgs, reply *AppendEntriesReply) bool {
ok := rf.peers[server].Call("Raft.AppendEntries", args, reply)
}
先描述,后贴代码。
leader只做一件事:协调followers
接受client给的log,不停的给followers发pkt,会根据RPC reply的内容更改自己的策略。因此leader的RPC其实是一个双向交互。
有两个点我觉得设计的很巧妙
// Volatile state on leaders:
nextIndex [] int
matchIndex [] int
由于log可能会产生许多的错误,比如少了,多了,冲突了。那么我们记录对每一个peer一个nextIndex,这样子可以把nextIndex之后的log一次性覆盖成leader的样子。
有一个小Trick:当我们发现冲突的时候,直接认为当前term的log我都不要了,以免一个个log都用prevIndex & prevTerm check,从而reduce the # of RPC
Candidate做了三件事
1. 请求别人给自己投票(默认FIFO),但是根据业务可以有多种变型。这就是我开头为什么说记代码没用。lecture中提到了:选取log最多的优先,选取term最大的优先,etc.
2. 发现有人成为了leader(即收到了heartbeat via channel),放弃竞选成为follower
3. 选举成功(通过elecwin),转变为follower
没错follower在这里的主要功能就是更新自己的log以及投票,就是在解决上述的两种RPC。
如果一段时间没有heartbeat或者也没有给别人投票,那就自己变成candidate
感觉是替代zookeeper的轻量级算法?并没有完全搞懂zookeeper
1. 一个优化就是使读操作可以不必须经过leader,从而实现了:“增加服务器同时也是增加performance”的目的
2. 其余的先留个坑
1. https://www.jianshu.com/p/66e2a0cd93d5
2. 微服务
3. zookeeper论文还是读一下
4. lab继续做