Talos 讀寫一致性

前文《萬億級消息背後:小米消息隊列的實踐》整體介紹了 Talos。

本文將深入介紹該系統設計中的關鍵問題—讀寫一致性。

Talos 消息隊列做爲一種特殊的存儲系統,其一致性包含兩方面:

  • 存儲層多副本數據一致性

  • 調度層處理邏輯的讀寫一致性

前者由 Talos 基於 HDFS 的存儲層來保障,本文將詳細展開調度層一致性邏輯。

Talos中的讀寫一致性問題

做爲一個消息隊列系統,需要保證消息的讀寫一致性。這需要滿足兩個方面:

  • 單條消息是連續的字節存放,不同消息的字節數據不能交錯存放,即消息內容一致性

  • 單 Partition 消息之間應當有序,寫入順序即是讀取順序,即順序一致性

Talos 在 HDFS 的數據存儲模型爲:每個 Partition 在 HDFS 中對應一個目錄,通過 append 方式追加消息到文件。Offset 代表此條消息在 Partition 中的位置,唯一且遞增。文件名是上一個文件最後一條消息的 Offset + 1,即上一個文件的 EndOffset + 1.

上述讀寫一致性需要滿足的兩個方面,由此可進一步具體化:

  • 同一時刻同一 Partition 的數據文件,只能有一個 Talos Server 寫入(否則文件中單條消息將不能完整連續的存儲)

  • 同一時刻同一 Partition 的數據目錄,只能有一個 Talos Server 寫入(否則同時寫多個文件,消息之間將喪失順序性)

綜上 Talos 的讀寫一致性需要滿足如下條件: 同一時刻同一 Partition 目錄,只允許一個 Talos Server 節點進行寫入。

Talos是如何解決的讀寫一致性問題

01

一致性哈希?

前文《Talos網卡負載優化:基於個性化一致性哈希的負載均衡》提到,Talos 使用一致性哈希來指導 Partition 調度,基於此所有節點在大部分時刻對於調度信息都具有相同視角。

但是由於收到 ZK 通知的時間不能完全一致,少數時刻的視角並不一致,所以依據一致性哈希並不能保證同一時刻同一 Partition 目錄只有一個 Talos Server 節點進行寫入請求。

一致性哈希的作用是在大部分時間統一了各節點調度視角,各節點僅在哈希結果爲應該接管某 Partition 時才參與其權限競爭,避免了無謂競爭造成的開銷浪費。

02

分佈式鎖?

爲了加強 Talos Server 節點與 Partition 的綁定關係,使用 TTL 的分佈式鎖機制給調度信息加鎖,Server 節點會在超時時間之內續約。獲得鎖之後 Server 節點對此 Partition 標記爲 Active 狀態,Active 狀態可以接受請求,反之則不能。

可見在分佈式鎖信息中,同一時刻 Partition 和 Server 節點唯一對應。那麼這種方式是否能滿足同一時刻同一 Partition 目錄只有一個 Talos Server 節點進行寫請求呢?否。
原因是:

  • 不能保證 Partition 的 Active 狀態與分佈式鎖信息的一致性:如果發生長時間 GC,續約間隔時間可能超過 TTL 時間,在 GC 完成和下次續約之間,可能出現實際已掉鎖但狀態爲 Active 的窗口,如下圖

  • 更不能保證寫請求和分佈式鎖信息的一致性:Server 在 Active 時接受寫請求,但寫請求進行時 Server 有可能會掉鎖

綜上,不能保證同一時刻同一 Partition 目錄只有一個 Talos Server 節點進行寫入。

分佈式鎖的作用是在一定時間粒度鎖定綁定關係,避免了時間粒度內的細微抖動觸發的遷移,使得服務進一步穩定,但是它無法解決節點假死或寫數據時掉鎖引發的腦裂問題。

03

Talos Fencing Process!

傳統解決腦裂問題的方法是 Fencing 機制,例如 HDFS 中 NameNode HA 的腦裂問題方案:NameNode 每次寫 Editlog 都需要傳遞一個 編號Epoch 給 JN,JN 會對比 Epoch,如果比自己保存的 Epoch 大或相同,則可以寫,JN 更新自己的 Epoch 到最新,否則拒絕操作。在切換時,Standby 轉換爲 Active 時,會把 Epoch + 1,這樣就能阻止之前的 NameNode 向 JN 寫入日誌

Talos 按照以下流程進行 Partition 的初始化恢復,以實現類似原理的 Fencing :

  • Load file list:加載該 Partition 目錄下的文件列表

  • Recover lease && Close:對加載到的最後一個文件,進行 recover lease && close 操作

  • Rotate new File:以最後一個文件的 EndOffet + 1 命名創建一個新的文件做爲接下來要進行寫入的文件

過程中任意一步失敗則恢復過程失敗,如需則從頭重新進行。成功之後才能進行寫操作。

如上過程可以滿足以下效果:

(1) Recover 過程之前的文件不會被再打開或者 append 寫

(2) 由於每創建一個新文件都會關閉相鄰的最後一個文件,遞推可知第三步時load到的文件都已經被 Recover lease && Close

(3) 新建的文件名是 load 到的文件的 EndOffet+1

這個過程能夠保證,同一時刻同一 Partition 目錄只有一個 Talos Server 節點進行寫入。以下將對其證明:
假設 Talos 集羣中,Server1 和 Server2 都要調度某 Partition

  • 由於(1),每個 Server 只寫 Recover 過程中或之後,由本節點新創建的文件,所以不存在兩個 Server 寫同一文件的情況

  • 是否會出現創建兩個不同文件從而同時寫該Partition目錄的情況呢?
    假設第m個文件的 Endoffset 是 endM,第n個文件的 Endoffset 是 endN,m<n。S1 加載到m個文件並創建了 endM+1,S2加載到n個文件並創建了 endN+1

    => 根據(2),m個文件都是 S2 已經加載到的且已經關閉的文件,則 endN >= endM+1

    => 則 endM+1 是在 S2 的 load 文件列表裏已經關閉了的文件,不能再次創建或寫入

    即不會出現創建兩個不同的文件從而同時寫該 Partition 目錄的情況

得證。

上述邏輯可以達到圖示效果,即不會出現兩個 Server節點 同時寫同一 Partition:

對比 HDFS 中 NameNode HA 的腦裂方案,兩者都在存儲層具有能判斷新的 Master 的信息,用以判斷在新的 Master 來臨時,剝奪掉舊 Master 的寫權利。

HDFS 在解決 Talos 讀寫一致性問題時的角色,相當於 JN 做 NameNode 與 standby NameNode 共享存儲時的角色。Talos Fencing 以最後一個文件的文件名做爲當前有效的 Epoch,創建 EndOffset+1 文件成功相當於獲取了新的 Epoch,創建新文件之前進行 Recover lease 相當於剝奪了舊的 Epoch 的權利。

這種機制防止了 Talos 分佈式節點寫 Partition 時的腦裂。

往期文章回顧

小米Talos GC性能調優實踐

小米流式平臺架構演進與實踐

Flink流式計算在節省資源方面的簡單分析

客官!在看一下唄~

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章