Etcd Raft架構設計和源碼剖析2:數據流 | Go語言充電站
前言
之前看到一幅描述etcd raft的流程圖,感覺非常直觀,但和自己看源碼的又有些不同,所以自己模仿著畫了一下,再介紹一下。
下圖從左到右依次分為4個部分:
- raft:raft主體功能部分
- Node:raft提供的接口,raft跟上層的通信接口,會運行一個run函數,持續循環處理通道上的數據
- raftNode:上層應用邏輯
- 其他:Client、Network、State
圖中的箭頭為數據的流向,這幅圖包含了多個流程,接下來會分成4個流程介紹:
- 客戶端請求
- 發送消息給其他節點
- 接收其他節點消息及處理
- 應用達成一致的日志
客戶端請求
客戶端請求的流程,在下圖已經使用紅色箭頭標出,流程如下:
- 客戶端將請求發送給應用層raftNode
- raftNode使用Propose方法,請求寫入到propc通道
- raft.Step接收到通道數據,會通過append等函數加入到raftLog
- raftLog用來暫時存儲和查詢日志,請求會先加入到unstable
發送消息
發送消息的數據流,已經用紅色箭頭標出,流程如下:
- raft發現有數據發送給其他節點,數據可以是leader要發送給follower的日志、snapshot,或者其他類型的消息,比如follower給leader的響應消息
- 利用NewReady創建結構體Ready,并寫入到readyc通道
- raftNode從通道讀到Ready,取出其中的消息,交給Network發送給其他節點
接收消息
接收消息的數據流,已經在下圖用紅色箭頭標出,流程如下:
- 從Network收到消息,可以是leader給follower的消息,也可以是follower發給leader的響應消息,Network的handler函數將數據回傳給raftNode
- raftNode調用Step函數,將數據發給raft,數據被寫入recvc通道
- raft的Step從recvc收到消息,并修改raftLog中的日志
應用日志
raft會將達成一致的log通知給raftNode,讓它應用到上層的數據庫,數據流已經在下圖用紅色箭頭標出,流程如下:
- raft發現有日志需要交給raftNode,調用NewReady創建Ready,從raftLog讀取日志,并存到Ready結構體
- Ready結構體寫入到readyc通道
- raftNode讀到Ready結構體,發現Ready結構體中包含日志
- raftNode會把日志寫入到storage和WAL,把需要應用的日志,提交給狀態機或數據庫,去修改數據
- raftNode處理完Ready后,調用Advance函數,通過advancec發送一個信號給raft,告知raft傳出來的Ready已經處理完畢
可以發現有2個storage,1個是raftLog.Storage,一個是raftNode.storage,Storage是一個接口,可以用來讀取storage中的數據,但不寫入,storage的數據寫入是由raftNode完成的,但raftNode.storage就是raft.MemoryStorage,所以不穩定的、穩定的都由raft存儲,持久化存儲由WAL負責,etcd中有現成實現的WAL操作可用,用來存儲歷史Entry、快照。
Storage接口更多信息請看Storage接口介紹。