etcd raft library設計原理和使用

早在2013年11月份,在raft論文還只能在網上下載到草稿版時,我曾經寫過一篇blog對其進行簡要分析。4年過去了,各種raft協議的講解鋪天蓋地,raft也確實得到了廣泛的應用。其中最知名的應用莫過于etcd。etcd將raft協議本身實現為一個library,位于https://github.com/coreos/etcd/tree/master/raft,然后本身作為一個應用使用它。

本文不講解raft協議核心內容,而是站在一個etcd raft library使用者的角度,講解要用上這個library需要了解的東西。

這個library使用起來相對來說還是有點麻煩。官方有一個使用示例在 https://github.com/coreos/etcd/tree/master/contrib/raftexample。整體來說,這個庫實現了raft協議核心的內容,比如append log的邏輯,選主邏輯,snapshot,成員變更等邏輯。需要明確的是:library沒有實現消息的網絡傳輸和接收,庫只會把一些待發送的消息保存在內存中,用戶自定義的網絡傳輸層取出消息并發送出去,并且在網絡接收端,需要調一個library的函數,用于將收到的消息傳入library,后面會詳細說明。同時,library定義了一個Storage接口,需要library的使用者自行實現。

Storage接口如下:

// Storage is an interface that may be implemented by the application
// to retrieve log entries from storage.
//
// If any Storage method returns an error, the raft instance will
// become inoperable and refuse to participate in elections; the
// application is responsible for cleanup and recovery in this case.
type Storage interface {// InitialState returns the saved HardState and ConfState information.InitialState() (pb.HardState, pb.ConfState, error)// Entries returns a slice of log entries in the range [lo,hi).// MaxSize limits the total size of the log entries returned, but// Entries returns at least one entry if any.Entries(lo, hi, maxSize uint64) ([]pb.Entry, error)// Term returns the term of entry i, which must be in the range// [FirstIndex()-1, LastIndex()]. The term of the entry before// FirstIndex is retained for matching purposes even though the// rest of that entry may not be available.Term(i uint64) (uint64, error)// LastIndex returns the index of the last entry in the log.LastIndex() (uint64, error)// FirstIndex returns the index of the first log entry that is// possibly available via Entries (older entries have been incorporated// into the latest Snapshot; if storage only contains the dummy entry the// first log entry is not available).FirstIndex() (uint64, error)// Snapshot returns the most recent snapshot.// If snapshot is temporarily unavailable, it should return ErrSnapshotTemporarilyUnavailable,// so raft state machine could know that Storage needs some time to prepare// snapshot and call Snapshot later.Snapshot() (pb.Snapshot, error)
}

這些接口在library中會被用到。熟悉raft協議的人不難理解。上面提到的官方示例https://github.com/coreos/etcd/tree/master/contrib/raftexample中使用了library自帶的MemoryStorage,和etcd的wal和snap包做持久化,重啟的時候從wal和snap中獲取日志恢復MemoryStorage。

要提供這種IO/網絡密集型的東西,提高吞吐最好的手段就是batch加批處理了。etcd raft library正是這么做的。

下面看一下為了做這事,etcd提供的核心抽象Ready結構體:

// 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
}

可以說,這個Ready結構體封裝了一批更新,這些更新包括:

  • pb.HardState: 包含當前節點見過的最大的term,以及在這個term給誰投過票,已經當前節點知道的commit index
  • Messages: 需要廣播給所有peers的消息
  • CommittedEntries:已經commit了,還沒有apply到狀態機的日志
  • Snapshot:需要持久化的快照

庫的使用者從node結構體提供的一個ready channel中不斷的pop出一個個的Ready進行處理,庫使用者通過如下方法拿到Ready channel:

func (n *node) Ready() <-chan Ready { return n.readyc }

應用需要對Ready的處理包括:

  1. 將HardState, Entries, Snapshot持久化到storage。
  2. 將Messages(上文提到的msgs)非阻塞的廣播給其他peers
  3. 將CommittedEntries(已經commit還沒有apply)應用到狀態機。
  4. 如果發現CommittedEntries中有成員變更類型的entry,調用node的ApplyConfChange()方法讓node知道(這里和raft論文不一樣,論文中只要節點收到了成員變更日志就應用)
  5. 調用Node.Advance()告訴raft node,這批狀態更新處理完了,狀態已經演進了,可以給我下一批Ready讓我處理。

應用通過raft.StartNode()來啟動raft中的一個副本,函數內部通過啟動一個goroutine運行

func (n *node) run(r *raft)

來啟動服務。

應用通過調用

func (n *node) Propose(ctx context.Context, data []byte) error

來Propose一個請求給raft,被raft開始處理后返回。

增刪節點通過調用

func (n *node) ProposeConfChange(ctx context.Context, cc pb.ConfChange) error

node結構體包含幾個重要的channel:

