我們做出了一個分散式註冊中心

螞蟻金服分散式架構發表於2021-07-27

開篇

這篇文章是基於SOFA Meetup合肥站的分享總結,主要針對於註冊中心的定位以及功能介紹,通過對螞蟻註冊中心發展史的分析,帶領大家瞭解,螞蟻的註冊中心是如何一步一步演變為現在的規模和特性的。

更多深入的技術細節,歡迎大家加入到SOFA和SOFARegistry的社群中,探尋結果。

註冊中心是什麼

服務發現 & 服務註冊

註冊中心簡單來說,是為了解決分散式場景下,服務之間互相發現的問題。

如下圖所示,服務A想要呼叫服務B的時候,需要知道B的地址在哪裡,如何解決這個問題?

一般來說,分為兩個點:

  1. 服務發現可以有一箇中心化的元件或者說是儲存,它承載了所有服務的地址,同時提供出來一個可供查詢和訂閱的能力,服務的消費方可以通過和這個中心化的儲存互動,獲取服務提供方的地址列表。
  2. 服務註冊:同樣是上文中中心化的元件,但是,這個時候的服務資訊可以有兩種措施
    1. 服務連線註冊中心,同時上報自身的服務以及後設資料(也是今天本文講述的重點)
    2. 有一個集中的控制面(control plane)將使用者定義的服務和IP的對映寫入註冊中心,例如AWS的CloudMap

呼叫流程

如上圖所示,就是目前一種主流的註冊中心模式,SOFARegistry和Nacos都是這種模式。

  1. 服務A,服務B通過SDK或者REST將自身的服務資訊上報給註冊中心
  2. 服務A需要呼叫服務B的時候,就對註冊中心發起請求,拉取和服務B相關的服務IP列表以及資訊
  3. 在獲取到服務B的列表之後,就可以通過自身定義的負載均衡演算法訪問服務B

心跳

心跳是註冊中心用於解決服務不可用時,及時拉出服務降低影響的預設方式,如下圖所示

  1. 服務B的一個節點斷網或是hang住,引發心跳超時;或是當機、斷鏈直接引發心跳失敗
  2. 註冊中心把問題節點從自身的儲存中拉出(這裡拉出根據具體實現:有的是直接刪除,有的是標記為不健康)
  3. 服務A收到註冊中心的通知,獲取到服務B最新的列表

DUBBO 註冊中心

下面通過DUBBO的例子,我們來看一下注冊中心是如何使用的,以及流程

首先,DUBBO在2.7和3.0中的配置略有不同,但是都是簡單易懂的,這裡都放上來

DUBBO-2.7

DUBBO-3.0

在RPC客戶端只需要配置一個註冊中心的地址即可,地址中包含了基礎三元素

  1. protocol(協議型別)比如,zookeeper
  2. host
  3. port

基於此,dubbo的註冊流程如下圖所示

  1. 服務的生產方通過DUBBO客戶端向註冊中心(Registry)發起註冊行為(register)
  2. 服務的消費方通過DUBBO客戶端訂閱資訊(subscribe)
  3. 註冊中心通過通知的方式,下發服務列表給服務消費方

註冊中心的本質

通過前文的講解,以及DUBBO元件的具體例子,我們大概可以歸納註冊中心的本質

“儲存” + “可運維”

  1. 一方面,註冊中心需要儲存能力去記錄服務的資訊,比如應用列表
  2. 另一方面,註冊中心在實踐過程中,需要提供必需的運維手段,比如關閉某一服務流量

螞蟻註冊中心編年史

史前時代

史前時代的螞蟻是相當久遠的架構,當時所有的服務部署在同一臺物理機上或者JVM上,服務之間不存在有跨機器呼叫的場景,這裡略過不表

硬負載時代

後來,為了解決應用之間的耦合帶來的部署難,運維難問題,我們對服務進行了拆分,拆分後的服務,遇到了一個問題,就是如何處理服務之間的呼叫關係,這個時候,螞蟻用了兩種硬負載 F5 或是 LVS。

