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

語言: 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 ?