還在用 Zookeeper 作為注冊中心?小心坑死你!
大家好,我是樓仔呀。
這篇文章對 Zookeeper 的注冊中心原理再深入研究一下,主要學習它的設(shè)計思想。
直接上文章目錄:
1. 基本概念
1.1 什么是注冊中心?
注冊中心主要有三種角色:
- 服務(wù)提供者(RPC Server):在啟動時,向 Registry 注冊自身服務(wù),并向 Registry 定期發(fā)送心跳匯報存活狀態(tài)。
- 服務(wù)消費者(RPC Client):在啟動時,向 Registry 訂閱服務(wù),把 Registry 返回的服務(wù)節(jié)點列表緩存在本地內(nèi)存中,并與 RPC Sever 建立連接。
- 服務(wù)注冊中心(Registry):用于保存 RPC Server 的注冊信息,當 RPC Server 節(jié)點發(fā)生變更時,Registry 會同步變更,RPC Client 感知后會刷新本地 內(nèi)存中緩存的服務(wù)節(jié)點列表。
最后,RPC Client 從本地緩存的服務(wù)節(jié)點列表中,基于負載均衡算法選擇一臺 RPC Sever 發(fā)起調(diào)用。
1.2 注冊中心需要實現(xiàn)功能
根據(jù)注冊中心原理的描述,注冊中心必須實現(xiàn)以下功能,偷個懶,直接貼幅圖:
2. ZK 注冊中心原理
Zookeeper 可以充當一個服務(wù)注冊表(Service Registry),讓多個服務(wù)提供者形成一個集群,讓服務(wù)消費者通過服務(wù)注冊表獲取具體的服務(wù)訪問地址(Ip + 端口)去訪問具體的服務(wù)提供者。
2.1 ZK 注冊流程
每當一個服務(wù)提供者部署后都要將自己的服務(wù)注冊到 Zookeeper 的某一路徑上: /{service}/{version}/{ip:port} 。
比如我們的 HelloWorldService 部署到兩臺機器,那么 Zookeeper 上就會創(chuàng)建兩條目錄:
- /HelloWorldService/1.0.0/100.19.20.01:16888
- /HelloWorldService/1.0.0/100.19.20.02:16888
在 Zookeeper 中,進行服務(wù)注冊,實際上就是在 Zookeeper 中創(chuàng)建了一個 znode 節(jié)點,該節(jié)點存儲了該服務(wù)的 IP、端口、調(diào)用方式(協(xié)議、序列化方式)等。
該節(jié)點承擔著最重要的職責,它由服務(wù)提供者(發(fā)布服務(wù)時)創(chuàng)建,以供服務(wù)消費者獲取節(jié)點中的信息,從而定位到服務(wù)提供者真正網(wǎng)絡(luò)拓撲位置以及得知如何調(diào)用。
RPC 服務(wù)注冊/發(fā)現(xiàn)過程簡述如下:
服務(wù)提供者啟動時,會將其服務(wù)名稱,IP 地址注冊到配置中心。
服務(wù)消費者在第一次調(diào)用服務(wù)時,會通過注冊中心找到相應的服務(wù)的 IP地址列表,并緩存到本地,以供后續(xù)使用。當消費者調(diào)用服務(wù)時,不會再去請求注冊中心,而是直接通過負載均衡算法從 IP 列表中取一個服務(wù)提供者的服務(wù)器調(diào)用服務(wù)。
當服務(wù)提供者的某臺服務(wù)器宕機或下線時,相應的 IP 會從服務(wù)提供者 IP 列表中移除。同時,注冊中心會將新的服務(wù) IP 地址列表發(fā)送給服務(wù)消費者機器,緩存在消費者本機。
當某個服務(wù)的所有服務(wù)器都下線了,那么這個服務(wù)也就下線了。
同樣,當服務(wù)提供者的某臺服務(wù)器上線時,注冊中心會將新的服務(wù) IP 地址列表發(fā)送給服務(wù)消費者機器,緩存在消費者本機。
服務(wù)提供方可以根據(jù)服務(wù)消費者的數(shù)量來作為服務(wù)下線的依據(jù)。
2.2 ZK 的心跳檢測
問題:第 3 步中“當服務(wù)提供者的某臺服務(wù)器宕機或下線時”,Zookeeper 如何感知到呢?
Zookeeper 提供了“心跳檢測”功能,它會定時向各個服務(wù)提供者發(fā)送一個請求(實際上建立的是一個 socket 長連接),如果長期沒有響應,服務(wù)中心就認為該服務(wù)提供者已經(jīng)“掛了”,并將其剔除。
比如 100.100.0.237 這臺機器如果宕機了,那么 Zookeeper 上的路徑就會只剩 /HelloWorldService/1.0.0/100.100.0.238:16888。
2.3 ZK 的 Watch 機制
問題:第 3 步和第 5 步中“注冊中心會將新的服務(wù) IP 地址列表發(fā)送給服務(wù)消費者機器”,這步是如何實現(xiàn)的呢?
這個問題也是經(jīng)典的生產(chǎn)者-消費者問題,解決的方式有兩種:
- 主動拉取策略:服務(wù)的消費者定期調(diào)用注冊中心提供的服務(wù)獲取接口獲取最新的服務(wù)列表并更新本地緩存,經(jīng)典案例就是 Eureka。
- 發(fā)布-訂閱模式:服務(wù)消費者能夠?qū)崟r監(jiān)控服務(wù)更新狀態(tài),通常采用監(jiān)聽器以及回調(diào)機制。
Zookeeper 使用的是“發(fā)布-訂閱模式”,這里就要提到 Zookeeper 的 Watch 機制,整體流程如下:
- 客戶端先向 ZooKeeper 服務(wù)端成功注冊想要監(jiān)聽的節(jié)點狀態(tài),同時客戶端本地會存儲該監(jiān)聽器相關(guān)的信息在 WatchManager 中;
- 當 ZooKeeper 服務(wù)端監(jiān)聽的數(shù)據(jù)狀態(tài)發(fā)生變化時,ZooKeeper 就會主動通知發(fā)送相應事件信息給相關(guān)會話客戶端,客戶端就會在本地響應式的回調(diào)相關(guān) Watcher 的 Handler。
上面講的有點抽象,大白話解讀一下,Zookeeper 的 Watch 機制其實就是一種推拉結(jié)合的模式:
- 服務(wù)消費者會去監(jiān)聽相應路徑(/HelloWorldService/1.0.0),一旦路徑上的數(shù)據(jù)有任務(wù)變化(增加或減少),Zookeeper 只會發(fā)送一個事件類型和節(jié)點信息給關(guān)注的客戶端,而不會包括具體的變更內(nèi)容,所以事件本身是輕量級的,這就是推的部分。
- 收到變更通知的客戶端需要自己去拉變更的數(shù)據(jù),這就是拉的部分。
3. ZK 是否適合作為注冊中心
探討這個問題前,我們一定需要知道什么是 CAP 理論。
3.1 CAP 理論
CAP 理論是分布式架構(gòu)中重要理論:
- 一致性(Consistency):所有節(jié)點在同一時間具有相同的數(shù)據(jù);
- 可用性(Availability) :保證每個請求不管成功或者失敗都有響應;
- 分隔容忍(Partition tolerance) :系統(tǒng)中任意信息的丟失或失敗不會影響系統(tǒng)的繼續(xù)運作。
關(guān)于 P 的理解,我覺得是在整個系統(tǒng)中某個部分,掛掉了,或者宕機了,并不影響整個系統(tǒng)的運作或者說使用,而可用性是,某個系統(tǒng)的某個節(jié)點掛了,但是并不影響系統(tǒng)的接受或者發(fā)出請求。
CAP 不可能都取,只能取其中 2 個的原因如下:
如果 C 是第一需求的話,那么會影響A的性能,因為要數(shù)據(jù)同步,不然請求結(jié)果會有差異,但是數(shù)據(jù)同步會消耗時間,期間可用性就會降低。
如果 A 是第一需求,那么只要有一個服務(wù)在,就能正常接受請求,但是對與返回結(jié)果變不能保證,原因是,在分布式部署的時候,數(shù)據(jù)一致的過程不可能想切線路那么快。
再如果,同時滿足一致性和可用性,那么分區(qū)容錯就很難保證了,也就是單點,也是分布式的基本核心。
3.2 ZK 作為注冊中心探討
作為一個分布式協(xié)同服務(wù),ZooKeeper 非常好,但是對于 Service 發(fā)現(xiàn)服務(wù)來說就不合適了,因為對于 Service 發(fā)現(xiàn)服務(wù)來說就算是返回了包含不實的信息的結(jié)果也比什么都不返回要好。
所以當向注冊中心查詢服務(wù)列表時,我們可以容忍注冊中心返回的是幾分鐘以前的注冊信息,但不能接受服務(wù)直接 down 掉不可用。
但是 zk 會出現(xiàn)這樣一種情況,當 master 節(jié)點因為網(wǎng)絡(luò)故障與其他節(jié)點失去聯(lián)系時,剩余節(jié)點會重新進行 leader 選舉。問題在于,選舉 leader 的時間太長,30 ~ 120s,且選舉期間整個zk集群都是不可用的,這就導致在選舉期間注冊服務(wù)癱瘓。
在云部署的環(huán)境下,因網(wǎng)絡(luò)問題使得 zk 集群失去 master 節(jié)點是較大概率會發(fā)生的事,雖然服務(wù)能夠最終恢復,但是漫長的選舉時間導致的注冊長期不可用是不能容忍的。
所以說,作為注冊中心,可用性的要求要高于一致性!
在 CAP 模型中,Zookeepe 整體遵循一致性(CP)原則,即在任何時候?qū)?Zookeeper 的訪問請求能得到一致的數(shù)據(jù)結(jié)果,但是當機器下線或者宕機時,不能保證服務(wù)可用性。
那為什么 Zookeeper 不使用最終一致性(AP)模型呢?因為這個依賴 Zookeeper 的核心算法是 ZAB,所有設(shè)計都是為了強一致性。這個對于分布式協(xié)調(diào)系統(tǒng),完全沒沒有毛病,但是你如果將 Zookeeper為分布式協(xié)調(diào)服務(wù)所做的一致性保障,用在注冊中心,或者說服務(wù)發(fā)現(xiàn)場景,這個其實就不合適。
4. 小節(jié)
我們對 Zookeeper 的注冊中心總結(jié)如下:
Zookeeper 的心跳檢測,可以自動探測服務(wù)提供者機器的宕機或下線;
Zookeeper 的 Watch 機制,可以將變更的注冊列表推給服務(wù)消費者;
Zookeeper 是 CP 模型,不太適合作為注冊中心。
不過網(wǎng)上也有說,Zookeeper 目前已經(jīng)支持 AP,準確說是 AP + Base 最終一致性,可以和我一起討論哈。