通過簡單的4層代理,我們可以把服務部署在代理的後面,服務與服務之間通過代理互相訪問,達到了跨機呼叫的目的

第一代註冊中心 -- 硬負載到軟負載的演變

通過硬負載訪問的方式,一方面解決了服務之間互相呼叫的問題,部署架構也簡單易懂;另一方面,在業務快速增長之後,卻帶來了一定的問題:

  1. 單點的問題(所有呼叫都走F5的話,F5一旦掛了,很多服務會不可用)
  2. 容量問題(F5承載的流量太高,本身會到一個效能瓶頸)

這個時候,螞蟻引進了阿里集團的一款產品叫ConfigServer,作為註冊中心進行使用,這個註冊中心的架構就和開頭提到的架構很像了,服務之間可以通過IP直接訪問,而降低了對負載均衡產品的強依賴,減少了單點風險。

第二代註冊中心 -- ScaleUp?ScaleOut?It's a problem

但是,問題還在持續,那就是註冊中心,本身是一個單點,那麼,他就會繼續遇到上文中所說的兩個問題

  1. 單點風險(註冊中心本身是單機應用)
  2. 容量瓶頸(單臺註冊中心的連線數和儲存資料的容量是有限的)

解決的方式有兩種

  1. scale-up(淘寶):通過增加機器的配置,來增強容量以及扛連結能力;同時,通過主-備這樣的架構,來保障可用性
  2. scale-out(螞蟻):通過分片機制,將資料和連結均勻分佈在多個節點上,做到水平擴充;通過分片之後的備份,做到高可用

螞蟻和淘寶走了兩條不同的路,也推進了螞蟻后面演進出一套獨立的生態系統

螞蟻的演進架構如下,產生了兩種不同的應用節點

  1. session節點,專門用來抗連結使用,本身無狀態可以快速擴充套件,單機對資源的佔用很小
  2. data節點,專門用來儲存資料,通過分片的方式降低單個節點的儲存量,控制資源佔用

第五代註冊中心 -- Meta節點的誕生

上面的架構已經很符合目前主流的分散式架構了,但是在運維過程中,產生了一系列問題,比如

  1. 所有data都是分散式的,data之間的服務發現需要通過啟動時給定一個配置檔案,這樣就和標準運維脫鉤
  2. data節點的上下線需要去及時修改配置檔案,否則叢集重啟會受到影響
  3. 分散式儲存一致性問題,每次迭代釋出,需要鎖定paas平臺,防止節點變動帶來的不一致

所有這些問題的產生,我們發現可以引入一個後設資料管理中心(Meta)節點來,解決對data和session管理的問題,data和session通過4層負載或是7層負載對meta訪問即可.

對比業界的解決方案,都有類似的模型,比如HDFS的Name Node、Kafka依賴於ZK,Oceanbase依賴於RootServer 或者 配置中心Apollo依賴於Euraka。

Meta節點的出現,緩解了手工運維註冊中心的瓶頸,但是,依然沒有從根本上解決問題,那麼問題在哪裡?詳見下文分析。

第六代註冊中心 -- 面向運維的註冊中心

上文說道,Meta節點的出現,承接了Data以及Session之間服務發現的問題,但是,叢雲未測來講,還是有很多問題解決不了,比如

  1. Data節點的釋出在資料量大的前提下,依然是個痛點
  2. Session節點的新加節點上,可能很久都沒有流量

等等,對於這些問題,在SOFARegistry5.x的基礎上,我們快速迭代了6.0版本,主要是面向運維的註冊中心。

Data節點發布難的問題,說到底是一個影響範圍的問題,如何控制單一data節點發布或者掛掉對資料的影響面,是解決問題的本源,這裡我們採用了兩個措施

  1. 改進資料儲存演算法(consistent-hash -> hash-slot)
  2. 應用級服務發現

