溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

etcd raft library設(shè)計(jì)原理和使用

發(fā)布時(shí)間:2020-05-13 16:27:18 來(lái)源:億速云 閱讀:198 作者:三月 欄目:開(kāi)發(fā)技術(shù)

本文主要給大家簡(jiǎn)單講講etcd raft library設(shè)計(jì)原理和使用,相關(guān)專業(yè)術(shù)語(yǔ)大家可以上網(wǎng)查查或者找一些相關(guān)書(shū)籍補(bǔ)充一下,這里就不涉獵了,我們就直奔主題吧,希望etcd raft library設(shè)計(jì)原理和使用這篇文章可以給大家?guī)?lái)一些實(shí)際幫助。

這個(gè)library使用起來(lái)相對(duì)來(lái)說(shuō)還是有點(diǎn)麻煩。官方有一個(gè)使用示例在 https://github.com/coreos/etcd/tree/master/contrib/raftexample。整體來(lái)說(shuō),這個(gè)庫(kù)實(shí)現(xiàn)了raft協(xié)議核心的內(nèi)容,比如append log的邏輯,選主邏輯,snapshot,成員變更等邏輯。需要明確的是:library沒(méi)有實(shí)現(xiàn)消息的網(wǎng)絡(luò)傳輸和接收,庫(kù)只會(huì)把一些待發(fā)送的消息保存在內(nèi)存中,用戶自定義的網(wǎng)絡(luò)傳輸層取出消息并發(fā)送出去,并且在網(wǎng)絡(luò)接收端,需要調(diào)一個(gè)library的函數(shù),用于將收到的消息傳入library,后面會(huì)詳細(xì)說(shuō)明。同時(shí),library定義了一個(gè)Storage接口,需要library的使用者自行實(shí)現(xiàn)。

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中會(huì)被用到。熟悉raft協(xié)議的人不難理解。上面提到的官方示例https://github.com/coreos/etcd/tree/master/contrib/raftexample中使用了library自帶的MemoryStorage,和etcd的wal和snap包做持久化,重啟的時(shí)候從wal和snap中獲取日志恢復(fù)MemoryStorage。

etcd raft library設(shè)計(jì)原理和使用

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

下面看一下為了做這事,etcd提供的核心抽象Ready結(jié)構(gòu)體:

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

可以說(shuō),這個(gè)Ready結(jié)構(gòu)體封裝了一批更新,這些更新包括:

  • pb.HardState: 包含當(dāng)前節(jié)點(diǎn)見(jiàn)過(guò)的最大的term,以及在這個(gè)term給誰(shuí)投過(guò)票,已經(jīng)當(dāng)前節(jié)點(diǎn)知道的commit index

  • Messages: 需要廣播給所有peers的消息

  • CommittedEntries:已經(jīng)commit了,還沒(méi)有apply到狀態(tài)機(jī)的日志

  • Snapshot:需要持久化的快照

庫(kù)的使用者從node結(jié)構(gòu)體提供的一個(gè)ready channel中不斷的pop出一個(gè)個(gè)的Ready進(jìn)行處理,庫(kù)使用者通過(guò)如下方法拿到Ready channel:

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

應(yīng)用需要對(duì)Ready的處理包括:

  1. 將HardState, Entries, Snapshot持久化到storage。

  2. 將Messages(上文提到的msgs)非阻塞的廣播給其他peers

  3. 將CommittedEntries(已經(jīng)commit還沒(méi)有apply)應(yīng)用到狀態(tài)機(jī)。

  4. 如果發(fā)現(xiàn)CommittedEntries中有成員變更類型的entry,調(diào)用node的ApplyConfChange()方法讓node知道(這里和raft論文不一樣,論文中只要節(jié)點(diǎn)收到了成員變更日志就應(yīng)用)

  5. 調(diào)用Node.Advance()告訴raft node,這批狀態(tài)更新處理完了,狀態(tài)已經(jīng)演進(jìn)了,可以給我下一批Ready讓我處理。

