RaftStore、Region、Peer的关系如下。
Peer封装了RawNode和PeerStorage(与2A中的MemoryStorage一样,同为raft.Storage接口的实现)。
type peer struct {
RaftGroup *raft.RawNode
peerStorage *PeerStorage
}
复制代码
2B从Raftstore.start()开始,它启动了一些raftWorker,主要做两件事情。
- 从Raft获取并处理Ready。
- 从raftCh获取并处理Msg。
获取并处理Ready
不同于MemoryStorage是在内存中持久化,PeerStorage使用badger做持久化。
type PeerStorage struct {
Engines *engine_util.Engines
}
复制代码
Engines里面有两个badger实例,各自存储了一些信息。不同类型的信息使用特殊的前后缀用作区分。
- raftDB
- raft log
- key: region_id + log_idx
- value: entry
- raft state
- key: region_id
- value: RaftLocalState
- raft log
- kvDB
- data
- apply state
- key: region_id
- value: RaftApplyState
- region state
- key: region_id
- value: RegionLocalState
我们需要实现的是两个函数。
PeerStorage.SaveReadyState(Ready) error
- 使用Append保存Ready.Entries。
- 更新PeerStorage.RaftLocalState.HardState并保存到raftDB。
PeerStorage.Append([]pb.Entry, *engine_util.WriteBatch) error
- 将Entires保存到raftDB,并删除现在Storage中在本轮Append最后一个Entry之后的所有Entries,同时更新PeerStorage.RaftLocalState并保存到raftDB。
- 删除这里补充一下,我在2A的文章提过,stabled是会变化的,因此是可能出现之前Append的Entry在最新一次Append之后的情况。
SaveReadyState
Append
获取并处理Msg
我们需要实现的是两个函数。
peerMsgHandler.HandleRaftReady()
peerMsgHandler.proposeRaftCommand()
peerMsgHandler对外暴露两个函数。
-
HandleMsgs
- HandleMsgs处理一些消息,比如说MsgTypeTick驱动Raft,调用RawNode.Tick();再比如收到MsgTypeRaftCmd向Raft追加一条日志,调用proposeRaftCommand()。
-
HandleRaftReady
- HandleMsgs后,Raft状态可能会有变化,HandleRaftReady察觉变化的状态,并进行相应的动作,最后调用RawNode.Advance()。
proposeRaftCommand
HandleRaftReady
总结
其实2B应该和2AC放到一起,关联性比较大,关于Storage、Peer、Node、Store等等类似的概念比较多,嵌套的层级也很深,但其实把raftWorker.run()当做入口开始看就好。
2B是不涉及Snapshot的,但测试程序要从Response中拿到Snap,再从Snap中拿到Region,所以你还是需要在apply中考虑CmdType_Snap的情况。