ZooKeeper、Consul、Eureka和新生的Nacos 都實現了注冊中心的功能。那么從哪些方面進行對比,進而選型呢?
1.數據模型:
注冊中心的核心數據是服務的名字和它對應的網絡地址,當服務注冊了多個實例時,我們需要對不健康的實例進行過濾或者針對實例的一些特征進行流量的分配,那么就需要在實例上存儲一些例如健康狀態、權重等屬性。隨着服務規模的擴大,漸漸的又需要在整個服務級別設定一些權限規則、以及對所有實例都生效的一些開關,於是在服務級別又會設立一些屬性。再往后,我們又發現單個服務的實例又會有划分為多個子集的需求,例如一個服務是多機房部署的,那么可能需要對每個機房的實例做不同的配置,這樣又需要在服務和實例之間再設定一個數據級別。
2.數據一致性:
數據一致性是分布式系統永恆的話題,Paxos協議的艱深更讓數據一致性成為程序員大牛們吹水的常見話題。不過從協議層面上看,一致性的選型已經很長時間沒有新的成員加入了。目前來看基本可以歸為兩家:一種是基於Leader的非對等部署的單點寫一致性,一種是對等部署的多寫一致性。
3.負載均衡:
負載均衡嚴格的來說,並不算是傳統注冊中心的功能。一般來說服務發現的完整流程應該是先從注冊中心獲取到服務的實例列表,然后再根據自身的需求,來選擇其中的部分實例或者按照一定的流量分配機制來訪問不同的服務提供者,因此注冊中心本身一般不限定服務消費者的訪問策略。Eureka、Zookeeper包括Consul,本身都沒有去實現可配置及可擴展的負載均衡機制,Eureka的負載均衡是由ribbon來完成的,而Consul則是由Fabio做負載均衡。
服務端的負載均衡,給服務提供者更強的流量控制權,但是無法滿足不同的消費者希望使用不同負載均衡策略的需求。而不同負載均衡策略的場景,確實是存在的。而客戶端的負載均衡則提供了這種靈活性,並對用戶擴展提供更加友好的支持。但是客戶端負載均衡策略如果配置不當,可能會導致服務提供者出現熱點,或者壓根就拿不到任何服務提供者。
4.健康檢查:
Zookeeper和Eureka都實現了一種TTL的機制,就是如果客戶端在一定時間內沒有向注冊中心發送心跳,則會將這個客戶端摘除。Eureka做的更好的一點在於它允許在注冊服務的時候,自定義檢查自身狀態的健康檢查方法。這在服務實例能夠保持心跳上報的場景下,是一種比較好的體驗,在Dubbo和SpringCloud這兩大體系內,也被培養成用戶心智上的默認行為。Nacos也支持這種TTL機制,不過這與ConfigServer在阿里巴巴內部的機制又有一些區別。Nacos目前支持臨時實例使用心跳上報方式維持活性,發送心跳的周期默認是5秒,Nacos服務端會在15秒沒收到心跳后將實例設置為不健康,在30秒沒收到心跳時將這個臨時實例摘除。
客戶端健康檢查和服務端健康檢查有一些不同的關注點。客戶端健康檢查主要關注客戶端上報心跳的方式、服務端摘除不健康客戶端的機制。而服務端健康檢查,則關注探測客戶端的方式、靈敏度及設置客戶端健康狀態的機制。從實現復雜性來說,服務端探測肯定是要更加復雜的,因為需要服務端根據注冊服務配置的健康檢查方式,去執行相應的接口,判斷相應的返回結果,並做好重試機制和線程池的管理。這與客戶端探測,只需要等待心跳,然后刷新TTL是不一樣的。同時服務端健康檢查無法摘除不健康實例,這意味着只要注冊過的服務實例,如果不調用接口主動注銷,這些服務實例都需要去維持健康檢查的探測任務,而客戶端則可以隨時摘除不健康實例,減輕服務端的壓力。
5.性能與容量:
雖然大部分用戶用到的性能不高,但是他們仍然希望選用的產品的性能越高越好。影響讀寫性能的因素很多:一致性協議、機器的配置、集群的規模、存量數據的規模、數據結構及讀寫邏輯的設計等等。在服務發現的場景中,我們認為讀寫性能都是非常關鍵的,但是並非性能越高就越好,因為追求性能往往需要其他方面做出犧牲。Zookeeper在寫性能上似乎能達到上萬的TPS,這得益於Zookeeper精巧的設計,不過這顯然是因為有一系列的前提存在。首先Zookeeper的寫邏輯就是進行K-V的寫入,內部沒有聚合;其次Zookeeper舍棄了服務發現的基本功能如健康檢查、友好的查詢接口,它在支持這些功能的時候,顯然需要增加一些邏輯,甚至棄用現有的數據結構;最后,Paxos協議本身就限制了Zookeeper集群的規模,3、5個節點是不能應對大規模的服務訂閱和查詢的。
6.易用性:
易用性也是用戶比較關注的一塊內容。產品雖然可以在功能特性或者性能上做到非常先進,但是如果用戶的使用成本極高,也會讓用戶望而卻步。易用性包括多方面的工作,例如API和客戶端的接入是否簡單,文檔是否齊全易懂,控制台界面是否完善等。對於開源產品來說,還有一塊是社區是否活躍。在比較Nacos、Eureka和Zookeeper在易用性上的表現時,我們誠邀社區的用戶進行全方位的反饋,因為畢竟在阿里巴巴集團內部,我們對Eureka、Zookeeper的使用場景是有限的。從我們使用的經驗和調研來看,Zookeeper的易用性是比較差的,Zookeeper的客戶端使用比較復雜,沒有針對服務發現的模型設計以及相應的API封裝,需要依賴方自己處理。對多語言的支持也不太好,同時沒有比較好用的控制台進行運維管理。
7.集群擴展性:
集群擴展性的另一個方面是多地域部署和容災的支持。當講究集群的高可用和穩定性以及網絡上的跨地域延遲要求能夠在每個地域都部署集群的時候,我們現有的方案有多機房容災、異地多活、多數據中心等。
首先是雙機房容災,基於Leader寫的協議不做改造是無法支持的,這意味着Zookeeper不能在沒有人工干預的情況下做到雙機房容災。在單機房斷網情況下,使機房內服務可用並不難,難的是如何在斷網恢復后做數據聚合,Zookeeper的單點寫模式就會有斷網恢復后的數據對賬問題。Eureka的部署模式天然支持多機房容災,因為Eureka采用的是純臨時實例的注冊模式:不持久化、所有數據都可以通過客戶端心跳上報進行補償。上面說到,臨時實例和持久化實例都有它的應用場景,為了能夠兼容這兩種場景,Nacos支持兩種模式的部署,一種是和Eureka一樣的AP協議的部署,這種模式只支持臨時實例,可以完美替代當前的Zookeeper、Eureka,並支持機房容災。另一種是支持持久化實例的CP模式,這種情況下不支持雙機房容災。
8.用戶擴展性:
在框架的設計中,擴展性是一個重要的設計原則。Spring、Dubbo、Ribbon等框架都在用戶擴展性上做了比較好的設計。這些框架的擴展性往往由面向接口及動態類加載等技術,來運行用戶擴展約定的接口,實現用戶自定義的邏輯。在Server的設計中,用戶擴展是比較審慎的。因為用戶擴展代碼的引入,可能會影響原有Server服務的可用性,同時如果出問題,排查的難度也是比較大的。設計良好的SPI是可能的,但是由此帶來的穩定性和運維的風險是需要仔細考慮的。在開源軟件中,往往通過直接貢獻代碼的方式來實現用戶擴展,好的擴展會被很多人不停的更新和維護,這也是一種比較好的開發模式。Zookeeper和Eureka目前Server端都不支持用戶擴展,一個支持用戶擴展的服務發現產品是CoreDNS。CoreDNS整體架構就是通過插件來串聯起來的,通過將插件代碼以約定的方式放到CoreDNS工程下,重新構建就可以將插件添加到CoreDNS整體功能鏈路的一環中。
所有產品都應該盡量支持用戶運行時擴展,這需要Server端SPI機制設計的足夠健壯和容錯。Nacos在這方面已經開放了對第三方CMDB的擴展支持,后續很快會開放健康檢查及負載均衡等核心功能的用戶擴展。目的就是為了能夠以一種解耦的方式支持用戶各種各樣的需求。