海量數據下的註冊中心 - SOFARegistry 架構介紹

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

SOFAStack
Scalable Open Financial Architecture Stack 是螞蟻金服自主研發的金融級分佈式架構,包含了構建金融級雲原生架構所需的各個組件,是在金融場景裏錘鍊出來的最佳實踐。 

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

GitHub 地址:https://github.com/sofastack/sofa-registry 

3 月 31 日,螞蟻金服正式開源了內部演進了近 10 年的註冊中心產品-SOFARegistry。先前的文章介紹了 SOFARegistry 的演進之路,而本文主要對 SOFARegistry 整體架構設計進行剖析,並着重介紹一些關鍵的設計特點,期望能幫助讀者對 SOFARegistry 有更直接的認識。

服務註冊中心是什麼

不可免俗地,先介紹一下服務註冊中心的概念。對此概念已經瞭解的讀者,可選擇跳過本節。
WX20190421-105201@2x.png

如上圖,服務註冊中心最常見的應用場景是用於 RPC 調用的服務尋址,在 RPC 遠程過程調用中,存在 2 個角色,一個服務發佈者(Publisher)、另一個是服務訂閱者(Subscriber)。Publisher 需要把服務註冊到服務註冊中心(Registry),發佈的內容通常是該 Publisher 的 IP 地址、端口、調用方式 (協議、序列化方式)等。而 Subscriber 在第一次調用服務時,會通過 Registry 找到相應的服務的 IP 地址列表,通過負載均衡算法從 IP 列表中取一個服務提供者的服務器調用服務。同時 Subscriber 會將 Publisher 的服務列表數據緩存到本地,供後續使用。當 Subscriber 後續再調用 Publisher 時,優先使用緩存的地址列表,不需要再去請求Registry。


WX20190421-110048@2x.png

如上圖,Subscriber 還需要能感知到 Publisher 的動態變化。比如當有 Publisher 服務下線時, Registry 會將其摘除,隨後 Subscriber 感知到新的服務地址列表後,不會再調用該已下線的 Publisher。同理,當有新的 Publisher 上線時,Subscriber 也會感知到這個新的 Publisher。

 

初步認識


WX20190425-001507@2x.png

在理解了常見的服務註冊中心的概念之後,我們來看看螞蟻金服的 SOFARegistry 長什麼樣子。如上圖,SOFARegistry 包含 4 個角色:

  1. Client

提供應用接入服務註冊中心的基本 API 能力,應用系統通過依賴客戶端 JAR 包,通過編程方式調用服務註冊中心的服務訂閱和服務發佈能力。

  1. SessionServer

會話服務器,負責接受 Client 的服務發佈和服務訂閱請求,並作爲一箇中間層將寫操作轉發 DataServer 層。SessionServer 這一層可隨業務機器數的規模的增長而擴容。

  1. DataServer

數據服務器,負責存儲具體的服務數據,數據按 dataInfoId 進行一致性 Hash 分片存儲,支持多副本備份,保證數據高可用。這一層可隨服務數據量的規模的增長而擴容。

  1. MetaServer

元數據服務器,負責維護集羣 SessionServer 和 DataServer 的一致列表,作爲 SOFARegistry 集羣內部的地址發現服務,在 SessionServer 或 DataServer 節點變更時可以通知到整個集羣。

產品特點