// node is the canonical implementation of the Node interface
type node struct {propc      chan pb.Messagerecvc      chan pb.Messageconfc      chan pb.ConfChangeconfstatec chan pb.ConfStatereadyc     chan Readyadvancec   chan struct{}tickc      chan struct{}done       chan struct{}stop       chan struct{}status     chan chan Statuslogger Logger
}
  • propc: propc是一個沒有buffer的channel,應用通過Propose接口寫入的請求被封裝成Message被push到propc中,node的run方法從propc中pop出Message,append自己的raft log中,并且將Message放入mailbox中(raft結構體中的msgs []pb.Message),這個msgs會被封裝在Ready中,被應用從readyc中取出來,然后通過應用自定義的transport發送出去。
  • recvc: 應用自定義的transport在收到Message后需要調用

    func (n *node) Step(ctx context.Context, m pb.Message) error
    來把Message放入recvc中,經過一些處理后,同樣,會把需要發送的Message放入到對應peers的mailbox中。后續通過自定義transport發送出去。
  • readyc/advancec: readyc和advancec都是沒有buffer的channel,node.run()內部把相關的一些狀態更新打包成Ready結構體(其中一種狀態就是上面提到的msgs)放入readyc中。應用從readyc中pop出Ready中,對相應的狀態進行處理,處理完成后,調用

    rc.node.Advance()
    往advancec中push一個空結構體告訴raft,已經對這批Ready包含的狀態進行了相應的處理,node.run()內部從advancec中得到通知后,對內部一些狀態進行處理,比如把已經持久化到storage中的entries從內存(對應type unstable struct)中刪除等。
  • tickc:應用定期往tickc中push空結構體,node.run()會調用tick()函數,對于leader來說,tick()會給其他peers發心跳,對于follower來說,會檢查是否需要發起選主操作。
  • confc/confstatec:應用從Ready中拿出CommittedEntries,檢查其如果含有成員變更類型的日志,則需要調用

    func (n *node) ApplyConfChange(cc pb.ConfChange) *pb.ConfState

    這個函數會push ConfChange到confc中,confc同樣是個無buffer的channel,node.run()內部會從confc中拿出ConfChange,然后進行真正的增減peers操作,之后將最新的成員組push到confstatec中,而ApplyConfChange函數從confstatec pop出最新的成員組返回給應用。

可以說,要想用上etcd的raft library還是需要了解不少東西的。

轉載于:https://www.cnblogs.com/foxmailed/p/7137431.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/254102.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/254102.shtml
英文地址,請注明出處:http://en.pswp.cn/news/254102.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

halcon通過點擬合圓形,鼠標選點

原圖 源碼 read_image (Image, 0.bmp) dev_clear_window () dev_open_window_fit_image (Image, 0, 0, -1, -1, WindowHandle) dev_display (Image)binary_threshold (Image, Region, max_separability, dark, UsedThreshold) connection (Region, ConnectedRegions) select_s…

JDBC事務--軟件開發三層架構--ThreadLocal

JDBC事務--軟件開發三層架構--ThreadLocal 一.JDBC事務 1.概述: 事務是指邏輯上的一組操作!這一組操作,通常認為是一個整體,不可拆分! 特點:同生共死;事務內的這一組操作要么全部成功,要么全部失敗! 作用:保證邏輯操作的完整性,安全性! 2.使用(3種方式) 1)面向數據庫,使用S…

LINUX多播編程

一.單播&#xff0c;廣播和多播 1.單播用于兩個主機之間的端對端通信&#xff0c;廣播用于一個主機對整個局域網上所有主機上的數據通信。單播和廣播是兩個極端&#xff0c;要么對一個主機進行通信&#xff0c;要么對整個局域網上的主機進行通信。實際情況下&#xff0c;經常需…

cas單點登錄搭建

Cas Server下載&#xff1a;http://developer.jasig.org/cas/ Cas Client下載&#xff1a;http://developer.jasig.org/cas-clients/ 測試環境&#xff1a; jdk&#xff1a;java version "1.8.0_60" tomcat&#xff1a;apache-tomcat-7.0.65 mysql&#xff1a;mysql5…

新CIO:Mark Schwartz認為的領先IT

美國公民及移民服務局前任CIO&#xff0c;現任AWS企業戰略師Mark Schwartz在倫敦舉行的DevOps企業峰會上介紹了什么是領先的IT。\\Schwartz介紹說&#xff0c;老舊、傳統的模型將業務和IT完全分開&#xff0c;他又提出了一種新的模型&#xff0c;在這種模型中&#xff0c;CIO擔…

689D Magic Odd Square 奇數幻方

1 奇數階幻方構造法 (1) 將1放在第一行中間一列; (2) 從2開始直到nn止各數依次按下列規則存放&#xff1a;按 45方向行走&#xff0c;向右上&#xff0c;即每一個數存放的行比前一個數的行數減1&#xff0c;列數加1 (3) 如果行列范圍超出矩陣范圍&#xff0c;則回繞。例如1在第…

Java單例的常見形式

