基於 raft 協議的 RocketMQ DLedger 多副本日誌複製設計原理

  • 2019 年 10 月 11 日
  • 筆記

RocketMQ 日誌複製源碼分析文章:

1、源碼分析 RocketMQ DLedger(多副本) 之日誌追加流程

2、源碼分析 RocketMQ DLedger(多副本) 之日誌複製-上篇

3、源碼分析 RocketMQ DLedger(多副本) 之日誌複製-下篇

前面已經用源碼的手段對 RocketMQ 日誌複製的實現細節做了一個詳細的介紹,可能有不少讀者朋友們覺得源碼閱讀較為枯燥,看的有點雲里霧裡,本篇將首先梳理一下 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超時推送的消息發起重推,盡最大可能幫助從節點及時更新到主節點的數據。

更多問題,`歡迎大家留言與我一起探討。如果覺得文章對自己有些用處的話,麻煩幫忙點個【收藏】唄,謝謝。