日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 运维知识 > windows >内容正文

windows

实现 Raft 协议

發(fā)布時(shí)間:2023/12/24 windows 40 coder
生活随笔 收集整理的這篇文章主要介紹了 实现 Raft 协议 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

文章地址

簡介

Raft 是一個(gè)分布式共識(shí)算法,用于保證所有機(jī)器對(duì)一件事達(dá)成一個(gè)看法。本文用于記錄實(shí)現(xiàn) Raft 選舉和日志復(fù)制的代碼細(xì)節(jié)。

選舉

節(jié)點(diǎn)啟動(dòng)時(shí)首先是跟隨者狀態(tài),如果到達(dá)選舉超時(shí)時(shí)間就嘗試選舉,為了預(yù)防對(duì)稱網(wǎng)絡(luò)分區(qū)帶來的任期不斷增加問題,需要使用預(yù)投票機(jī)制。

選舉超時(shí)時(shí)間:跟隨者在這段時(shí)間內(nèi)沒有搜到領(lǐng)導(dǎo)者的消息,就觸發(fā)選舉超時(shí),轉(zhuǎn)變?yōu)楹蜻x者開始競(jìng)選

對(duì)稱網(wǎng)絡(luò)分區(qū):以 3 臺(tái)機(jī)器為例,其中一臺(tái)機(jī)器與另外兩臺(tái)機(jī)器(這兩臺(tái)中有一個(gè)領(lǐng)導(dǎo)者)的網(wǎng)絡(luò)隔離開了,此時(shí)跟隨者會(huì)觸發(fā)選舉超時(shí),導(dǎo)致其不斷增加任期,在網(wǎng)絡(luò)恢復(fù)正常時(shí),領(lǐng)導(dǎo)者會(huì)因任期小而下線,集群因此觸發(fā)重新選舉

預(yù)投票機(jī)制:觸發(fā)選舉超時(shí)先詢問其他節(jié)點(diǎn)是否同意當(dāng)前節(jié)點(diǎn)進(jìn)行投票,當(dāng)多數(shù)節(jié)點(diǎn)同意時(shí)再進(jìn)行投票,即正式投票

上面介紹了選舉需要注意的問題,下面說具體流程。

  1. 節(jié)點(diǎn)啟動(dòng)時(shí)開啟一個(gè)選舉超時(shí)時(shí)間檢測(cè)定時(shí)任務(wù),用于在當(dāng)前節(jié)點(diǎn)是跟隨者時(shí)不斷檢測(cè)是否發(fā)生了選舉超時(shí),發(fā)生超時(shí)就開始競(jìng)選。每一次定時(shí)任務(wù)的觸發(fā)時(shí)間都是變化的,以防止所有節(jié)點(diǎn)一起選舉,所有人都不投票后死循環(huán)。
  2. 任務(wù)內(nèi)容:如果當(dāng)前節(jié)點(diǎn)不是跟隨者或選舉超時(shí)時(shí)間內(nèi)收到了來自領(lǐng)導(dǎo)者的消息就跳過這輪檢測(cè),否則就代表領(lǐng)導(dǎo)者可能下線了,開始競(jìng)選。
  3. 競(jìng)選的第一階段是預(yù)投票:發(fā)起預(yù)投票 RPC,內(nèi)容有想競(jìng)選的任期以及當(dāng)前的最新日志的任期和索引,如果只有少數(shù)節(jié)點(diǎn)同意投票就結(jié)束這輪任務(wù),否則開始正式投票,RPC 內(nèi)容與上次相同,但這次需要改變當(dāng)前節(jié)點(diǎn)任期號(hào)為想競(jìng)選的任期號(hào)了,同時(shí)也要更改狀態(tài)為候選者,如果只有少數(shù)節(jié)點(diǎn)同意投票就結(jié)束這輪任務(wù)(同時(shí)回滾狀態(tài)為跟隨者),否則就更改狀態(tài)成為領(lǐng)導(dǎo)者,開始發(fā)送心跳等等(成為領(lǐng)導(dǎo)者的一些事后面再說)。

