服務註冊中心Eureka vs Zookeeper vs Consul
前言
在現在雲計算和大數據快速發展的今天,業務快速發展和變化。我們以前的單一應用難以應對這種快速的變化, 因此我們需要將以前單一的大應用不斷進行差分,分成若幹微小的應用或者服務,這就是微服務的思想。但是當我們對服務進行拆分之後,我們將又面臨新的問題。服務之間該如何調用?直接調用?當服務很少只有幾個的時候你可能感覺直接調用是最高效和便捷的,但是當服務多達上千個呢?這時我們又該怎麽來管理我們的調用關系呢?
服務間直接調用
服務註冊中心解決的問題
服務註冊中心主要解決兩個關鍵問題:服務註冊和服務發現。
- 服務註冊:服務實例將自身服務信息註冊到註冊中心。這部分服務信息包括服務所在主機IP和提供服務的Port,以及暴露服務自身狀態以及訪問協議等信息。
- 服務發現:服務實例請求註冊中心獲取所依賴服務信息。服務實例通過註冊中心,獲取到註冊到其中的服務實例的信息,通過這些信息去請求它們提供的服務。
除了這兩個核心功能之外,一般服務註冊中心還需要監控服務實例的運行狀態,負載均衡等問題。
- 監控:服務實例一直處於動態的變化中,因此我們需要監控服務實例的健康狀況,從註冊中心剔除無用的服務。一般實現心跳連接等。
- 負載均衡:在一個服務有多個實例的情況下,我們需要根據負載均衡策略正確處理請求。
服務註冊中心模式
CAP原則
這裏不得不提一下,我們知道分布式裏一個重要的理論,那就是CAP原則。指的是在一個分布式系統中,Consistency(一致性)、Availability(可用性)、Partition Tolerance(分區容錯性),不能同時成立。
- 一致性:它要求在同一時刻點,分布式系統中的所有數據備份都處於同一狀態。
- 可用性:在系統集群的一部分節點宕機後,系統依然能夠響應用戶的請求。
- 分區容錯性:在網絡區間通信出現失敗,系統能夠容忍。
CAP原則
一般來講,基於網絡的不穩定性,分布容錯是不可避免的,所以我們默認CAP中的P總是成立的。一致性的強制數據統一要求,必然會導致在更新數據時部分節點處於被鎖定狀態,此時不可對外提供服務,影響了服務的可用性,反之亦然。因此一致性和可用性不能同時滿足。在註冊中心的發展上面,一直有兩個分支:一個就是 CP 系統,追求數據的強一致性。還有一個是 AP 系統,追求高可用與最終一致。我們接下來介紹的服務註冊和發現組件中,Eureka滿足了其中的AP,Consul和Zookeeper滿足了其中的CP。
Eureka
Eureka是在Java語言上,基於Restful Api開發的服務註冊與發現組件,由Netflix開源。遺憾的是,目前Eureka僅開源到1.X版本,2.X版本已經停止開發。Eureka由兩個組件組成:Eureka服務器和Eureka客戶端。Eureka服務器用作服務註冊服務器。Eureka客戶端是一個java客戶端,用來簡化與服務器的交互、作為輪詢負載均衡器,並提供服務的故障切換支持。Netflix在其生產環境中使用的是另外的客戶端,它提供基於流量、資源利用率以及出錯狀態的加權負載均衡。Spring Cloud 封裝了 Netflix 公司開發的 Eureka 模塊來實現服務註冊和發現。Eureka 采用了 C-S 的設計架構。Eureka Server 作為服務註冊功能的服務器,它是服務註冊中心。而系統中的其他微服務,使用 Eureka 的客戶端連接到 Eureka Server,並維持心跳連接。這樣系統的維護人員就可以通過 Eureka Server 來監控系統中各個微服務是否正常運行。Spring Cloud 的一些其他模塊(比如Zuul)就可以通過 Eureka Server 來發現系統中的其他微服務,並執行相關的邏輯。
上圖來自Eureka官方的架構圖,大致描述了Eureka集群的工作過程。圖中包含的組件非常多,可能比較難以理解,我們用通俗易懂的語言解釋一下:
- Application Service: 作為Eureka Client,扮演了服務的提供者,提供業務服務,向Eureka Server註冊和更新自己的信息,同時能從Eureka Server的註冊表中獲取到其他服務的信息。
- Eureka Server:扮演服務註冊中心的角色,提供服務註冊和發現的功能,每個Eureka Cient向Eureka Server註冊自己的信息,也可以通過Eureka Server獲取到其他服務的信息達到發現和調用其他服務的目的。
- Application Client:作為Eureka Client,扮演了服務消費者,通過Eureka Server獲取到註冊到上面的其他服務的信息,從而根據信息找到所需的服務發起遠程調用。
- Replicate: Eureka Server中的註冊表信息的同步拷貝,保持不同的Eureka Server集群中的註冊表中的服務實例信息的一致性。提供了數據的最終一致性。
- Make Remote Call: 服務之間的遠程調用。
- Register: 註冊服務實例,Client端向Server端註冊自身的元數據以進行服務發現。
- Renew:續約,通過發送心跳到Server維持和更新註冊表中的服務實例元數據的有效性(默認周期為30秒)。當在一定時長內Server沒有收到Client的心跳信息(默認90秒),將默認服務下線,將服務實例的信息從註冊表中刪除。
- Cancel:服務下線,Client在關閉時主動向Server註銷服務實例元數據,這時Client的的服務實例數據將從Server的註冊表中刪除。
最後我們需要註意的Eureka的緩存機制。Eureka Client會緩存Eureka Server中的信息。即使所有的Eureka Server節點都宕掉,服務消費者依然可以使用緩存中的信息找到服務提供者。Ereka Client客戶端默認每30秒發送一次請求來更新緩存的註冊信息。如果你並不是在Spring Cloud環境下使用這些組件(Eureka, Ribbon),你的服務啟動後並不會馬上向Eureka註冊,而是需要等到第一次發送心跳請求時才會註冊。心跳請求的發送間隔也是30s。(Spring Cloud對此做了修改,服務啟動後會馬上註冊)
Zookeeper
ZooKeeper 是一個開源的分布式協調服務,由雅虎創建,是 Google Chubby 的開源實現。 分布式應用程序可以基於 ZooKeeper 實現諸如數據發布/訂閱、負載均衡、命名服務、分布式協 調/通知、集群管理、Master 選舉、配置維護,名字服務、分布式同步、分布式鎖和分布式隊列 等功能。為了保證高可用,最好是以集群形態來部署 ZooKeeper,這樣只要集群中大部分機器是可用的(能夠容忍一定的機器故障),那麽 ZooKeeper 本身仍然是可用的。客戶端在使用 ZooKeeper 時,需要知道集群機器列表,通過與集群中的某一臺機器建立 TCP 長連接來使用服務。客戶端使用這個 TCP 鏈接來發送請求、獲取結果、獲取監聽事件以及發送心跳包。如果這個連接異常斷開了,客戶端可以連接到另外的機器上。
上圖是ZooKeeper 官方提供的架構圖,圖中每一個 Server 代表一個安裝 ZooKeeper 服務的服務器。組成 ZooKeeper 服務的服務器都會在內存中維護當前的服務器狀態,並且每臺服務器之間都互相保持著通信。集群間通過 Zab 協議(Zookeeper AtomicBroadcast)來保持數據的一致性。對於來自客戶端的每個更新請求,ZooKeeper 都會分配一個全局唯一的遞增編號。這個編號反應了所有事務操作的先後順序,應用程序可以使用 ZooKeeper 這個特性來實現更高層次的同步原語。這個編號也叫做時間戳—zxid(ZooKeeper Transaction Id)。
最典型集群模式:Master/Slave 模式(主備模式)。在這種模式中,通常 Master 服務器作為主服務器提供寫服務,其他的 Slave 服務器從服務器通過異步復制的方式獲取 Master 服務器最新的數據提供讀服務。但是,在 ZooKeeper 中沒有選擇傳統的 Master/Slave 概念,而是引入了Leader、Follower 和 Observer 三種角色。如下圖所示:
- ZooKeeper 集群中的所有機器通過一個 Leader 選舉過程來選定一臺稱為 “Leader” 的機器。
- Leader 既可以為客戶端提供寫服務又能提供讀服務。除了 Leader 外,Follower 和 Observer 都只能提供讀服務
- Server:Server中存在兩種類型:Follower和Observer。其中Follower接受客戶端的請求並返回結果(事務請求將轉發給Leader處理),並在選舉過程中參與投票;Observer與Follower功能一致,唯一的區別在於 Observer 機器不參與 Leader 的選舉過程,也不參與寫操作的“過半寫成功”策略,因此 Observer 機器可以在不影響寫性能的情況下提升集群的讀性能。
- Client:請求發起方,Server和Client之間可以通過長連接的方式進行交互。如發起註冊或者請求集群信息等。
- 集群間通過 Zab 協議(Zookeeper Atomic Broadcast)來保持數據的一致性。
Zab協議
ZAB(ZooKeeper Atomic Broadcast 原子廣播)協議是為分布式協調服務 ZooKeeper 專門設計的一種支持崩潰恢復的原子廣播協議。在 ZooKeeper 中,主要依賴 ZAB 協議來實現分布式數據一致性,基於該協議,ZooKeeper 實現了一種主備模式的系統架構來保持集群中各個副本之間的數據一致性。ZAB 協議包括兩種基本的模式,分別是崩潰恢復和消息廣播。
當整個服務框架在啟動過程中,或是當 Leader 服務器出現網絡中斷、崩潰退出與重啟等異常情況時,ZAB 協議就會進入恢復模式並選舉產生新的 Leader 服務器。當選舉產生了新的 Leader 服務器,同時集群中已經有過半的機器與該 Leader 服務器完成了狀態同步之後,ZAB 協議就會退出恢復模式。其中,所謂的狀態同步是指數據同步,用來保證集群中存在過半的機器能夠和 Leader 服務器的數據狀態保持一致。當集群中已經有過半的 Follower 服務器完成了和 Leader 服務器的狀態同步,那麽整個服務框架就可以進人消息廣播模式了。當一臺同樣遵守 ZAB 協議的服務器啟動後加入到集群中時,如果此時集群中已經存在一個 Leader 服務器在負責進行消息廣播。那麽新加入的服務器就會自覺地進人數據恢復模式:找到 Leader 所在的服務器,並與其進行數據同步,然後一起參與到消息廣播流程中去。正如上文介紹中所說的,ZooKeeper 設計成只允許唯一的一個 Leader 服務器來進行事務請求的處理。Leader 服務器在接收到客戶端的事務請求後,會生成對應的事務提案並發起一輪廣播協議。而如果集群中的其他機器接收到客戶端的事務請求,那麽這些非 Leader 服務器會首先將這個事務請求轉發給 Leader 服務器。
consul
Consul是HashiCorp公司推出的開源工具,基於Go語言開發的支持多數據中心分布式高可用的服務發布和註冊服務軟件,采用Raft算法保證服務的一致性,且支持健康檢查。因為通過Golang實現,因此具有天然可移植性(支持Linux、windows和Mac OS X);安裝包僅包含一個可執行文件,方便部署,與Docker等輕量級容器可無縫配合。Consul采用主從模式的設計,使得集群的數量可以大規模擴展,集群間通過RPC的方式調用(HTTP和DNS)。它的結構圖如下所示:
- Client:作為一個代理(非微服務實例),它將轉發所有的RPC請求到Server中。作為相對無狀態的服務,它不持有任何註冊信息。
- Server:作為一個具備擴展功能的代理,它將響應RPC查詢、參與Raft選舉、維護集群狀態和轉發查詢給Leader等。
- Leader-Server:一個數據中心的所有Server都作為Raft節點集合的一部分。其中Leader將負責所有的查詢和事務(如服務註冊),同時這些事務也會被復制到所有其他的節點。
- Data Center:數據中心作為一個私有的,低延遲和高帶寬的一個網絡環境。每個數據中心會存在Consul集群,一般建議Server是3-5臺(考慮到Raft算法在可用性和性能上取舍),而Leader只能唯一,Client的數量沒有限制,可以輕松擴展。Consul 通過 WAN 的 Gossip 協議,完成跨數據中心的同步;而且其他的產品則需要額外的開發工作來實現。
Raft算法
不同於Paxos算法直接從分布式一致性問題出發推導出來,Raft算法則是從多副本狀態機的角度提出,用於管理多副本狀態機的日誌復制。Raft實現了和Paxos相同的功能,它將一致性分解為多個子問題:Leader選舉(Leader election)、日誌同步(Log replication)、安全性(Safety)、日誌壓縮(Log compaction)、成員變更(Membership change)等。同時,Raft算法使用了更強的假設來減少了需要考慮的狀態,使之變的易於理解和實現。
Raft算法將Server分為三種類型:Leader、Follower和Candidate。Leader處理所有的查詢和事務,並向Follower同步事務。Follower會將所有的RPC查詢和事務轉發給Leader處理,它僅從Leader接受事務的同步。數據的一致性以Leader中的數據為準實現。在節點初始啟動時,節點的Raft狀態機將處於Follower狀態等待來來自Leader節點的心跳。如果在一定時間周期內沒有收到Leader節點的心跳,節點將發起選舉。Follower節點選舉時會將自己的狀態切換為Candidate,然後向集群中其它Follower節點發送請求,詢問其是否選舉自己成為Leader。當收到來自集群中過半數節點的接受投票後,節點即成為Leader,開始接收Client的事務處理和查詢並向其它的Follower節點同步事務。Leader節點會定時向Follower發送心跳來保持其地位。
Gossip協議
Gossip protocol 也叫 Epidemic Protocol (流行病協議),實際上它還有很多別名,比如:“流言算法”、“疫情傳播算法”等。Gossip 過程是由種子節點發起,當一個種子節點有狀態需要更新到網絡中的其他節點時,它會隨機的選擇周圍幾個節點散播消息,收到消息的節點也會重復該過程,直至最終網絡中所有的節點都收到了消息。這個過程可能需要一定的時間,由於不能保證某個時刻所有節點都收到消息,但是理論上最終所有節點都會收到消息,因此它是一個最終一致性協議。這個協議的作用就像其名字表示的意思一樣,非常容易理解,它的方式其實在我們日常生活中也很常見,比如電腦病毒的傳播,森林大火,細胞擴散等等。
為了表述清楚,我們先做一些前提設定
- Gossip 是周期性的散播消息,把周期限定為 1 秒
- 被感染節點隨機選擇 k 個鄰接節點(fan-out)散播消息,這裏把 fan-out 設置為 3,每次最多往 3 個節點散播。
- 每次散播消息都選擇尚未發送過的節點進行散播
- 收到消息的節點不再往發送節點散播,比如 A -> B,那麽 B 進行散播的時候,不再發給 A。
這裏一共有 16 個節點,節點 1 為初始被感染節點,通過 Gossip 過程,最終所有節點都被感染。
Gossip 的特點(優勢)
1)擴展性
網絡可以允許節點的任意增加和減少,新增加的節點的狀態最終會與其他節點一致。
2)容錯
網絡中任何節點的宕機和重啟都不會影響 Gossip 消息的傳播,Gossip 協議具有天然的分布式系統容錯特性。
3)去中心化
Gossip 協議不要求任何中心節點,所有節點都可以是對等的,任何一個節點無需知道整個網絡狀況,只要網絡是連通的,任意一個節點就可以把消息散播到全網。
4)一致性收斂
Gossip 協議中的消息會以一傳十、十傳百一樣的指數級速度在網絡中快速傳播,因此系統狀態的不一致可以在很快的時間內收斂到一致。消息傳播速度達到了 logN。
5)簡單
Gossip 協議的過程極其簡單,實現起來幾乎沒有太多復雜性。
Gossip 的缺陷
分布式網絡中,沒有一種完美的解決方案,Gossip 協議跟其他協議一樣,也有一些不可避免的缺陷,主要是兩個:
1)消息的延遲
由於 Gossip 協議中,節點只會隨機向少數幾個節點發送消息,消息最終是通過多個輪次的散播而到達全網的,因此使用 Gossip 協議會造成不可避免的消息延遲。不適合用在對實時性要求較高的場景下。
2)消息冗余
Gossip 協議規定,節點會定期隨機選擇周圍節點發送消息,而收到消息的節點也會重復該步驟,因此就不可避免的存在消息重復發送給同一節點的情況,造成了消息的冗余,同時也增加了收到消息的節點的處理壓力。而且,由於是定期發送,因此,即使收到了消息的節點還會反復收到重復消息,加重了消息的冗余。
Gossip協議是為了解決分布式環境下監控和事件通知的瓶頸。Gossip協議中的每個Agent會利用Gossip協議互相檢查在線狀態,分擔了服務器節點的心跳壓力,通過Gossip廣播的方式發送消息。所有的Agent都運行著Gossip協議。服務器節點和普通Agent都會加入這個Gossip集群,收發Gossip消息。每隔一段時間,每個節點都會隨機選擇幾個節點發送Gossip消息,其他節點會再次隨機選擇其他幾個節點接力發送消息。這樣一段時間過後,整個集群都能收到這條消息。基於Raft算法,Consul提供強一致性的註冊中心服務,但是由於Leader節點承擔了所有的處理工作,勢必加大了註冊和發現的代價,降低了服務的可用性。通過Gossip協議,Consul可以很好地監控Consul集群的運行,同時可以方便通知各類事件,如Leader選擇發生、Server地址變更等。
總結
下面是這三個服務註冊中心的特性對比, 沒有最好的服務中心, 只有最合適的,我們可以根據項目的實際情況來進行選擇。
服務註冊中心Eureka vs Zookeeper vs Consul