一致性协议raft详解(三):raft中的消息类型
一致性協(xié)議raft詳解(三):raft中的消息類型
- 前言
- raft 節(jié)點(diǎn)
- Raft中RPC的種類
- RequestVote
- leader選舉成功后
- AppendEntries
- 請(qǐng)求參數(shù)
- 返回值
- 存儲(chǔ)日志(日志同步過(guò)程)
- InstallSnapshot RPC
- 快照的并發(fā)性
- 快照實(shí)現(xiàn)以及何時(shí)做快照
- 快照實(shí)現(xiàn)
- disk-based
- memory-based
- 參考鏈接
前言
有關(guān)一致性協(xié)議的資料網(wǎng)上有很多,當(dāng)然錯(cuò)誤也有很多。筆者在學(xué)習(xí)的過(guò)程中走了不少?gòu)澛贰,F(xiàn)在回過(guò)頭來(lái)看,最好的學(xué)習(xí)資料就是Leslie Lamport和Diego Ongaro的數(shù)篇論文、Ongaro在youtube上發(fā)的三個(gè)視頻講解,以及何登成的ppt。
本系列文章是只是筆者在學(xué)習(xí)一致性協(xié)議過(guò)程中的摘抄和總結(jié),有疏漏之處敬請(qǐng)諒解,歡迎討論。
raft 節(jié)點(diǎn)
Raft算法中服務(wù)器有三種角色
每個(gè)服務(wù)器上都會(huì)存儲(chǔ)的持久狀態(tài):
每個(gè)服務(wù)器上都會(huì)存儲(chǔ)的易失狀態(tài):
上面兩個(gè)index只是索引,可能會(huì)有空擋,比如某個(gè)log entry沒(méi)有commit上
在狀態(tài)為L(zhǎng)eader的服務(wù)器上會(huì)額外存儲(chǔ)的易失狀態(tài):
Raft中RPC的種類
RequestVote
candidate節(jié)點(diǎn)請(qǐng)求其他節(jié)點(diǎn)投票給自己
請(qǐng)求參數(shù):
返回值:
一個(gè)節(jié)點(diǎn)(無(wú)論當(dāng)前是什么狀態(tài))在接收到RequestVote(term, candidateId, lastLogIndex, lastLogTerm)消息時(shí), 其會(huì)做如下判斷:
leader選舉成功后
領(lǐng)導(dǎo)人:
- 一旦成為領(lǐng)導(dǎo)人:發(fā)送空的附加日志 RPC(心跳)給其他所有的服務(wù)器;在一定的空余時(shí)間之后不停的重復(fù)發(fā)送,以阻止follower超時(shí)(5.2 節(jié))
- 如果接收到來(lái)自客戶端的請(qǐng)求:附加條目到本地日志中,在條目被應(yīng)用到狀態(tài)機(jī)后響應(yīng)客戶端(5.3 節(jié))
- 如果對(duì)于一個(gè)follower,如果leader發(fā)現(xiàn)自己的最后日志條目的索引值大于等于 nextIndex,那么:發(fā)送從 nextIndex 開(kāi)始的所有日志條目:
- 如果成功:更新相應(yīng)follower的 nextIndex 和 matchIndex
- 如果因?yàn)槿罩静灰恢露?#xff0c;減少 nextIndex 重試
- 如果存在一個(gè)滿足N > commitIndex的 N,并且大多數(shù)的matchIndex[i] ≥ N成立,并且log[N].term == currentTerm成立,那么令 commitIndex 等于這個(gè) N (5.3 和 5.4 節(jié)) (figure 8),這樣的話,leader就可以把漏下的日志補(bǔ)上
- 之所以這么做,是因?yàn)樵谛碌膌eader選舉的過(guò)程中,老的leader是可以繼續(xù)生效的,那么也就導(dǎo)致新的leader可能確實(shí)了一部分老leader最后commit的日志,或者network partition了,某個(gè)節(jié)點(diǎn)的term很大,導(dǎo)致其一定是主,但是這個(gè)主上有很多漏掉的leader
AppendEntries
leader節(jié)點(diǎn)使用該消息向其他節(jié)點(diǎn)同步日志, 或者發(fā)送空消息作為心跳包以維持leader的統(tǒng)治地位
請(qǐng)求參數(shù)
返回值
一個(gè)節(jié)點(diǎn)(無(wú)論當(dāng)前是什么狀態(tài))接收到AppendEntries(term, leaderId, prevLogIndex, prevLogTerm, entries[], leaderCommit)消息時(shí), 其會(huì)做如下判斷(條件從上往下依次判斷):
存儲(chǔ)日志(日志同步過(guò)程)
leader會(huì)將commit index置為0 --> 大部分follower將commitindex推進(jìn)之后 --> leader才會(huì)推進(jìn)自己的commit index --> leader代表整個(gè)系統(tǒng)推進(jìn)commit index
InstallSnapshot RPC
該rpc主要用于leader將集群的快照同步給其他節(jié)點(diǎn)。這里主要講一下快照的機(jī)制:
本節(jié)主要參考文章條分縷析 Raft 算法(續(xù)):日志壓縮和性能優(yōu)化
log過(guò)多就需要做快照,最初設(shè)計(jì) LogCabin 的時(shí)候沒(méi)有考慮日志壓縮,因此代碼上假定了如果 entry i 在日志中,那么 entry 1 到 i - 1 也一定在日志中。有了日志壓縮,這就不再成立了,前面的 entry 可能已經(jīng)被丟棄了。
和配置變化不同,不同的系統(tǒng)有不同的日志壓縮方式,取決于你的性能考量,以及基于硬盤還是基于內(nèi)存。日志壓縮的大部分責(zé)任都落在狀態(tài)機(jī)上。
不同的壓縮方法有幾個(gè)核心的共同點(diǎn):
memory-based 狀態(tài)機(jī)的快照的大部分工作是序列化內(nèi)存中的數(shù)據(jù)結(jié)構(gòu)。
快照的并發(fā)性
創(chuàng)建一個(gè)快照需要耗費(fèi)很長(zhǎng)時(shí)間,包括序列化和寫(xiě)入磁盤。**因此,序列化和寫(xiě)快照都要與常規(guī)操作并發(fā)進(jìn)行,避免服務(wù)不可用。**copy-on-write 技術(shù)允許進(jìn)行新的更新而不影響寫(xiě)快照。有兩個(gè)方法來(lái)實(shí)現(xiàn):
- 狀態(tài)機(jī)可以用不可變的(immutable)數(shù)據(jù)結(jié)構(gòu)來(lái)實(shí)現(xiàn)。因?yàn)闋顟B(tài)機(jī)命令不會(huì) in-place 的方式來(lái)修改狀態(tài)(通常使用追加的方式),快照任務(wù)可以引用之前狀態(tài)的并把狀態(tài)一致地寫(xiě)入到快照。
- 另外,也可以使用操作系統(tǒng)的 copy-on-write。例如,在 Linux 上可以使用 fork 來(lái)復(fù)制父進(jìn)程的整個(gè)地址空間,然后子進(jìn)程就可以把狀態(tài)機(jī)的狀態(tài)寫(xiě)出并退出,整個(gè)過(guò)程中父進(jìn)程都可以持續(xù)地提供服務(wù)。LogCabin中當(dāng)前使用的就是這種方法。
快照實(shí)現(xiàn)以及何時(shí)做快照
服務(wù)器需要決定什么時(shí)候做快照。太過(guò)頻繁地做快照,將會(huì)浪費(fèi)磁盤帶寬和其他資源;太不頻繁地做快照,則有存儲(chǔ)空間耗盡的風(fēng)險(xiǎn),并且重啟服務(wù)需要更長(zhǎng)的重放日志時(shí)間。
**一個(gè)簡(jiǎn)單的策略是設(shè)置一個(gè)閾值,當(dāng)日志大小超過(guò)閾值則做快照。**然而,這會(huì)導(dǎo)致對(duì)于小型狀態(tài)機(jī)時(shí)有著不必要的大日志。
一個(gè)更好的方法是引入快照大小和日志大小的對(duì)比,如果日志超過(guò)快照好幾倍,可能就需要做快照。但是在做快照之前計(jì)算快照的大小是困難并且繁重的,會(huì)引入額外負(fù)擔(dān)。所以使用前一個(gè)快照的大小是比較合理的行為,一旦日志大小超過(guò)之前的快照的大小乘以擴(kuò)展因子(expansion factor),服務(wù)器就做快照。
這個(gè)擴(kuò)展因子權(quán)衡空間和帶寬利用率。例如,擴(kuò)展因子為 4 的話會(huì)有 20% 的帶寬用于快照(每1byte 的快照寫(xiě)入有對(duì)應(yīng)的 4bytes 的 log 寫(xiě)入)和大約 6 倍的硬盤空間使用(舊的快照+日志+新的快照)。
快照仍然會(huì)導(dǎo)致 CPU 和磁盤的占用率突發(fā),可以增加額外的磁盤來(lái)減輕該現(xiàn)象。
**同時(shí),可以通過(guò)調(diào)度使得做快照對(duì)客戶端請(qǐng)求沒(méi)有影響。**服務(wù)器需要協(xié)調(diào)保證在某一時(shí)刻集群只有小部分成員集同時(shí)在做快照。由于 Raft 是多數(shù)派成員構(gòu)成的 commit,所以這樣就不會(huì)影響請(qǐng)求的提交了。當(dāng) Leader 想做快照的時(shí)候,首先要先下臺(tái),讓其他服務(wù)器選出另一個(gè) Leader 接替工作。如果這個(gè)方法充分地可行,就可能消除快照的并發(fā),服務(wù)器在快照期間其實(shí)是不可用的(這可能會(huì)造成集群的容錯(cuò)能力降低的問(wèn)題)。這是一個(gè)令人興奮的提升集群性能并降低實(shí)現(xiàn)機(jī)制的機(jī)會(huì)。(這里其實(shí)可以通過(guò)實(shí)現(xiàn)指定服務(wù)器做快照來(lái)優(yōu)化,braft 里就有提到這點(diǎn)。)
快照實(shí)現(xiàn)
根據(jù)log的實(shí)現(xiàn)方式不同(分為memory-based和disk-based),快照也有不同的實(shí)現(xiàn)方式
disk-based
對(duì)于幾十或上百 GB 的狀態(tài)機(jī),需要使用磁盤作為主要存儲(chǔ)。對(duì)于每一條記錄,當(dāng)其被提交并應(yīng)用到狀態(tài)機(jī)后,其實(shí)就可以被丟棄了,因?yàn)榇疟P已經(jīng)持久化存儲(chǔ)了,可以理解為每條日志就做了一個(gè)快照。
Disk-based 狀態(tài)機(jī)的主要問(wèn)題是,磁盤會(huì)導(dǎo)致性能不佳。在沒(méi)有寫(xiě)緩沖的情況下,每應(yīng)用一條命了都需要進(jìn)行一次或多次隨機(jī)磁盤寫(xiě)入,這會(huì)限制系統(tǒng)的整體吞吐量。
Disk-based 狀態(tài)機(jī)仍然需要支持向日志落后的 Follower 提供最新的快照,而寫(xiě)快照也要繼續(xù)提供服務(wù),所以仍然需要 copy-on-write 技術(shù)以在一定期間內(nèi)保持一個(gè)一致地快照傳輸。幸運(yùn)的是,磁盤總是被劃分為邏輯塊,因此在狀態(tài)機(jī)中實(shí)現(xiàn)應(yīng)該是直接的。基于磁盤的狀態(tài)機(jī)也可以依靠操作系統(tǒng)的支持,例如 Linux 的 LVM 也可以用來(lái)創(chuàng)建快照。或者是使用系統(tǒng)的COW支持,Linux的fork,或者是ZFS的Snapshot等。
memory-based
memory-based日志主要有Log-structured File System 或 LSM tree方式做快照
參考鏈接
- MIT 6.824 Raft 設(shè)計(jì)文檔
總結(jié)
以上是生活随笔為你收集整理的一致性协议raft详解(三):raft中的消息类型的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 一致性协议raft详解(二):安全性
- 下一篇: 一致性协议raft详解(四):raft在