到這里跟隨者如何選舉成為候選者以及領(lǐng)導(dǎo)者就大致完成了,還缺少其他節(jié)點(diǎn)如何處理投票請(qǐng)求:

  1. 請(qǐng)求的任期比當(dāng)前節(jié)點(diǎn)小就拒絕。
  2. 領(lǐng)導(dǎo)者有效(選舉超時(shí)時(shí)間內(nèi)有收到了心跳)就拒接。
  3. 該任期已經(jīng)投過票就拒絕。
  4. 最新的本地日志任期大就拒絕,日志任期相同但本地最新日志的索引更大也拒絕。

如果上面 4 個(gè)條件全通過就投票。投票過程中還有一些節(jié)點(diǎn)狀態(tài)的變更處理,比如收到正式投票的任期比當(dāng)前節(jié)點(diǎn)任期大需要轉(zhuǎn)變?yōu)楦S者等等,當(dāng)前這些也不是重點(diǎn)。

日志復(fù)制

日志復(fù)制是 Raft 的核心,這里涉及到狀態(tài)機(jī)的執(zhí)行,也就是共識(shí)的關(guān)鍵,比較復(fù)雜。

在完成選舉后集群有了領(lǐng)導(dǎo)者,由領(lǐng)導(dǎo)者負(fù)責(zé)與客戶端溝通,在領(lǐng)導(dǎo)者收到客戶端請(qǐng)求時(shí),領(lǐng)導(dǎo)者將這條待狀態(tài)機(jī)執(zhí)行的命令和當(dāng)前任期組合成一條日志寫入本地磁盤,并向其他節(jié)點(diǎn)發(fā)送該條日志,如果多數(shù)節(jié)點(diǎn)都表示收到了,也就表明達(dá)成共識(shí)了,那么領(lǐng)導(dǎo)者就會(huì)將這個(gè)命令放到狀態(tài)機(jī)中執(zhí)行,那么什么時(shí)候集群中的其他跟隨者節(jié)點(diǎn)的狀態(tài)機(jī)執(zhí)行該條日志的命令呢?答案是由定時(shí)的心跳負(fù)責(zé),每次心跳都會(huì)攜帶領(lǐng)導(dǎo)者狀態(tài)機(jī)最后執(zhí)行的日志索引,當(dāng)跟隨者收到后就會(huì)將當(dāng)前節(jié)點(diǎn)狀態(tài)機(jī)最后執(zhí)行的日志索引和心跳中領(lǐng)導(dǎo)者的日志索引之間的日志放到狀態(tài)機(jī)中執(zhí)行,也就是說日志中命令的執(zhí)行是一個(gè)二階段的過程。

選舉中我們忽略了一個(gè)地方,就是成為領(lǐng)導(dǎo)者后需要詢問集群的節(jié)點(diǎn)日志復(fù)制情況,以此來將當(dāng)前領(lǐng)導(dǎo)者多的日志復(fù)制到其他跟隨者,大概過程如下:領(lǐng)導(dǎo)者拿著最新日志的任期和索引和跟隨者對(duì)比,如果相同,等著領(lǐng)導(dǎo)者新的日志復(fù)制就行了,如果不同,說明這個(gè)日志是臟的(日志沒被復(fù)制給大多數(shù)),此時(shí)領(lǐng)導(dǎo)者拿著該條日志的前一條日志繼續(xù)對(duì)比,直到相同,然后領(lǐng)導(dǎo)者將相同的日志之后的所有日志復(fù)制給跟隨者,跟隨者將相同日志后的日志都刪掉,再追加上領(lǐng)導(dǎo)者發(fā)來的日志,這樣跟隨者的日志就正確了。跟隨者與領(lǐng)導(dǎo)者日志的對(duì)齊后就可以等待領(lǐng)導(dǎo)者發(fā)心跳了(即通知跟隨者將哪些日志放到狀態(tài)機(jī)中執(zhí)行)。

