Categories
程式開發

螞蟻金服服務註冊中心數據一致性方案分析


SOFARegistry 是螞蟻金服開源的具有承載海量服務註冊和訂閱能力的、高可用的服務註冊中心,在支付寶/螞蟻金服的業務發展驅動下,近十年間已經演進至第五代。

本文為《剖析 | SOFARegistry 框架》第七篇,本篇作者明不二。 《剖析 | SOFARegistry 框架》系列由 SOFA 團隊和源碼愛好者們出品,項目代號:,文末包含往期系列文章。

GitHub 地址:

https://github.com/sofastack/sofa-registry

1 概述

在前面的文章已經做過介紹,與其他註冊中心相比,SOFARegistry 主要特點在於支持海量數據、支持海量客戶端、秒級的服務上下線通知以及高可用特性。本文將從如下幾個方面來講述 SOFARegistry 的一致性方案:

  • MetaServer 數據一致性

為支持高可用特性,對於 MetaServer 來說,存儲了 SOFARegistry 的元數據,為了保障 MetaServer 集群的一致性,其採用了 Raft 協議來進行選舉和復制。

  • SessionServer 數據一致性

為支持海量客戶端的連接,SOFARegistry 在客戶端與 DataServer 之間添加了一個 SessionServer 層,客戶端與 SessionServer 連接,避免了客戶端與 DataServer 之間存在大量連接所導致的連接數過多不可控的問題。客戶端通過 SessionServer 與 DataServer 連接的時候,Publisher 數據同時會緩存在 SessionServer 中,此時就需要解決 DataServer 與 SessionServer 之間數據一致性的問題。

  • DataServer 數據一致性

為支持海量數據,SOFARegistry 採用了一致性 Hash 來分片存儲 Publisher 數據,避免了單個服務器存儲全量數據時產生的容量瓶頸問題。而在這個模型中,每個數據分片擁有多個副本,當存儲註冊數的DataServer 進行擴容、縮容時,MetaServer 會把這個變更通知到DataServer 和SessionServer,數據分片會在集群內部進行數據遷移與同步,此時就出現了DataServer 內部數據的一致性問題。

2 MetaServer 數據一致性

MetaServer 在 SOFARegistry 中,承擔著集群元數據管理的角色,用來維護集群成員列表,可以認為是 SOFARegistry 註冊中心的註冊中心。當 SessionServer 和 DataServer 需要知道集群列表,並且需要擴縮容時,MetaServer 將會提供相應的數據。

螞蟻金服服務註冊中心數據一致性方案分析 1

圖1 MetaServer 內部結構
圖源自 《螞蟻金服服務註冊中心 MetaServer 功能介紹和實現剖析 | SOFARegistry 解析》

因為 SOFARegistry 集群節點列表數據並不是很多,因此不需要使用數據分片的方式在 MetaServer 中存儲。如圖 1 所示,集群節點列表存儲在 Repository 中,上面通過 Raft 強一致性協議對外提供節點註冊、續約、列表查詢等 Bolt 請求,從而保障集群獲得的數據是強一致性的。

Raft 協議

關於 Raft 協議算法,具體可以參考 The Raft Consensus Algorithm 中的解釋。在 SOFAStack 體系中,對於 Raft 協議有 SOFAJRaft 實現。

SOFAJRaft 源碼解析系列:https://www.sofastack.tech/tags/剖析-sofajraft-實現原理/

下面對 Raft 協議算法的原理進行簡要介紹。

Raft 協議由三個部分組成,領導人選舉(Leader Election)、日誌複製(Log Replication)、安全性(Safety)。

  • 領導人選舉

通過一定的算法選舉出領導人,用於接受客戶端請求,並且把指令追加到日誌中。

螞蟻金服服務註冊中心數據一致性方案分析 2

圖2 Raft 狀態機狀態轉換圖
圖源自 Understanding the Raft consensus algorithm: an academic article summary

  • 日誌複製

領導人接受到客戶端請求之後,把操作追加到日誌中,同時與其他追隨者同步消息,最終 Commit 日誌,並且把結果返回給客戶端。

螞蟻金服服務註冊中心數據一致性方案分析 3

圖3 複製狀態機
圖源自 Raft一致性算法筆記

  • 安全性

安全性保證了數據的一致性。

基於 Raft 協議的數據一致性保障

螞蟻金服服務註冊中心數據一致性方案分析 4

圖4 SOFARegistry 中的 Raft 存儲過程
圖源自 《螞蟻金服服務註冊中心 MetaServer 功能介紹和實現剖析 | SOFARegistry 解析》

如圖 4 所示,SOFARegistry 中的 Raft 協議數據存儲經歷瞭如上的一些流程。客戶端發起Raft 協議調用,進行數據註冊、續約、查詢等操作時,會通過動態代理實現ProxyHandler 類進行代理,通過RaftClient 把數據發送給RaftServer ,並且通過內部的狀態機Statemachine ,最終實現數據的操作,從而保證了MetaServer 內部的數據一致性。