2019獨角獸企業重金招聘Python工程師標準>>> Java單例的常見形式 本文目的&#xff1a;總結Java中的單例模式 本文定位&#xff1a;學習筆記 學習過程記錄&#xff0c;加深理解&#xff0c;便于回顧。也希望能給學習的同學一些靈感 一、非延遲加載單例類 public cla…

運動控制卡的基類函數與實現例子

基類 namespace MotionCardDll {public abstract class IMotionCard{public Int32 m_Mode;public Int32 m_BoardId;//Card 號public Int32 m_Card_name;public Int32 m_StartAxisID

U-Boot啟動過程完全分析

1.1 U-Boot 工作過程 U-Boot啟動內核的過程可以分為兩個階段&#xff0c;兩個階段的功能如下&#xff1a; &#xff08;1&#xff09;第一階段的功能 硬件設備初始化 加載U-Boot第二階段代碼到RAM空間 設置好棧 跳轉到第二階段代碼入口 &#xff08;2&#x…

CJOJ 2171 火車站開飯店(樹型動態規劃)

CJOJ 2171 火車站開飯店&#xff08;樹型動態規劃&#xff09; Description 政府邀請了你在火車站開飯店&#xff0c;但不允許同時在兩個相連的火車站開。任意兩個火車站有且只有一條路徑&#xff0c;每個火車站最多有 50 個和它相連接的火車站。 告訴你每個火車站的利潤&#…

JavaWeb總結(十五)

AJAX&#xff08;Asynchronous JavaScript and XML&#xff08;異步的 JavaScript 和 XML&#xff09;&#xff09; AJAX的作用是什么&#xff1f; 在無需重新加載整個網頁的情況下&#xff0c;能夠更新部分網頁的技術 是一種用于創建快速動態網頁的技術 通過在后臺與服務器進行…

工業相機基類與實現

基類 namespace Cameron {//相機參數public struct CamPara{public int DeviceID; //設備描述public string Name;public int WorkMode; //工作類型,0為連續模式,1為觸發模式public float Expours

物聯網技術周報第 143 期: Unity 3D 和 Arduino 打造虛擬現實飛行器

新聞 \\\\t《西門子、阿里云簽約助力中國工業物聯網發展》德國工業集團西門子和中國阿里巴巴集團旗下的云計算公司阿里云&#xff19;日在柏林簽署備忘錄&#xff0c;共同推進中國工業物聯網發展。根據備忘錄內容&#xff0c;西門子和阿里云將發揮各自技術和行業優勢&#xff…

不同平臺下 sleep區別用法

應用程序&#xff1a; #include <syswait.h> usleep(n) //n微秒 Sleep&#xff08;n&#xff09;//n毫秒 sleep&#xff08;n&#xff09;//n秒 驅動程序&#xff1a; #include <linux/delay.h> mdelay(n) //微秒milliseconds 其實現 #ifdef notdef #define mdelay…

各視頻、各音頻之間格式任意玩弄(圖文詳解)

寫在前面說的話 在這里&#xff0c;記錄下來&#xff0c;是為了方便以后偶爾所制作所需和你們前來的瀏覽學習。 學會&#xff0c;玩弄一些視頻和音頻的軟件&#xff0c;只有好處沒有害處。同時&#xff0c;也不需很多時間&#xff0c;練練手罷了。也是方便自己所用吧&#xff0…

oracle 如何查看日志?

2019獨角獸企業重金招聘Python工程師標準>>> Oracle日志查看一&#xff0e;Oracle日志的路徑&#xff1a;登錄&#xff1a;sqlplus "/as sysdba"查看路徑&#xff1a;SQL> select * from v$logfile;SQL> select * from v$logfile;(#日志文件路徑)二…

回歸_英國酒精和香煙關系

sklearn實戰-乳腺癌細胞數據挖掘(博客主親自錄制視頻教程) https://study.163.com/course/introduction.htm?courseId1005269003&utm_campaigncommission&utm_sourcecp-400000000398149&utm_mediumshare 數據統計分析聯系:&#xff31;&#xff31;&#xff1a;&a…

C# ini文件讀寫函數

namespace Tools {class IniOperate{[DllImport("kernel32")]private static extern int GetPrivateProfileString(string section, string key,

Visual studio內存泄露檢查工具--BoundsChecker

BoundsChecker是一個Run-Time錯誤檢測工具&#xff0c;它主要定位程序在運行時期發生的各種錯誤。 BoundsChecker能檢測的錯誤包括&#xff1a; 1&#xff09;指針操作和內存、資源泄露錯誤&#xff0c;比如&#xff1a;內存泄露&#xff1b;資源泄露&#xff…

【轉】如何用Maven創建web項目(具體步驟)

使用eclipse插件創建一個web project 首先創建一個Maven的Project如下圖 我們勾選上Create a simple project &#xff08;不使用骨架&#xff09; 這里的Packing 選擇 war的形式 由于packing是war包&#xff0c;那么下面也就多出了webapp的目錄 由于我們的項目要使用eclipse發…