儲存演算法的演進

之前我們使用了一致性hash的演算法,如下圖所示,每一個節點承載一部分資料,通過是儲存進行hash運算,算出儲存內容的hash值,再計算出hash值落在哪一個data所負責的儲存區間,來儲存資料。

當data節點當機或者重啟時,由下一個data節點接收當機節點的資料以及資料的訪問支援。

這樣依賴,資料遷移的粒度只能以單個data節點所儲存的資料為單位,在資料量較大(單節點8G)的情況下,對資料的重建有一定的影響,而且,在data連續當機的情況下,可能存在資料丟失或是不一致的場景。

改進後的演算法,我們參考了Redis Cluster的演算法機制,使用hash slot進行資料分片

這樣,在data釋出過程中,可以控制資料的遷移以slot為單位(單個data節點多個slot,可配置)

同時,為了解決遷移或是當機期間,資料寫入不一致的場景,我們引入了資料回放的補償機制,data在promotion為slot的master之後,會主動地去和所有的session完成一次資料比對/校驗,增量同步新增資料

應用級服務發現

應用級服務發現是為了解決資料儲存量大的問題,因為篇幅原因,這裡略過不表

開源

SOFARegistry從專案早期就開始了開源的程式,與目前主流的註冊中心的對比如下

我們認為,註冊中心首先需要解決的是可用性的問題,所以,在分散式一致性的問題上,我們選擇了AP的模型,這點也和主流的註冊中心,例如Euraka以及Nacos保持一致的觀點。

其次,在效能方面,基於長連線的SOFARegistry擁有更短的推送延遲,相較於Nacos1.0的推送時延更短(Nacos1.0基於Long Polling的模型,Nacos2.0也使用了長連線的模型)

在協議方面,SOFARegistry使用了螞蟻開源協議棧:BOLT協議(類似於HTTP2.0)的流式協議,更加輕量級,同時協議本身的全雙工模式:無阻塞,大大提升了資源利用率。

FeatureConsulZookeeperEtcdEurekaNacosSOFARegistry
服務健康檢查定期healthcheck (http/tcp/script/docker)定期心跳保持會話(session) + TTL定期refresh(http)+TTL定期心跳+TTL;支援自定義healthCheck定期連結心跳+斷鏈定期連線心跳 + 斷鏈敏感
多資料中心支援---支援支援
Kv儲存服務支援支援支援-支援支援
一致性raftZABraft最終一致性最終一致(註冊中心) Raft(配置中心)最終一致性
capcpcpcpapap+cpap
使用介面(多語言能力)支援http和dns客戶端http/grpc客戶端/http客戶端(多語言) http客戶端(java)
watch支援全量/支援long polling支援支援long polling不支援(client定期fetch)支援支援(服務端推送)
安全acl/httpsaclhttps支援-httpsacl
spring cloud整合支援支援支援支援支援支援

和大家所熟知的Nacos對比,我們在金融級和分散式(儲存量級)上具有很大優勢,易用性和雲原生方面,目前還在追趕

歡迎加入我們

一個人可以走得很快,但一群人可以走的更遠 -- 題記

SOFARegistry是一個開源專案,也是開源社群SOFA重要的一環,我們希望用社群的力量推動SOFARegistry的前進,而不是隻有螞蟻的工程師去開發。我們在今年也啟動了兩個專案,用於支援更多的開發者參與進來:

  1. Trun-Key Project (開箱即用計劃):github.com/sofastack/s…
  2. Deep-Dive Project(深入淺出計劃):github.com/sofastack/s…

計劃目前還處在初期階段,歡迎大家加入進來,可以幫助我們解決一個issue,或是寫一篇文件,都可以更好地幫助社群,幫助自己去成長。

本週推薦閱讀

更多文章請掃碼關注“金融級分散式架構”公眾號

相關文章