上一篇 源碼分析 RocketMQ DLedger(多副本) 之日志複制(傳播) ,可能有不少讀者朋友們覺得源碼閱讀較為枯燥,看的有點雲裡霧裡,本篇将首先梳理一下 RocketMQ DLedger 多副本關于日志複制的三個核心流程圖,然後再思考一下在異常情況下如何保證資料一緻性。
1、RocketMQ DLedger 多副本日志複制流程圖
1.1 RocketMQ DLedger 日志轉發(append) 請求流程圖

1.2 RocketMQ DLedger 日志仲裁流程圖
1.3 RocketMQ DLedger 從節點日志複制流程圖
2、RocketMQ DLedger 多副本日志複制實作要點
上圖是一個簡易的日志複制的模型:圖中用戶端向 DLedger 叢集發起一個寫請求,叢集中的 Leader 節點來處理寫請求,首先資料先存入 Leader 節點,然後需要廣播給它的所有從節點,從節點接收到 Leader 節點的資料推送對資料進行存儲,然後向主節點彙報存儲的結果,Leader 節點會對該日志的存儲結果進行仲裁,如果超過叢集數量的一半都成功存儲了該資料,主節點則向用戶端傳回寫入成功,否則向用戶端寫入寫入失敗。
接下來我們來探讨日志複制的核心設計要點。
2.1 日志編号
為了友善對日志進行管理與辨識,raft 協定為一條一條的消息進行編号,每一條消息達到主節點時會生成一個全局唯一的遞增号,這樣可以根據日志序号來快速的判斷資料在主從複制過程中資料是否一緻,在 DLedger 的實作中對應 DLedgerMemoryStore 中的 ledgerBeginIndex、ledgerEndIndex,分别表示目前節點最小的日志序号與最大的日志序号,下一條日志的序号為 ledgerEndIndex + 1 。
與日志序号還與一個概念綁定的比較緊密,即目前的投票輪次。
2.2 追加與送出機制
請思考如下問題,Leader 節點收到用戶端的資料寫入請求後,通過解析請求,提取資料部分,建構日志對象,并生成日志序号,用 seq 表示,然後存儲到 Leader 節點中,然後将日志廣播(推送)到其從節點,由于這個過程中存在網絡時延,如果此時用戶端向主節點查詢 seq 的日志,由于日志已經存儲在 Leader 節點中了,如果直接傳回給用戶端顯然是有問題的,那該如何來避免這種情況的發生呢?
為了解決上述問題,DLedger 的實作(應該也是 raft 協定的一部分)引入了已送出指針(committedIndex)。即當主節點收到用戶端請求時,首先先将資料存儲,但此時資料是未送出的,此過程可以稱之為追加,此時用戶端無法通路,隻有當叢集内超過半數的節點都将日志追加完成後,才會更新 committedIndex 指針,得以是資料能否用戶端通路。
一條日志要能被送出的充分必要條件是日志得到了叢集内超過半數節點成功追加,才能被認為已送出。
2.3 日志一緻性如何保證
從上文得知,一個擁有3個節點的 DLedger 叢集,隻要主節點和其中一個從節點成功追加日志,則認為已送出,用戶端即可通過主節點通路。由于部分資料存在延遲,在 DLedger 的實作中,讀寫請求都将由 Leader 節點來負責。那落後的從節點如何再次跟上叢集的步驟呢?
要重新跟上主節點的日志記錄,首先要知道的是如何判斷從節點已丢失資料呢?
DLedger 的實作思路是,DLedger 會按照日志序号向從節點源源不斷的轉發日志,從節點接收後将這些待追加的資料放入一個待寫隊列中。關鍵中的關鍵:從節點并不是從挂起隊列中處理一個一個的追加請求,而是首先查閱從節點目前已追加的最大日志序号,用 ledgerEndIndex 表示,然後嘗試追加 (ledgerEndIndex + 1)的日志,用該序号從代寫隊列中查找,如果該隊列不為空,并且沒有 (ledgerEndIndex + 1)的日志條目,說明從節點未接收到這條日志,發生了資料缺失。然後從節點在響應主節點 append 的請求時會告知資料不一緻,然後主節點的日志轉發線程其狀态會變更為COMPARE,将向該從節點發送COMPARE指令,用來比較主從節點的資料差異,根據比較的差異重新從主節點同步資料或删除從節點上多餘的資料,最終達到一緻。于此同時,主節點也會對PUSH逾時推送的消息發起重推,盡最大可能幫助從節點及時更新到主節點的資料。
更多問題,`歡迎大家留言與我一起探讨。如果覺得文章對自己有些用處的話,麻煩幫忙點個贊,謝謝。
推薦閱讀:RocketMQ 日志複制系列文章:
1、
源碼分析 RocketMQ DLedger 多副本存儲實作2、
源碼分析 RocketMQ DLedger(多副本) 之日志追加流程3、
原文釋出時間為:2019-10-02
本文作者:丁威,《RocketMQ技術内幕》作者。
本文來自
中間件興趣圈,了解相關資訊可以關注
。