面试官:聊聊 etcd 中的 Raft 吧( 四 )
文章插图
Progress State Machine
raft.go前面铺设了一大堆概念 , 现在终于轮到实现逻辑了 。 从名字也可以看出 , raft 协议的具体实现就在这个文件里 。 这其中 , 大部分的逻辑是由Step函数驱动的 。
// #L752func (r *raft) Step(m pb.Message) error {//...switch m.Type {case pb.MsgHup://...case pb.MsgVote, pb.MsgPreVote://...default:r.step(r, m)}}
Step的主要作用是处理不同的消息[17] , 所以以后当我们想知道 raft 对某种消息的处理逻辑时 , 到这里找就对了 。 在函数的最后 , 有个default语句 , 即所有上面不能处理的消息都落入这里 , 由一个小写的step函数处理 , 这个设计的原因是什么呢?其实是因为这里的 raft 也被实现为一个状态机 , 它的step属性是一个函数指针 , 根据当前节点的不同角色 , 指向不同的消息处理函数:stepLeader[18]/stepFollower[19]/stepCandidate[20] 。 与它类似的还有一个tick函数指针 , 根据角色的不同 , 也会在tickHeartbeat[21]和tickElection[22]之间来回切换 , 分别用来触发定时心跳和选举检测 。 这里的函数指针感觉像实现了OOP里的多态 。
文章插图
Raft State Machine
node.gonode的主要作用是应用层(etcdserver)和共识模块(raft)的衔接 。 将应用层的消息传递给底层共识模块 , 并将底层共识模块共识后的结果反馈给应用层 。 所以它的初始化函数[23]创建了很多用来通信的channel , 然后就在另一个goroutine里面开始了事件循环 , 不停的在各种channel中倒腾数据(貌似这种由for-select-channel组成的事件循环在 Go 里面很受欢迎) 。
// #L286for {select {case m := <-propc:r.Step(m)case m := <-n.recvc:r.Step(m)case cc := <-n.confc:// Add/remove/update node according to cc.Typecase <-n.tickc:r.tick()case readyc <- rd:// Cleaning after result is consumed by applicationcase <-advancec:// Stablize logscase c := <-n.status:// Update statuscase <-n.stop:close(n.done)return}}
propc和recvc中拿到的是从上层应用传进来的消息 , 这个消息会被交给 raft 层的Step函数处理 , 具体处理逻辑我上面有过介绍 。下面来解释下readyc的作用 。 在 etcd 的这个实现中 , node并不负责数据的持久化、网络消息的通信、以及将已经提交的 log 应用到状态机中 , 所以node使用readyc这个channel对外通知有数据要处理了 , 并将这些需要外部处理的数据打包到一个Ready结构体中:
// #L52// Ready encapsulates the entries and messages that are ready to read,// be saved to stable storage, committed or sent to other peers.// All fields in Ready are read-only.type Ready struct {// The current volatile state of a Node.// SoftState will be nil if there is no update.// It is not required to consume or store SoftState.*SoftState// The current state of a Node to be saved to stable storage BEFORE// Messages are sent.// HardState will be equal to empty state if there is no update.pb.HardState// ReadStates can be used for node to serve linearizable read requests locally// when its applied index is greater than the index in ReadState.// Note that the readState will be returned when raft receives msgReadIndex.// The returned is only valid for the request that requested to read.ReadStates []ReadState// Entries specifies entries to be saved to stable storage BEFORE// Messages are sent.Entries []pb.Entry// Snapshot specifies the snapshot to be saved to stable storage.Snapshot pb.Snapshot// CommittedEntries specifies entries to be committed to a// store/state-machine. These have previously been committed to stable// store.CommittedEntries []pb.Entry// Messages specifies outbound messages to be sent AFTER Entries are// committed to stable storage.// If it contains a MsgSnap message, the application MUST report back to raft// when the snapshot has been received or has failed by calling ReportSnapshot.Messages []pb.Message// MustSync indicates whether the HardState and Entries must be synchronously// written to disk or if an asynchronous write is permissible.MustSync bool}
推荐阅读
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 聊聊网易云音乐:“心动模式”
- 华为突然宣布!腾讯也没有想到,一切来得如此快
- 全球最受欢迎的4部5G手机:靠量取胜的小米竟榜上无名?
- Java学习:Java学习到什么程度可以进行面试
- 程序员面试金典17.05_go_字母与数字
- 安卓春招面经:二本渣院面试网易被拒,最终获腾讯阿里offer
- 中国最赚钱的公司诞生!不是移动,也不是阿里巴巴,那第一是?
- 「6」进大厂必须掌握的面试题-Hibernate
- 高通骁龙888提前来了!华为“慌”了?但一切还没有定论
- 震惊!京东T4大佬面试整整三个月,才写了两份java面试笔记