從 dgraph-io/dgraph 了解 etcd/raft 的使用 0x00

0. Intro

0.1 raft

raft 是一種分布式一致性算法.

簡(jiǎn)單來(lái)說, raft 的使用場(chǎng)景是 log replication.

關(guān)于分布式一致性算法, paxos, raft 等概念, 網(wǎng)上有大量的文章, 這里不再多做說明.

raft in go

比較成熟的 raft golang 實(shí)現(xiàn)有 hashicorp/raftetcd/raft 兩個(gè)版本.

二者分別被用在 hashicorp/consulcoreos/etcd 中, 均有大量生產(chǎn)環(huán)境的使用案例.

有很多優(yōu)秀的開源項(xiàng)目使用二者之一, 比如

0.2 etcd/raft

相比而言, hashicorp/raft 比較容易上手; 而 etcd/raft 則基于簡(jiǎn)潔的抽象, 提供了更多可能性.

很多 etcd/raft 的使用者選擇自行實(shí)現(xiàn)高度定制的網(wǎng)絡(luò)傳輸層和持久化層等組件.

etcd/raft README 的 Usage 段落用了很大篇幅來(lái)描述user has a few responsibilities, 這些定制化的組件也就是在這里做文章

可以參考 cockroach 的開發(fā)博客 Scaling Raft . (文中 Multi-Raft 的鏈接已經(jīng)失效, 這是因?yàn)?cockroach 的開發(fā)者發(fā)現(xiàn)這套實(shí)現(xiàn)很難從使用應(yīng)用中解耦出來(lái) etcd/issues/4932 )

TiDB 開發(fā)過程中遇到了類似的問題, 因此他們的底層存儲(chǔ) tikv 也選擇參考 etcd/raft 的實(shí)現(xiàn).

我們先簡(jiǎn)單介紹一下 etcd/raft .

raftpb

raftpb 使用 protobuf 定義了基礎(chǔ)數(shù)據(jù)結(jié)構(gòu).

Node

這個(gè)接口定義基本說明了使用者能做哪些事情...

// Node represents a node in a raft cluster.
type Node interface {
    // Tick increments the internal logical clock for the Node by a single tick. Election
    // timeouts and heartbeat timeouts are in units of ticks.
    Tick()
    // Campaign causes the Node to transition to candidate state and start campaigning to become leader.
    Campaign(ctx context.Context) error
    // Propose proposes that data be appended to the log.
    Propose(ctx context.Context, data []byte) error
    // ProposeConfChange proposes config change.
    // At most one ConfChange can be in the process of going through consensus.
    // Application needs to call ApplyConfChange when applying EntryConfChange type entry.
    ProposeConfChange(ctx context.Context, cc pb.ConfChange) error
    // Step advances the state machine using the given message. ctx.Err() will be returned, if any.
    Step(ctx context.Context, msg pb.Message) error

    // Ready returns a channel that returns the current point-in-time state.
    // Users of the Node must call Advance after retrieving the state returned by Ready.
    //
    // NOTE: No committed entries from the next Ready may be applied until all committed entries
    // and snapshots from the previous one have finished.
    Ready() <-chan Ready

    // Advance notifies the Node that the application has saved progress up to the last Ready.
    // It prepares the node to return the next available Ready.
    //
    // The application should generally call Advance after it applies the entries in last Ready.
    //
    // However, as an optimization, the application may call Advance while it is applying the
    // commands. For example. when the last Ready contains a snapshot, the application might take
    // a long time to apply the snapshot data. To continue receiving Ready without blocking raft
    // progress, it can call Advance before finishing applying the last ready.
    Advance()
    // ApplyConfChange applies config change to the local node.
    // Returns an opaque ConfState protobuf which must be recorded
    // in snapshots. Will never return nil; it returns a pointer only
    // to match MemoryStorage.Compact.
    ApplyConfChange(cc pb.ConfChange) *pb.ConfState

    // TransferLeadership attempts to transfer leadership to the given transferee.
    TransferLeadership(ctx context.Context, lead, transferee uint64)

    // ReadIndex request a read state. The read state will be set in the ready.
    // Read state has a read index. Once the application advances further than the read
    // index, any linearizable read requests issued before the read request can be
    // processed safely. The read state will have the same rctx attached.
    ReadIndex(ctx context.Context, rctx []byte) error

    // Status returns the current status of the raft state machine.
    Status() Status
    // ReportUnreachable reports the given node is not reachable for the last send.
    ReportUnreachable(id uint64)
    // ReportSnapshot reports the status of the sent snapshot.
    ReportSnapshot(id uint64, status SnapshotStatus)
    // Stop performs any necessary termination of the Node.
    Stop()
}
Storage