關(guān)于狀態(tài)機(jī)執(zhí)行日志還有很重要的一點(diǎn),就是節(jié)點(diǎn)需不需要保存當(dāng)前狀態(tài)機(jī)執(zhí)行過的最后一條日志的索引,比如機(jī)器重啟了,從頭執(zhí)行所有日志對(duì)狀態(tài)機(jī)有沒有影響。可以思考下,如果是一個(gè) KV 數(shù)據(jù)庫狀態(tài)機(jī),不保存也沒問題,因?yàn)槿罩静还軓哪睦飯?zhí)行,數(shù)據(jù)庫中的數(shù)據(jù)也不會(huì)變,但如果是 id 生成器,就會(huì)出現(xiàn)多執(zhí)行一次 id 就會(huì)變化,多執(zhí)行很多次甚至可能出現(xiàn) id 分配完無法繼續(xù)分配的問題,所以命令執(zhí)行多次有問題就需要保存,并且需要滿足保存執(zhí)行過的索引和執(zhí)行狀態(tài)機(jī)命令是一個(gè)原子性的操作。

讀請(qǐng)求優(yōu)化(讀索引讀)

日志復(fù)制是需要刷盤的,這個(gè)操作非常耗時(shí),寫請(qǐng)求只能通過領(lǐng)導(dǎo)者進(jìn)行日志復(fù)制處理,但讀請(qǐng)求不同,可以像 ReentrantReadWriteLock 讀寫鎖一樣,將讀請(qǐng)求負(fù)載到跟隨者上,也就是實(shí)現(xiàn)跨機(jī)器的 volatile 語義(和跨進(jìn)程類似),即讀跟隨者時(shí)確保跟隨者的狀態(tài)機(jī)已經(jīng)和領(lǐng)導(dǎo)者的狀態(tài)機(jī)一樣,具體過程如下:跟隨者收到讀請(qǐng)求,跟隨者請(qǐng)求領(lǐng)導(dǎo)者同步日志以及狀態(tài)機(jī)應(yīng)該執(zhí)行到那條日志,領(lǐng)導(dǎo)者收到請(qǐng)求后向所有的節(jié)點(diǎn)發(fā)一個(gè) RPC 確認(rèn)領(lǐng)導(dǎo)者地位(防止領(lǐng)導(dǎo)者所在的少部分節(jié)點(diǎn)分區(qū)后還能正常讀),確認(rèn)后同步日志并回復(fù)該跟隨者,收到回復(fù)后的跟隨者的狀態(tài)機(jī)再執(zhí)行讀請(qǐng)求。

對(duì)于領(lǐng)導(dǎo)者的讀請(qǐng)求同樣也不需要走日志復(fù)制,只需要和其他跟隨者確認(rèn)自己的領(lǐng)導(dǎo)者地位就可以執(zhí)行讀命令了。

最后

coding 時(shí)要注意節(jié)點(diǎn)任期的變化,剛開始可以先用一個(gè)全局鎖來回避這個(gè)問題,等后面到一定的復(fù)雜程度再細(xì)化鎖。完整的 Raft 還需要考慮很多,比如快照、批量、pipeline、刪減節(jié)點(diǎn)等等。最后貼上我的實(shí)現(xiàn) raft/README.md 以及相關(guān)學(xué)習(xí)資料:

  • Raft 入門動(dòng)畫

  • 可視化的 Raft

  • 剖析-sofajraft-實(shí)現(xiàn)原理

  • MIT 6.824 2020 Raft 實(shí)現(xiàn)細(xì)節(jié)備忘 | 木鳥雜記 (qtmuniao.com)

  • Raft算法之日志復(fù)制 - 觸不可及` - 博客園 (cnblogs.com)

  • SOFAJRaft 源碼分析二(日志復(fù)制、心跳) - 個(gè)人文章 - SegmentFault 思否

  • 關(guān)于 DDIA 上對(duì) Raft 協(xié)議的這種極端場(chǎng)景的描述,要如何理解? - 知乎 (zhihu.com)

  • 為什么 Raft 的 ApplyIndex 和 CommitIndex 不需要持久化? - 知乎 (zhihu.com)

  • stateIs0/lu-raft-kv: this is raft java project. raft-kv-storage (github.com)

  • wenweihu86/raft-java: Raft Java implementation which is simple and easy to understand. (github.com)

總結(jié)

以上是生活随笔為你收集整理的实现 Raft 协议的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。