WX20190424-123703@2x.png
(圖片改編自 https://luyiisme.github.io/2017/04/22/spring-cloud-service-discovery-products )

首先放一張常見的服務註冊中心的特性對比,可以看出,在這些 Feature 方面,SOFARegistry 並不佔任何優勢。那麼,我們爲什麼還開源這樣的一個系統?SOFARegistry 開源的優勢是什麼?下面將着重介紹 SOFARegistry 的特點。

支持海量數據

大部分的服務註冊中心繫統,每臺服務器都是存儲着全量的服務註冊數據,服務器之間依靠一致性協議(如 Paxos/Raft/2PC 等)實現數據的複製,或者只保證最終一致性的異步數據複製。“每臺服務器都存儲着全量的服務註冊數據”,在一般規模下是沒問題的。但是在螞蟻金服龐大的業務規模下,服務註冊的數據總量早就超過了單臺服務器的容量瓶頸。

SOFARegistry 基於一致性 Hash 做了數據分片,每臺 DataServer 只存儲一部分的分片數據,隨數據規模的增長,只要擴容 DataServer 服務器即可。這是相對服務發現領域的其他競品來說最大的特點,詳細介紹見後面《如何支持海量數據》一節。

支持海量客戶端

SOFARegistry 集羣內部使用分層的架構,分別爲連接會話層(SessionServer)和數據存儲層(DataServer)。SessionServer 功能很純粹,只負責跟 Client 打交道,SessionServer 之間沒有任何通信或數據複製,所以隨着業務規模(即 Client 數量)的增長,SessionServer 可以很輕量地擴容,不會對集羣造成額外負擔。

相比之下,其他大多數的服務發現組件,如 eureka,每臺服務器都存儲着全量的數據,依靠 eurekaServer 之間的數據複製來傳播到整個集羣,所以每擴容 1 臺 eurekaServer,集羣內部相互複製的數據量就會增多一份。再如 Zookeeper 和 Etcd 等強一致性的系統,它們的複製協議(Zab/Raft)要求所有寫操作被複制到大多數服務器後才能返回成功,那麼增加服務器還會影響寫操作的效率。

秒級的服務上下線通知

對於服務的上下線變化,SOFARegistry 使用推送機制,快速地實現端到端的傳達。詳細介紹見後面《秒級服務上下線通知》一節。

接下來,我將圍繞這些特點,講解 SOFARegistry 的關鍵架構設計原理。

高可用

各個角色都有 failover 機制:

  • MetaServer 集羣部署,內部基於 Raft 協議選舉和複製,只要不超過 1/2 節點宕機,就可以對外服務。
  • DataServer 集羣部署,基於一致性 Hash 承擔不同的數據分片,數據分片擁有多個副本,一個主副本和多個備副本。如果 DataServer 宕機,MetaServer 能感知,並通知所有 DataServer 和 SessionServer,數據分片可 failover 到其他副本,同時 DataServer 集羣內部會進行分片數據的遷移。
  • SessionServer 集羣部署,任何一臺 SessionServer 宕機時 Client 會自動 failover 到其他 SessionServer,並且 Client 會拿到最新的 SessionServer 列表,後續不會再連接這臺宕機的 SessionServer。

數據模型

模型介紹


WX20190421-211911@2x.png


注意:這裏只列出核心的模型和字段,實際的代碼中不止這些字段,但對於讀者來說,只要理解上述模型即可。

  • 服務發佈模型(PublisherRegister)

    • dataInfoId:服務唯一標識,由、<分組 group>和<租戶 instanceId>構成,例如在 SOFARPC 的場景下,一個 dataInfoId 通常是 _com.sofastack.sofa.rpc.example.HelloService#@#SOFA#@#00001_,其中SOFA 是 group 名稱,00001 是租戶 id。group 和 instance 主要是方便對服務數據做邏輯上的切分,使不同 group 和 instance 的服務數據在邏輯上完全獨立。模型裏有 group 和 instanceId 字段,但這裏不額外列出來,讀者只要理解 dataInfoId 的含義即可。
  • zone:是一種單元化架構下的概念,代表一個機房內的邏輯單元,通常一個物理機房(Datacenter)包含多個邏輯單元(zone),更多內容可參考 異地多活單元化架構解決方案。在服務發現場景下,發佈服務時需指定邏輯單元(zone),而訂閱服務者可以訂閱邏輯單元(zone)維度的服務數據,也可以訂閱物理機房(datacenter)維度的服務數據,即訂閱該 datacenter 下的所有 zone 的服務數據。
  • dataList:服務註冊數據,通常包含“協議”、“地址”和“額外的配置參數”,例如 SOFARPC 所發佈的數據類似“_bolt://192.168.1.100:8080?timeout=2000_”。這裏使用 dataList,表示一個 PublisherRegister 可以允許同時發佈多個服務數據(但是通常我們只會發佈一個)。
  • 服務訂閱模型(SubscriberRegister)

    • dataInfoId:服務唯一標識,上面已經解釋過了。
  • scope: 訂閱維度,共有 3 種訂閱維度:zone、dataCenter 和 global。zone 和 datacenter 的意義,在上述有關“zone”的介紹裏已經解釋。global 維度涉及到機房間數據同步的特性,目前暫未開源。

關於“zone”和“scope”的概念理解,這裏再舉個例子。如下圖所示,物理機房內有 ZoneA 和 ZoneB 兩個單元,PublisherA 處於 ZoneA 裏,所以發佈服務時指定了 zone=ZoneA,PublisherB 處於 ZoneB 裏,所以發佈服務時指定了 zone=ZoneB;此時 Subscriber 訂閱時指定了 scope=datacenter 級別,因此它可以獲取到 PublisherA 和 PublisherB (如果 Subscriber 訂閱時指定了 scope=zone 級別,那麼它只能獲取到 PublisherA)。

WX20190421-214936@2x.png

服務註冊和訂閱的示例代碼如下 (詳細可參看官網的《客戶端使用》文檔):

// 構造發佈者註冊表,主要是指定dataInfoId和zone
PublisherRegistration registration = new PublisherRegistration("com.sofastack.test.demo.service");
registration.setZone("ZoneA");

// 發佈服務數據,dataList內容是 "10.10.1.1:12200?xx=yy",即只有一個服務數據
registryClient.register(registration, "10.10.1.1:12200?xx=yy");

發佈服務數據的代碼示例

// 構造訂閱者,主要是指定dataInfoId,並實現回調接口
SubscriberRegistration registration = new SubscriberRegistration("com.sofastack.test.demo.service",
                (dataId, userData) -> System.out
                        .println("receive data success, dataId: " + dataId + ", data: " + userData));
// 設置訂閱維度,ScopeEnum 共有三種級別 zone, dataCenter, global
registration.setScopeEnum(ScopeEnum.dataCenter);

// 將註冊表註冊進客戶端並訂閱數據,訂閱到的數據會以回調的方式通知
registryClient.register(registration);

訂閱服務數據的代碼示例

SOFARegistry 服務端在接收到“服務發佈(PublisherRegister)”和“服務訂閱(SubscriberRegister)”之後,在內部會彙總成這樣的一個邏輯視圖。


WX20190425-001752@2x.png
注意,這個樹形圖只是邏輯上存在,實際物理上 publisherList 和 subscriberList 並不是在同一臺服務器上,publisherList 是存儲在 DataServer 裏,subscriberList 是存儲在 SessionServer 裏。

業界產品對比

可以看出來,SOFARegistry 的模型是非常簡單的,大部分服務註冊中心產品的模型也就這麼簡單。比如 eureka 的核心模型是應用(Application)和實例(InstanceInfo),如下圖,1 個 Application 可以包含多個 InstanceInfo。eureka 和 SOFARegistry 在模型上的主要區別是,eureka 在語義上是以應用(Application)粒度來定義服務的,而SOFARegistry 則是以 dataInfoId 爲粒度,由於 dataInfoId 實際上沒有強語義,粗粒度的話可以作爲應用來使用,細粒度的話則可以作爲 service 來使用。基於以上區別,SOFARegistry 能支持以接口爲粒度的 SOFARPC 和 Dubbo,也支持以應用爲粒度的 SpringCloud,而 eureka 由於主要面向應用粒度,因此最多的場景是在springCloud 中使用,而 Dubbo 和 SOFAPRC 目前均未支持 eureka。

另外,eureka 不支持 watch 機制(只能定期 fetch),因此不需要像 SOFARegistry 這樣的 Subscriber 模型。

image.png
(圖片摘自 https://www.jianshu.com/p/0356b7e9bc42


最後再展示一下 SOFARPC 基於 Zookeeper 作爲服務註冊中心時,在 Zookeeper 中的數據結構(如下圖),Provider/Consumer 和 SOFARegistry 的 Publisher/Subscriber 類似,最大的區別是 SOFARegistry 在訂閱的維度上支持 scope(zone/datacenter),即訂閱範圍。


WX20190421-232335@2x.png

如何支持海量客戶端

從前面的架構介紹中我們知道,SOFARegistry 存在數據服務器(DataServer)和會話服務器(SessionServer)這 2 個角色。爲了突破單機容量瓶頸,DataServer 基於一致性 Hash 存儲着不同的數據分片,從而能支持螞蟻金服海量的服務數據,這是易於理解的。但 SessionServer 存在的意義是什麼?我們先來看看,如果沒有SessionServer的話,SOFARegistry 的架構長什麼樣子:

WX20190422-200639@2x.png

如上圖,所有 Client 在註冊和訂閱數據時,根據 dataInfoId 做一致性 Hash,計算出應該訪問哪一臺 DataServer,然後與該 DataServer 建立長連接。由於每個 Client 通常都會註冊和訂閱比較多的 dataInfoId 數據,因此我們可以預見每個 Client 均會與好幾臺 DataServer 建立連接。這個架構存在的問題是:“每臺 DataServer 承載的連接數會隨 Client 數量的增長而增長,每臺 Client 極端的情況下需要與每臺 DataServer 都建連,因此通過 DataServer 的擴容並不能線性的分攤 Client 連接數”。

講到這裏讀者們可能會想到,基於數據分片存儲的系統有很多,比如 Memcached、Dynamo、Cassandra、Tair 等,這些系統都也是類似上述的架構,它們是怎麼考慮連接數問題的?其實業界也給出了答案,比如 twemproxy,twitter 開發的一個 memcached/redis 的分片代理,目的是將分片邏輯放到 twemproxy 這一層,所有 Client 都直接和 twemproxy 連接,而 twemproxy 負責對接所有的 memcached/Redis,從而減少 Client 直接對memcached/redis 的連接數。twemproxy 官網也強調了這一點:“It was built primarily to reduce the number of connections to the caching servers on the backend”,如下圖,展示的是基於 twemproxy 的 redis 集羣部署架構。類似 twemproxy 的還有 codis,關於 twemproxy 和 codis 的區別,主要是分片機制不一樣,下節會再談及。

58db25961bad6 (1).jpg
(圖片摘自 http://www.hanzhicaoa.com/1.php?s=twemproxy redis) 


當然也有一些分佈式 KV 存儲系統,沒有任何連接代理層。比如 Tair (Alibaba 開源的分佈式 KV 存儲系統),只有 Client、DataServer、ConfigServer 這 3 個角色,Client 直接根據數據分片連接多臺 DataServer。但螞蟻金服內部在使用 Tair 時本身會按業務功能垂直劃分出不同的 Tair 集羣,所部署的機器配置也比較高,而且 Tair 的 Client 與 data server 的長連接通常在空閒一段時間後會關閉,這些都有助於緩解連接數的問題。當然,即便如此,Tair 的運維團隊也在時刻監控着連接數的總量。

經過上面的分析,我們明白了爲數據分片層(DataServer)專門設計一個連接代理層的重要性,所以 SOFARegistry 就有了 SessionServer 這一層。如圖,隨着 Client 數量的增長,可以通過擴容 SessionServer 就解決了單機的連接數瓶頸問題。

WX20190424-145609@2x.png

如何支持海量數據

面對海量數據,想突破單機的存儲瓶頸,唯一的辦法是將數據分片,接下來將介紹常見的有 2 種數據分片方式。

傳統的一致性 Hash 分片

傳統的一致性 Hash 算法,每臺服務器被虛擬成 N 個節點,如下圖所示(簡單起見虛擬份數 N 設爲 2 )。每個數據根據 Hash 算法計算出一個值,落到環上後順時針命中的第一個虛擬節點,即負責存儲該數據。業界使用一致性 Hash 的代表項目有 Memcached、Twemproxy 等。


 

想看完整文章內容:點擊這裏

原文出處:阿里雲大學開發者社區

文中涉及到的相關鏈接

歡迎加入,參與 SOFARegistry 源碼解析 

image.png


本文爲 SOFARegistry 的架構介紹,希望大家對 SOFARegistry 有一個初步的認識和了解。同時,我們開啓了《剖析 | SOFARegistry  實現原理》系列,會逐步詳細介紹各個部分的代碼設計和實現

如果有同學對以上某個主題特別感興趣的,可以留言討論,我們會適當根據大家的反饋調整文章的順序,謝謝大家關注 SOFA ,關注 SOFARegistry,我們會一直與大家一起成長。

歡迎提交 issue 和 PR:
SOFARegistry:https://github.com/sofastack/sofa-registry 

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