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