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