技術分享| 分散式系統中服務註冊發現元件的原理及比較

語言: CN / TW / HK

背景

在分散式架構的系統中,服務發現簡單來講就是通過服務名找到提供服務的例項地址和埠,主要用於解決如何獲取服務例項地址問題。

隨著容器技術的興起,服務叢集部署在系統各處,服務之間的遠端呼叫都需要通過服務發現來實現。服務註冊發現是分散式系統中不可或缺的關鍵元件,常用於構建服務註冊發現解決方案的開源框架如ZooKeeper、Etcd、Consul。下面主要介紹一下如何基於ZooKeeper、Etcd、Consul構建服務註冊發現方案並進行一下對比。

一個標準的服務註冊發現架構主要有三部分組成,分別是服務註冊中心、服務消費者、服務提供者,架構圖如下所示:

服務註冊中心是服務發現的核心元件,其本質上是一個服務名和服務例項地址對映集合,除了提供服務註冊發現,服務記錄,動態管理服務等功能外,還需要具備如下能力:

  • 容錯(Fault Tolerance):服務註冊中心儲存了分散式系統中所有服務名與服務例項地址對映,一旦故障必將導致整個系統不可用,是整個分散式系統核心,必須具備高可用性。
  • 服務健康檢查(Service Health Check):服務註冊中心必須要能及時發現故障例項並將其登出以防止被錯誤訪問。
  • 監視器(Watcher):服務註冊中心必須具備及時通知服務呼叫者服務例項註冊或登出的能力,以便服務呼叫者及時採取措施。

註冊/登出服務例項一般有兩種選擇:

  1. 服務例項自己註冊即Self-Registration模式,在服務例項啟動成功後主動將自己註冊到服務註冊中心,這種方法好處是架構簡單但需要為服務用到的每種程式語言實現註冊程式碼;
  2. 通過其他元件來註冊服務例項即Thrid-party Registration模式,例如使用一個獨立Agent通過輪詢或監聽事件去跟蹤執行的服務例項變化進行註冊或登出,好處是服務例項與服務註冊中心解耦但引入第三方元件增加了架構複雜性。

服務註冊發現元件方案

ZooKeeper

ZooKeeper提供分散式協調服務,在分散式系統中常被用於配置管理、名字服務、分散式鎖及組管理,通常執行在一組節點上實現容錯(當執行在n個節點上時能容忍n/2個節點同時故障)。

如何通過ZooKeeper來實現服務發現?

ZooKeeper使用臨時節點(ephemeral node)來實現服務註冊和基本的健康檢查功能。每當服務例項啟動就會在ZooKeeper中註冊一個臨時節點,而當服務例項故障或下線該臨時節點會被ZooKeeper自動刪除,如果有其他服務依賴這個服務可以設定監聽該服務例項對應的臨時節點,當臨時節點被刪除時,依賴該服務的其他服務會獲得通知。依賴ZooKeeper自身的高可用及臨時節點提供的健康檢查和監聽機制來實現具備容錯能力的服務發現機制。

元件特點

從ZooKeeper的資料結構特點看,並不是基於服務註冊而設計的,ZooKeeper提供的名稱空間與檔案系統的名稱空間非常相似,在資料結構上高度抽象為K-V格式,十分通用。

ZooKeeper元件支援節點短暫存在,只要建立znode的會話處於活動狀態,這些znode就會存在,會話結束時,將刪除znode。Dubbo框架正是基於這個特點,服務啟動往Zookeeper註冊的就是臨時節點,需要定時發心跳到ZooKeeper來續約節點,並允許服務下線時,將ZooKeeper上相應的節點刪除,同時ZooKeeper使用ZAB協議雖然保證了資料的強一致性。

實際開發過程中,建議使用Apache Curator來替代Zookeeper原生客戶端庫,Apache Curator通過封裝ZooKeeper原生API,提供更高抽象層次API讓ZooKeeper使用起來更加容易和可靠,而且提供專用於實現服務發現的API。

Etcd

Etcd是一個基於Raft共識演算法具備線性強一致性(linearizable)的Key-Value儲存系統,可以為每個Key設定TTL(time to live),當TTL過後相應Key會自動過期失效。

基於Etcd構建服務發現解決方案將Etcd作為服務註冊中心,服務例項註冊就是在Etcd中構建一個Key-Value記錄,由服務例項自身或代理負責設定並定期更新其關聯Key的TTL,如果服務例項故障其對應Key就會在TTL之後過期失效,相當於將該故障服務例項登出,通過定時心跳以達到監控健康狀態的效果。而且Etcd提供監聽機制,允許為Key設定監聽器當該Key發生變化時,監聽器能及時獲取通知。

Etcd自身的高可用特性,基於TTL提供基本的服務健康檢查,基於監聽機制及時感知服務例項變化,使Etcd成為微服務架構中常用服務發現解決方案。

Consul

Consul是一個成熟的服務發現解決方案。其核心是一個基於Raft共識演算法具備線性強一致性的Key-Value儲存系統作為服務註冊中心,並提供代理(Agent)機制一方面用於協調服務註冊,一方面提供服務健康檢查。代理(Agent)會在每個執行服務的節點上啟動,獲取節點地址並將該服務例項註冊到服務註冊中心。

架構上Consul包括兩類元件:Server、Agent,服務註冊資訊儲存在Server上,通過Raft共識演算法保證多個Server間資料線性強一致,保證服務註冊中心高可用;將所有Agent作為叢集節點,使用Gossip協議進行組關係管理和故障探測,當有Agent加入(啟動)或離開(故障)叢集時其他Agent會得到通知,實現服務健康檢查和監視功能。

Gossip協議常用於叢集組關係管理和故障檢測,每個節點都通過一個或多個引導節點加入叢集,引導節點有叢集中所有節點列表,每個節點都從自己所知節點列表中隨機選擇一組節點週期性地傳送多播訊息,最終叢集中所有節點都能知道其他節點。這個過程看起來很神奇,實際上Gossip協議能在幾秒內將訊息傳遍有上百節點的叢集。Akka、Riak、Cassandra都使用Gossip協議維護叢集成員列表和故障探測。

總結

文章總結了分散式系統中常用的服務註冊發現解決方案,基於Zookeeper、Etcd、Consul框架方案核心思想是通過一組例項(3個或者5個)提供線性強一致性(Linearizable)分散式高可用Key-Value儲存服務,將Key-Value儲存作為服務註冊中心,當相關Key發生變化時監視器能及時通知客戶端,通知機制配合服務健康檢查當有服務例項啟動或故障時客戶端能及時感知服務拓撲變化以實現智慧路由,從實現方式上看它們可以看作是中心化的服務發現方案。

總體比較,Consul 提供了原生的分散式鎖、健康檢查、服務發現機制支援,讓業務可以更省心,同時也對多資料中心進行了支援; 當然 Etcd 和 ZooKeeper 也都有相應的庫,也能很好的進行支援,但是這兩者不支援多資料中心;

ZooKeeper 在 Java 業務中選型使用的較多,Etcd 因為是 go 語言開發的,所以如果本身就是 go 的技術棧,使用這個也是個不錯的選擇,Consul 在國外應用比較多,中文文件及實踐案例相比 etcd 較少。

此外Consul和Etcd都非常適合容器環境,因為Docker容器啟動、停止都會發送事件(Event),基於事件通知機制非常便於將服務例項從Consul或Etcd上註冊、登出。

參考文件

服務發現框架選型: Consul、Zookeeper還是etcd ?