天天看點

我們做出了一個分布式注冊中心

開篇

這篇文章是基于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)的流式協定,更加輕量級,同時協定本身的全雙工模式:無阻塞,大大提升了資源使用率。

Feature Consul Zookeeper Etcd Eureka Nacos SOFARegistry
服務健康檢查 定期healthcheck (http/tcp/script/docker) 定期心跳保持會話(session) + TTL 定期refresh(http)+TTL 定期心跳+TTL;支援自定義healthCheck 定期連結心跳+斷鍊 定期連接配接心跳 + 斷鍊敏感
多資料中心 支援 -
Kv存儲服務
一緻性 raft ZAB 最終一緻性 最終一緻(注冊中心) Raft(配置中心)
cap cp ap ap+cp
使用接口(多語言能力) 支援http和dns 用戶端 http/grpc 用戶端/http 用戶端(多語言) http 用戶端(java)
watch支援 全量/支援long polling 支援long polling 不支援(client定期fetch) 支援(服務端推送)
安全 acl/https acl https支援 https
spring cloud內建

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

我們做出了一個分布式注冊中心

歡迎加入我們

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

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

  1. Trun-Key Project (開箱即用計劃): https://github.com/sofastack/sofa-registry/projects/5
  2. Deep-Dive Project(深入淺出計劃): https://github.com/sofastack/sofa-registry/projects/4

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

本周推薦閱讀

更多文章請掃碼關注“金融級分布式架構”公衆号

我們做出了一個分布式注冊中心