這是日志持久化層

但是實(shí)際上可以看到, 沒有要求提供寫的方法.

言下之意是 我只需要讀, 至于該怎么存, 存哪里, 請(qǐng)?jiān)?Node.Ready() 中自行解決

// 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)
}
網(wǎng)絡(luò)傳輸

etcd/raft 沒有提供任何網(wǎng)絡(luò)傳輸層的接口定義.

與日志的持久化類似, 我只告訴你哪些 message 需要發(fā)出, 怎么發(fā), 發(fā)往哪里請(qǐng)自行解決 ??.

總得有一些開箱即用的東西...

對(duì)于日志持久化層, etcd/raft 提供了一個(gè)內(nèi)存版本的 Storage 實(shí)現(xiàn) MemoryStorage , 通過 wal 落盤.

rafthttp 則提供了節(jié)點(diǎn)尋址和基于 http 的網(wǎng)絡(luò)傳輸能力...

可以參考一下 etcd 官方提供的 demo .

港真, 我在用 hashicorp/raft 寫了一些基本能用的小玩具之后看這個(gè) demo, 還是把我繞暈了.

0.3 dgraph

dgraph 是一款使用 go 語(yǔ)言開發(fā)的分布式圖數(shù)據(jù)庫(kù).

dgraph zero

zero 節(jié)點(diǎn)用于管理 dgraph 集群, 維護(hù)成員信息, 數(shù)據(jù)的 sharding 和 rebalancing.

我們借著閱讀 zero 的實(shí)現(xiàn)代碼來(lái)看一看 etcd/raft 的使用, 以及它的周邊組件的實(shí)現(xiàn)方式.

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖涯保,帶你破解...
    沈念sama閱讀 219,589評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡兼贡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,615評(píng)論 3 396
  • 文/潘曉璐 我一進(jìn)店門幢泼,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)紧显,“玉大人,你說我怎么就攤上這事缕棵》醢啵” “怎么了?”我有些...
    開封第一講書人閱讀 165,933評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵招驴,是天一觀的道長(zhǎng)篙程。 經(jīng)常有香客問我,道長(zhǎng)别厘,這世上最難降的妖魔是什么虱饿? 我笑而不...
    開封第一講書人閱讀 58,976評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮触趴,結(jié)果婚禮上氮发,老公的妹妹穿的比我還像新娘。我一直安慰自己冗懦,他們只是感情好爽冕,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,999評(píng)論 6 393
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著披蕉,像睡著了一般颈畸。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上没讲,一...
    開封第一講書人閱讀 51,775評(píng)論 1 307
  • 那天眯娱,我揣著相機(jī)與錄音,去河邊找鬼爬凑。 笑死徙缴,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的嘁信。 我是一名探鬼主播娜搂,決...
    沈念sama閱讀 40,474評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼迁霎,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了百宇?” 一聲冷哼從身側(cè)響起考廉,我...
    開封第一講書人閱讀 39,359評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎携御,沒想到半個(gè)月后昌粤,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,854評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡啄刹,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,007評(píng)論 3 338
  • 正文 我和宋清朗相戀三年涮坐,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片誓军。...
    茶點(diǎn)故事閱讀 40,146評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡袱讹,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出昵时,到底是詐尸還是另有隱情捷雕,我是刑警寧澤,帶...
    沈念sama閱讀 35,826評(píng)論 5 346
  • 正文 年R本政府宣布壹甥,位于F島的核電站救巷,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏句柠。R本人自食惡果不足惜浦译,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,484評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望溯职。 院中可真熱鬧精盅,春花似錦、人聲如沸谜酒。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,029評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)甚带。三九已至,卻和暖如春佳头,著一層夾襖步出監(jiān)牢的瞬間鹰贵,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,153評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工康嘉, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留碉输,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,420評(píng)論 3 373
  • 正文 我出身青樓亭珍,卻偏偏與公主長(zhǎng)得像敷钾,于是被迫代替她去往敵國(guó)和親枝哄。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,107評(píng)論 2 356