3 SessionServer 數據一致性

SessionServer 在 SOFARegistry 中,承擔著會話管理及連接的功能。同時,Subscriber 需要通過 SessionServer 來訂閱 DataServer 的服務數據,Publisher 需要通過 SessionServer 來把服務數據發佈到 DataServer 中。

在這個場景下,SessionServer 作為中間代理層,緩存從 DataServer 中獲取的數據成了必然。 DataServer 的數據需要通過 SessionServer 推送到 Subscriber 中,觸發 SessionServer 推送的場景有兩個:一個是 Publisher 到 DataServer 的數據發生變化;另外一個是 Subscriber 有了新增。

而在實際的場景中,Subscriber 新增的情況更多,在這種場景下,直接把 SessionServer 緩存的數據推送到 Subscriber 中即可,能夠大大減輕 SessionServer 從 DataServer 獲取數據對 DataServer 的壓力。因此,這也進一步確認了在 SessionServer 緩存數據的必要性。

螞蟻金服服務註冊中心數據一致性方案分析 5

圖5 兩種場景的數據推送對比圖

SessionServer 與 DataServer 數據對比機制

當服務 Publisher 上下線或者斷連時,相應的數據會通過 SessionServer 註冊到 DataServer 中。此時,DataServer 的數據與 SessionServer 會出現短暫的不一致性。為了保障這個數據的一致性,DataServer 與 SessionServer 之間通過推和拉兩種方式實現了數據的同步。

  • 數據推送模式

    DataServer 在服務數據有變化時會主動通知到 SessionServer 中,此時 SessionServer 會比對兩者數據的版本號 version ,對比之後若需要更新數據,則會主動向 DataServer 獲取相應的數據。

  • 數據拉取模式

    SessionServer 會每隔一定的時間(默認 30s)主動向 DataServer 查詢所有 dataInfoId 的 version 信息,若發現有版本號有變化,則會進行相應的同步操作。

    • SessionServer 從 DataServer 同步數據:常規情況下,一般是 DataServer 的數據要比 SessionServer 更新,此時,當 SessionServer 發現數據版本號有變化時,會主動拉取 DataServer 的數據進行同步。注意,此時緩存的數據只與當前 SessionServer 管理的客戶端所訂閱的服務信息有關,並不會緩存全量的數據,而且容量也不允許;
    • DataServer 從SessionServer 同步數據:特殊情況下,DataServer 數據出現缺失,並且副本數據也出現問題之後,當SessionServer 與DataServer 數據進行版本號比對時,會觸發數據恢復操作,能夠把SessionServer 內存中所存儲的全量數據恢復到DataServer 中,實現了數據的反向同步與補償機制;
  • 數據的緩存方式

    SOFARegistry 中採用了 LoadingCache的數據結構來在 SessionServer 中緩存從 DataServer 中同步來的數據。每個cache 中的entry 都有過期時間,在拉取數據的時候可以設置過期時間(默認是30s),使得cache 定期去DataServer 查詢當前session 所有sub 的dataInfoId,對比如果session 記錄的最近推送version(見com.alipay.sofa.registry.server.session.store.SessionInterests#interestVersions )比DataServer 小,說明需要推送,然後SessionServer 主動從DataServer 獲取該dataInfoId 的數據(此時會緩存到cache 裡),推送給client。

    同時,當 DataServer 中有數據更新時,也會主動向 SessionServer 發請求使對應 entry 失效,從而促使 SessionServer 去更新失效 entry。

SessionServer 與 Subscriber 之間的數據一致性同步

當 SessionServer 的數據發生變更時,會與 Subscriber 之間進行數據同步,把變化的 dataInfoId 數據推送到 Subscriber 中,保證客戶端本地所緩存的數據與 SessionServer 中的一致。

4 DataServer 數據一致性

DataServer 在 SOFARegistry 中,承擔著核心的數據存儲功能。數據按 dataInfoId 進行一致性 Hash 分片存儲,支持多副本備份,保證數據高可用。這一層可隨服務數據量的規模的增長而擴容。

如果 DataServer 宕機,MetaServer 能感知,並通知所有 DataServer 和 SessionServer,數據分片可 failover 到其他副本,同時 DataServer 集群內部會進行分片數據的遷移。

DataServer 請求接收過程

在講解一致性之前,先講一下 DataServer 的啟動之後關於數據同步方面做了哪些事情。 DataServer 啟動之時,會啟動一個數據同步 Bolt 服務 openDataSyncServer ,進行相應的 DataServer 數據同步處理。

啟動 DataSyncServer 時,註冊瞭如下幾個 handler 用於處理 bolt 請求 :

螞蟻金服服務註冊中心數據一致性方案分析 6

圖5 DayaSyncServer 註冊的 Handler

  • getDataHandler

該 Handler 主要用於數據的獲取,當一個請求過來時,會通過請求中的 DataCenter 和 DataInfoId 獲取當前 DataServer 節點存儲的相應數據。

  • publishDataProcessor unPublishDataHandler

當有數據發布者 publisher 上下線時,會分別觸發 publishDataProcessor 或 unPublishDataHandler ,Handler 會往 dataChangeEventCenter 中添加一個數據變更事件,用於異步地通知事件變更中心數據的變更。

事件變更中心收到該事件之後,會往隊列中加入事件。此時 dataChangeEventCenter 會根據不同的事件類型異步地對上下線數據進行相應的處理。與此同時,DataChangeHandler 會把這個事件變更信息通過 ChangeNotifier 對外發布,通知其他節點進行數據同步。

  • notifyFetchDatumHandler

這是一個數據拉取請求,當該Handler 被觸發時,通知當前DataServer 節點進行版本號對比,若請求中數據的版本號高於當前節點緩存中的版本號,則會進行數據同步操作,保證數據是最新的。

  • notifyOnlineHandler

這是一個 DataServer 上線通知請求 Handler,當其他節點上線時,會觸發該 Handler,從而當前節點在緩存中存儲新增的節點信息。用於管理節點狀態,究竟是 INITIAL 還是 WORKING 。

  • syncDataHandler

節點間數據同步Handler,該Handler 被觸發時,會通過版本號進行比對,若當前DataServer 所存儲數據版本號含有當前請求版本號,則會返回所有大於當前請求數據版本號的所有數據,便於節點間進行數據同步。

  • dataSyncServerConnectionHandler

連接管理Handler,當其他DataServer 節點與當前DataServer 節點連接時,會觸發connect 方法,從而在本地緩存中註冊連接信息,而當其他DataServer 節點與當前節點斷連時,則會觸發disconnect 方法,從而刪除緩存信息,進而保證當前DataServer 節點存儲有所有與之連接的DataServer 節點。

最終一致性

SOFARegistry 在數據存儲層面採用了類似Eureka 的最終一致性的過程,但是存儲內容上和Eureka 在每個節點存儲相同內容特性不同,採用每個節點上的內容按照一致性Hash 數據分片來達到數據容量無限水平擴展能力。

SOFARegistry 是一個 AP 分佈式系統,表明了在已有條件 P 的前提下,選擇了 A 可用性。當數據進行同步時,獲取到的數據與實際數據不一致。但因為存儲的信息為服務的註冊節點,儘管會有短暫的不一致產生,但對於客戶端來說,大概率還是能從這部分數據中找到可用的節點,不會因為數據暫時的不一致對業務系統帶來致命性的傷害。

集群內部數據遷移過程

SOFARegistry 的 DataServer 選擇了“一致性 Hash分片”來存儲數據。在“一致性Hash分片”的基礎上,為了避免“分片數據不固定”這個問題,SOFARegistry 選擇了在DataServer 內存里以dataInfoId 的粒度記錄操作日誌,並且在DataServer 之間也是以dataInfoId 的粒度去做數據同步。

螞蟻金服服務註冊中心數據一致性方案分析 7

圖6 DataServer 之間進行異步數據同步

數據和副本分別分佈在不同的節點上,進行一致性Hash 分片,當時對主副本進行寫操作之後,主副本會把數據異步地更新到其他副本中,實現了集群內部不同副本之間的數據遷移工作。

5 總結

在分佈式系統的設計中,可用性、分區容錯性、一致性是我們必須進行權衡的選項,CAP 理論告訴我們,這三者中只能同時滿足兩個的要求。在設計分佈式系統時,如何進行權衡選擇,是擺在每個系統設計者面前的一個難題。

SOFARegistry 系統分為三個集群,分別是元數據集群 MetaServer、會話集群 SessionServer、數據集群 DataServer。複雜的系統有多個地方需要考慮到一致性問題,SOFARegistry 針對不同模塊的一致性需求也採取了不同的方案。對於 MetaServer 模塊來說,採用了強一致性的 Raft 協議來保證集群信息的一致性。對於數據模塊來說,SOFARegistry 選擇了 AP 保證可用性,同時保證了最終一致性。

SOFARegistry 的設計給了我們啟示,在設計一個多模塊的分佈式系統時,可以根據不同模塊的需求選擇不同的一致性方案,同時CAP 三者的權衡也需要結合系統不同模塊的目標作出合理的權衡,不必拘泥。

文中涉及的相關鏈接

6 SOFARegistryLab 系列閱讀

本文轉載自公眾號金融級分佈式架構(ID:Antfin_SOFA)。

原文鏈接

https://mp.weixin.qq.com/s/5GddbUUxOA1l8hXafLmAQw