應(yīng)用通過(guò)raft.StartNode()來(lái)啟動(dòng)raft中的一個(gè)副本,函數(shù)內(nèi)部通過(guò)啟動(dòng)一個(gè)goroutine運(yùn)行

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

來(lái)啟動(dòng)服務(wù)。

應(yīng)用通過(guò)調(diào)用

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

來(lái)Propose一個(gè)請(qǐng)求給raft,被raft開(kāi)始處理后返回。

增刪節(jié)點(diǎn)通過(guò)調(diào)用

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

node結(jié)構(gòu)體包含幾個(gè)重要的channel:

// node is the canonical implementation of the Node interfacetype node struct {
    propc      chan pb.Message
    recvc      chan pb.Message
    confc      chan pb.ConfChange
    confstatec chan pb.ConfState
    readyc     chan Ready
    advancec   chan struct{}
    tickc      chan struct{}
    done       chan struct{}
    stop       chan struct{}
    status     chan chan Status

    logger Logger
}
  • propc: propc是一個(gè)沒(méi)有buffer的channel,應(yīng)用通過(guò)Propose接口寫(xiě)入的請(qǐng)求被封裝成Message被push到propc中,node的run方法從propc中pop出Message,append自己的raft log中,并且將Message放入mailbox中(raft結(jié)構(gòu)體中的msgs []pb.Message),這個(gè)msgs會(huì)被封裝在Ready中,被應(yīng)用從readyc中取出來(lái),然后通過(guò)應(yīng)用自定義的transport發(fā)送出去。

  • recvc: 應(yīng)用自定義的transport在收到Message后需要調(diào)用

    func (n *node) Step(ctx context.Context, m pb.Message) error

    來(lái)把Message放入recvc中,經(jīng)過(guò)一些處理后,同樣,會(huì)把需要發(fā)送的Message放入到對(duì)應(yīng)peers的mailbox中。后續(xù)通過(guò)自定義transport發(fā)送出去。

  • readyc/advancec: readyc和advancec都是沒(méi)有buffer的channel,node.run()內(nèi)部把相關(guān)的一些狀態(tài)更新打包成Ready結(jié)構(gòu)體(其中一種狀態(tài)就是上面提到的msgs)放入readyc中。應(yīng)用從readyc中pop出Ready中,對(duì)相應(yīng)的狀態(tài)進(jìn)行處理,處理完成后,調(diào)用

    rc.node.Advance()

    往advancec中push一個(gè)空結(jié)構(gòu)體告訴raft,已經(jīng)對(duì)這批Ready包含的狀態(tài)進(jìn)行了相應(yīng)的處理,node.run()內(nèi)部從advancec中得到通知后,對(duì)內(nèi)部一些狀態(tài)進(jìn)行處理,比如把已經(jīng)持久化到storage中的entries從內(nèi)存(對(duì)應(yīng)type unstable struct)中刪除等。

  • tickc:應(yīng)用定期往tickc中push空結(jié)構(gòu)體,node.run()會(huì)調(diào)用tick()函數(shù),對(duì)于leader來(lái)說(shuō),tick()會(huì)給其他peers發(fā)心跳,對(duì)于follower來(lái)說(shuō),會(huì)檢查是否需要發(fā)起選主操作。

  • confc/confstatec:應(yīng)用從Ready中拿出CommittedEntries,檢查其如果含有成員變更類型的日志,則需要調(diào)用

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

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

etcd raft library設(shè)計(jì)原理和使用就先給大家講到這里,對(duì)于其它相關(guān)問(wèn)題大家想要了解的可以持續(xù)關(guān)注我們的行業(yè)資訊。我們的板塊內(nèi)容每天都會(huì)捕捉一些行業(yè)新聞及專業(yè)知識(shí)分享給大家的。

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI