1. 程式人生 > 其它 >服務發現之 Etcd VS Consul

服務發現之 Etcd VS Consul

在分散式微服務架構中,一個應用可能由一組職責單一化的服務組成。這時候就需要一個註冊服務的機制,註冊某個服務或者某個節點是可用的,還需要一個發現服務的機制來找到哪些服務或者哪些節點還在提供服務。

在實際應用中,通常還都需要一個配置檔案告訴我們一些配置資訊,比如資料連線的地址,redis 的地址等等。但很多時候,我們想要動態地在不修改程式碼的情況下得到這些資訊,並且能很好地管理它們。

有了這些需求,於是發展出了一系列的開源框架,比如 ZooKeeper, Etcd, Consul 等等。

這些框架一般會提供這樣的服務:

服務註冊

主機名,埠號,版本號,或者一些環境資訊。

服務發現

可以讓客戶端拿到服務端地址。

一個分散式的通用k/v儲存系統

基於Paxos演算法或者Raft演算法

領導者選舉(LeaderElection)

其它一些例子:

分散式鎖(Distributedlocking)

原子廣播(Atomicbroadcast)

序列號(Sequencenumbers)

我們都知道CAP是EricBrewer提出的分散式系統三要素:

強一致性(Consistency)

可用性(Availability)

分割槽容忍性(PartitionTolerance)

幾乎所有的服務發現和註冊方案都是 CP 系統,也就是說滿足了在同一個資料有多個副本的情況下,對於資料的更新操作達到最終的效果和操作一份資料是一樣的,但是在出現網路分割槽(分割槽間的節點無法進行網路通訊)這樣的故障的時候,在節點之間恢復通訊並且同步資料之前,一些節點將會無法提供服務(一些系統在節點丟失的情況下支援 stale reads )。

ZooKeeper 作為這類框架中歷史最悠久的之一,是由 Java 編寫。Java 在許多方面非常偉大,然而對於這種型別的工作還是顯得有些沉重,也正因為其複雜性和對新鮮事物的嚮往,我們第一時間就放棄了選擇它。

本文將從協議和應用層來比較 Etcd 和 Consul,並最終給出了筆者的偏好。

Etcd

Etcd 是一個使用 go 語言寫的分散式 k/v 儲存系統。考慮到它是 coreos 公司的專案,以及在 GitHub 上的 star 數量 (9000+),Google 著名的容器管理工具 Kuberbetes 就是基於 Etcd 的。我們最先考慮的就是它。

在介紹 Etcd 之前,我們需要了解一些基本的概念。我們知道在單臺伺服器單個程序中維護一個狀態是很容易的,讀寫的時候不會產生衝突。即便在多程序或者多執行緒環境中,使用鎖機制或者協程(coroutine)也可以讓讀寫有序地進行。但是在分散式系統中,情況就會複雜很多,伺服器可能崩潰,節點間的機器可能網路不通等等。所以一致性協議就是用來在一個叢集裡的多臺機器中維護一個一致的狀態。

很多的分散式系統都會採用 Paxos 協議,但是 Paxos 協議難以理解,並且在實際實現中差別比較大。所以 Etcd 選擇了 Raft 作為它的一致性協議。Raft 是 Diego Ongaro 和 John Ousterhout 在 ‘In Search of an Understandable Consensus Algorithm’ 中提出的。它在犧牲很少可用性,達到相似功能的情況下,對 Paxos 做了很大的優化,並且比 Paxos 簡單易懂很多。

它主要集中在解決兩個問題:

領導者選舉(LeaderElection)

Raft 先通過領導選舉選出一個 Leader,後續的一致性維護都由 Leader 來完成,這就簡化了一致性的問題。Raft 會保證一個時間下只會有一個 Leader,並且在超過一半節點投票的情況下才會被選為 Leader。當 Leader 掛掉的時候,新的 Leader 將會被選出來。

日誌複製(LogReplication)

為了維護狀態,系統會記錄下來所有的操作命令日誌。Leader 在收到客戶端操作命令後,會追加到日誌的尾部。然後 Leader 會向叢集裡所有其它節點發送AppendEntriesRPC請求,每個節點都通過兩階段提交來複制命令,這保證了大部分的節點都能完成。

在實際的應用中,一般 Etcd 叢集以 5 個或者 7 個為宜,可以忍受 2 個或者 3 個節點掛掉,為什麼不是越多越好呢?是出於效能的考慮,因為節點多了以後,日誌的複製會導致 CPU 和網路都出現瓶頸。

Etcd 的 API 比較簡單,可以對一個目錄或者一個 key 進行 GET,PUT,DELETE 操作,是基於 HTTP 的。Etcd 提供 watch 某個目錄或者某個 key 的功能,客戶端和 Etcd 叢集之間保持著長連線 (long polling)。基於這個長連線,一旦資料發生改變,客戶端馬上就會收到通知,並且返回的結果是改變後的值和改變前的值,這一點在實際應用中會很有用(這也是後面的 Consul 的槽點之一)。

Etcd 的 watch 和在一般情況下不會漏掉任何的變更。因為 Etcd 不僅儲存了當前的鍵值對,還儲存了最近的變更記錄,所以如果一個落後於當前狀態的 watch 還是可以通過遍歷歷史變更記錄來獲取到所有的更新。Etcd 還支援 CompareAndSwap 這個原子操作,首先對一個 key 進行值比較,只有結果一致才會進行下一步的賦值操作。利用這個特性就像利用 x86 的 CAS 實現鎖一樣可以實現分散式鎖。

在 Etcd 中有個 proxy 的概念,它其實是個轉發伺服器,啟動的時候需要指定叢集的地址,然後就可以轉發客戶端的請求到叢集,它本身不儲存資料。一般來說,在每個服務節點都會啟動一個 proxy,所以這個 proxy 也是一個本地 proxy,這樣服務節點就不需要知道 Etcd 叢集的具體地址,只需要請求本地 proxy。之前提到過一個 k/v 系統還應該支援 leader election,Etcd 可以通過 TTL (time to live) key 來實現。

Consul

Consul 和 Etcd 一樣也有兩種節點,一種叫 client (和 Etcd 的 proxy 一樣) 只負責轉發請求,另一種是 server,是真正儲存和處理事務的節點。

Consul 使用基於 Serf 實現的 gossip 協議來管理從屬關係,失敗檢測,事件廣播等等。gossip 協議是一個神奇的一致性協議,之所以取名叫 gossip 是因為這個協議是模擬人類中傳播謠言的行為而來。要傳播謠言就要有種子節點,種子節點每秒都會隨機向其它節點發送自己所擁有的節點列表,以及需要傳播的訊息。任何新加入的節點,就在這種傳播方式下很快地被全網所知道。這個協議的神奇就在於它從設計開始就沒想要資訊一定要傳遞給所有的節點,但是隨著時間的增長,在最終的某一時刻,全網會得到相同的資訊。當然這個時刻可能僅僅存在於理論,永遠不可達。

Consul 使用了兩個不同的 gossip pool,分別叫做 LAN 和 WAN,這是因為 Consul 原生支援多資料中心。在一個數據中心內部,LAN gossip pool 包含了這個資料中心所有的節點,包括 proxy 和 servers。WAN pool 是全域性唯一的,所有資料中心的 servers 都在這個 pool 中。這兩個 pool 的區別就是 LAN 處理的是資料中心內部的失敗檢測,事件廣播等等,而 WAN 關心的是跨資料中心的。除了 gossip 協議之外,Consul 還使用了 Raft 協議來進行 leader election,選出 leader 之後複製日誌的過程和 Etcd 基本一致。

回到應用層面上來說,Consul 更像是一個 full stack 的解決方案,它不僅提供了一致性 k/v 儲存,還封裝了服務發現,健康檢查,內建了 DNS server 等等。這看上去非常美好,簡直可以開箱即用。於是,我們初步選定了 Consul 作為我們的服務發現和動態配置的框架。但現實往往是冰冷的,在深入研究它的 API 之後發現了比較多的坑,可能設計者有他自己的考慮。

在使用獲取所有 services 的 API 的時候返回的只是所有服務的名字和 tag,如果想要每個服務的具體資訊,你還需要挨個去請求。這在客戶端就會十分不方便,因為在筆者看來,獲取所有服務的列表以及具體資訊應該是一個很常見的需求,並且請求的次數越少越好。

如果 watch 服務是否有變化,當值發生改變的時候,返回的結果居然是相當於重新讀取所有 services,沒有能夠體現出服務資訊的變化,這會導致客戶端很難進行更新操作。

健康檢查是 Consul 的內建功能,在註冊一個服務的時候可以加上自定義的健康檢查,這聽上去很不錯。但是如果你忘記給你某個服務加上健康檢查,那它在各種 API 的返回結果中變得難以預料。

結語

在折騰了數天之後,最終我們決定迴歸 Etcd,事實證明這個決定是正確的。我們基於 Etcd 實現了穩定的服務發現和動態配置功能,當然還有一些比較細節的問題沒有在文中闡述,歡迎一起探討。

轉載連結:https://www.jianshu.com/p/6160d414dd5e