當前位置:
首頁 > 知識 > etcd raft如何實現成員變更

etcd raft如何實現成員變更

成員變更在一致性協議里稍複雜一些,由於不同的成員不可能在同一時刻從舊成員組切換至新成員組,所以可能出現兩個不相交的majority,從而導致同一個term出現兩個leader,進而導致同一個index的日誌不一致,違反一致性協議。下圖是個例子:

etcd raft如何實現成員變更

raft作者提出了一種比較簡單的方法,一次只增加或減少一個成員,這樣能夠保證任何時刻,都不可能出現兩個不相交的majority,所以,可以從舊成員組直接切到新成員組。如下圖:

etcd raft如何實現成員變更

切換的時機是把成員變更日誌寫盤的時候,不管是否commit。這個切換時機帶來的問題是如果這條成員變更日誌最終沒有commit,在發生leader切換的時候,成員組就需要回滾到舊的成員組。

etcd raft為了實現簡單,將切換成員組的實機選在apply成員變更日誌的時候。

下面看看etcd raft library如何實現的:

應用調用

func (n *node) ProposeConfChange(ctx context.Context, cc pb.ConfChange) error {
data, err := cc.Marshal
if err != nil {
return err
}
return n.Step(ctx, pb.Message{Type: pb.MsgProp, Entries: []pb.Entry{{Type: pb.EntryConfChange, Data: data}}})
}

可以看出,ConfChange是和普通的log entry一樣封裝在MsgProp消息中,進入propc,

跑raft演算法的goroutine從propc中拿到消息後,會做如下判斷:

for i, e := range m.Entries {
if e.Type == pb.EntryConfChange {
if r.pendingConf {
r.logger.Infof("propose conf %s ignored since pending unapplied configuration", e.String)
m.Entries[i] = pb.Entry{Type: pb.EntryNormal}
}
r.pendingConf = true
}
}

檢查已經有成員變更正在做,就忽略新的成員變更。然後將pendingConf置為true,意味著目前有成員變更正在做了,從這裡可以看出,多個成員變更不能同時進行。follower接收端的處理和普通log entry一樣。

如果成員變更日誌達成了一致,則會被封裝在Ready中,應用拿到後,做如下處理:

if entry.Type == raftpb.EntryConfChange {
var cc raftpb.ConfChange
cc.Unmarshal(entry.Data)
s.Node.ApplyConfChange(cc)
}

ApplyConfChange:

func (n *node) ApplyConfChange(cc pb.ConfChange) *pb.ConfState {
var cs pb.ConfState
select {
case n.confc <- cc: case <-n.done: } select { case cs = <-n.confstatec: case <-n.done: } return &cs }

講ConfChange放入confc,然後阻塞在confstatec上,跑raft協議的goroutine從confc中拿出ConfChange,做相應的增加/刪除節點操作,然後將成員組放入confstatec。

switch cc.Type {
case pb.ConfChangeAddNode:
r.addNode(cc.NodeID)
case pb.ConfChangeRemoveNode:
// block incoming proposal when local node is
// removed
if cc.NodeID == r.id {
propc = nil
}
r.removeNode(cc.NodeID)
case pb.ConfChangeUpdateNode:
r.resetPendingConf
default:
panic("unexpected conf type")
}
select {
case n.confstatec <- pb.ConfState{Nodes: r.nodes}: case <-n.done: }

增加/刪除節點操作都只是更新prs,map的每個元素保存一個peer的狀態,其中最重要的狀態莫過於

Match, Next uint64

看過raft小論文的人一看變數名就很明確意義,Match代表最大的已經落盤的log index,Next代表下一條需要發給這個peer的log index。然後將pendingConf置為false,代表成員變更結束。

重啟如何恢復成員組:

hs, cs, err := c.Storage.InitialState

Storage介面中:

// InitialState returns the saved HardState and ConfState information.
InitialState (pb.HardState, pb.ConfState, error)

Storage是個介面,其中InitialState用於恢復成員組,需要應用自己實現,通常將ConfState記在最後一次Snapshot的Metadata中:

message SnapshotMetadata {
optional ConfState conf_state = 1 [(gogoproto.nullable) = false];
optional uint64 index = 2 [(gogoproto.nullable) = false];
optional uint64 term = 3 [(gogoproto.nullable) = false];
}

ConfState:

message ConfState {
repeated uint64 nodes = 1;
}

拿到ConfState後就可以初始化上面提到的prs,snapshot後續的已經commit的log entry一樣,通過Ready封裝,應用進行apply,如果其中有ConfChange,則調用

s.Node.ApplyConfChange(cc)

喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 科技優家 的精彩文章:

使用jenkins實現持續集成
深入RxEasyHttp網路庫教你3分鐘學會自定義數據結構
滑鼠框選操作(支持遮罩層)上篇
MySQL存儲寫入性能嚴重抖動分析
代碼綜合後的電路對比

TAG:科技優家 |

您可能感興趣

又是跳票?!Air Jordan 1 「Rookie of the Year」 發售日期變更
又是跳票!Air Jordan 1 「Rookie of the Year」 發售日期變更
以上變更會對 AirBoard 產生何種影響?
白色版 Virgil Abloh x Air Jordan 1 發售日期變更
平台本身被變更!Intel基於Cascade Lake-SP架構的Xeon系列處理器泄漏
別說沒提醒你!Virgil Abloh x Air Jordan 1「北卡藍」發售日期變更了!
AJ1 「Best Hand in the Game」系列發售日期再度變更!陪跑機會都不見得有!
博鏈財經完成工商變更,正式啟用qianba.com域名
蘋果iPhone 5s實測,iOS 12讓舊手機變更快
iPhone 5s實測,iOS 12讓舊手機變更快
Science:健康組織中的基因突變更加常見!
中國的Apple iCloud安全變更引發了隱私問題
Apple Watch將迎首次設計變更:好處還不少
蘋果企業聯繫地址變更為 Apple Park
Smartisan OS官微變更認證,成位元組跳動子公司產品
Apple Watch迎首次設計變更:外觀真心贊!
DOTA2:Tigers變更陣容,Dendi等多位老將加入
甜點不見了 谷歌變更Android命名方式
OFF-WHITE x Blazer Mid 彩虹漸變更多實物諜照釋出!
Apple Watch將迎首次設計變更:屏更大邊框變細