大廠開始放棄ZooKeeper,還用學(xué)嗎?
共 17412字,需瀏覽 35分鐘
·
2024-11-14 08:45
??目錄
1 ZooKeeper,時(shí)代棄子?
2 ZooKeeper 核心通識(shí)
3 典型應(yīng)用場(chǎng)景
11 月 14 日晚 8 點(diǎn),騰訊云開發(fā)者視頻號(hào)揭秘《北大人在鵝廠寫代碼有神器?》,預(yù)約看看大佬在工作中都有哪些“秘密武器”!觀看直播還有機(jī)會(huì)搶鵝廠周邊好禮!
01
為了應(yīng)對(duì)大流量,現(xiàn)代應(yīng)用/中間件通常采用分布式部署,此時(shí)不得不考慮 CAP 問(wèn)題。ZooKeeper(后文簡(jiǎn)稱 ZK)是面向 CP 設(shè)計(jì)的一個(gè)開源的分布式協(xié)調(diào)框架,將那些復(fù)雜且容易出錯(cuò)的分布式一致性服務(wù)封裝起來(lái),構(gòu)成一個(gè)高效可靠的原語(yǔ)集,并以一系列簡(jiǎn)單易用的接口提供給用戶使用,分布式應(yīng)用程序可以基于它實(shí)現(xiàn)諸如數(shù)據(jù)發(fā)布/訂閱、負(fù)載均衡、命名服務(wù)、集群管理、Master 選舉、分布式鎖、分布式隊(duì)列等功能。
但最近幾年,業(yè)界卻逐漸發(fā)現(xiàn),互聯(lián)網(wǎng)大廠紛紛放棄了對(duì) ZooKeeper 的選型,一時(shí)引起爭(zhēng)議無(wú)數(shù)。不僅如此,與 ZooKeeper 捆綁甚深的 Kafka 也在 2.8 版本里宣布了放棄 ZooKeeper,而在之前的版本中,沒(méi)有 ZooKeeper 甚至無(wú)法運(yùn)行。
隨著云原生技術(shù)的興起和分布式系統(tǒng)規(guī)模的擴(kuò)大,Zookeeper在處理大規(guī)模集群和快速擴(kuò)展時(shí)的性能瓶頸日益顯現(xiàn)。這是它開始被逐漸放棄、替代的原因之一。
從技術(shù)的角度來(lái)看,歷史的車輪在不斷向前滾動(dòng),學(xué)術(shù)界和工業(yè)界的理論基礎(chǔ)一直在不斷進(jìn)化,技術(shù)也要適應(yīng)不斷革新的業(yè)務(wù)不停去演進(jìn)。但是合適的組件總會(huì)出現(xiàn)在合適的地方,這就是架構(gòu)師和研發(fā)人員的工作和責(zé)任,也是我們應(yīng)該繼續(xù)去學(xué)習(xí) ZooKeeper 的原因。
02
public class DataNode implements Record { byte data[]; Long acl; public StatPersisted stat; private Set<String> children = null; }
-
data:znode 相關(guān)的業(yè)務(wù)數(shù)據(jù)均存儲(chǔ)在這里,但是,父節(jié)點(diǎn)不可存儲(chǔ)數(shù)據(jù); -
children:存儲(chǔ)當(dāng)前節(jié)點(diǎn)的子節(jié)點(diǎn)引用信息,因?yàn)閮?nèi)存限制,所以 znode 的子節(jié)點(diǎn)數(shù)不是無(wú)限的; -
stat:包含 znode 節(jié)點(diǎn)的狀態(tài)信息,比如: 事務(wù) id、版本號(hào)、時(shí)間戳等,其中事務(wù) id 和 ZK 的數(shù)據(jù)一直性、選主相關(guān),下面將重點(diǎn)介紹; -
acl:記錄客戶端對(duì) znode 節(jié)點(diǎn)的訪問(wèn)權(quán)限;
-
epoch:任期/紀(jì)元,Zxid 的高32位, ZAB 協(xié)議通過(guò) epoch 編號(hào)來(lái)區(qū)分Leader 周期變化,每次一個(gè) leader 被選出來(lái),它都會(huì)有一個(gè)新的 epoch=(原來(lái)的 epoch+1),標(biāo)識(shí)當(dāng)前屬于那個(gè)leader的 統(tǒng)治時(shí)期;可以假設(shè) leader 就像皇帝,epoch 則相當(dāng)于年號(hào),每個(gè)皇帝都有自己的年號(hào); -
事務(wù)計(jì)數(shù)器:Zxid 的低32位,每次數(shù)據(jù)變更,計(jì)數(shù)器都會(huì)加一;
-
PERSISTENT:永久節(jié)點(diǎn) -
EPHEMERAL:臨時(shí)節(jié)點(diǎn) -
PERSISTENT_SEQUENTIAL:永久順序節(jié)點(diǎn) -
EPHEMERAL_SEQUENTIAL:臨時(shí)順序節(jié)點(diǎn)
-
watcher 變更通知是一次性的:當(dāng)數(shù)據(jù)發(fā)生變化的時(shí)候, ZK 會(huì)產(chǎn)生一個(gè) watcher 事件,并且會(huì)發(fā)送到客戶端。但是客戶端只會(huì)收到一次通知。如果后續(xù)這個(gè)節(jié)點(diǎn)再次發(fā)生變化,那么之前設(shè)置 Watcher 的客戶端不會(huì)再次收到消息。可以通過(guò)循環(huán)監(jiān)聽去達(dá)到永久監(jiān)聽效果。 -
客戶端 watcher 順序回調(diào):watcher 回調(diào)是順序串行化執(zhí)行的,只有回調(diào)后客戶端才能看到節(jié)點(diǎn)最新的狀態(tài)。watcher 回調(diào)邏輯不應(yīng)太復(fù)雜,否則可能影響 watcher 執(zhí)行。 -
不會(huì)告訴節(jié)點(diǎn)變化前后的具體內(nèi)容:watchEvent 是最小的通信單元,結(jié)構(gòu)上包含通知狀態(tài)、事件類型和節(jié)點(diǎn)路徑,但是,不會(huì)告訴節(jié)點(diǎn)變化前后的具體內(nèi)容。 -
時(shí)效性:watcher 只有在當(dāng)前 session 徹底失效時(shí)才會(huì)無(wú)效,若在 session 有效期內(nèi)快速重連成功,則 watcher 依然存在,仍可收到事件通知。
-
Leader:一個(gè) ZK 集群同一時(shí)間只會(huì)有一個(gè)實(shí)際工作的 Leader,它會(huì)發(fā)起并維護(hù)與各 Follwer 及 Observer 間的心跳。所有的寫操作必須要通過(guò) Leader 完成再由 Leader 將寫操作廣播給其它服務(wù)器。 -
Follower:一個(gè) ZK 集群可同時(shí)存在多個(gè) Follower,它會(huì)響應(yīng) Leader 的心跳。Follower 可直接處理并返回客戶端的讀請(qǐng)求,同時(shí)會(huì)將寫請(qǐng)求轉(zhuǎn)發(fā)給 Leader 處理,參與事務(wù)請(qǐng)求 Proposal 的投票及 Leader 選舉投票。 -
Observer:Observer 是3.3.0 版本開始引入的一個(gè)服務(wù)器角色,一個(gè) ZK 集群可同時(shí)存在多個(gè) Observer, 功能與 Follower 類似,但是,不參與投票。
-
LOOKING:尋找 Leader 狀態(tài)。當(dāng)服務(wù)器處于該狀態(tài)時(shí),它會(huì)認(rèn)為當(dāng)前集群中沒(méi)有 Leader,因此需要進(jìn)入 Leader 選舉狀態(tài)。 -
LEADING:領(lǐng)導(dǎo)者狀態(tài)。表明當(dāng)前服務(wù)器角色是 Leader。 -
FOLLOWING:跟隨者狀態(tài),同步 leader 狀態(tài),參與投票。表明當(dāng)前服務(wù)器角色是 Follower。 -
OBSERVING:觀察者狀態(tài),同步 leader 狀態(tài),不參與投票。表明當(dāng)前服務(wù)器角色是 Observer。
-
客戶端向 Leader 發(fā)起寫請(qǐng)求。 -
Leader 將寫請(qǐng)求以 Proposal 的形式發(fā)給所有 Follower 并等待 ACK。 -
Follower 收到 Leader 的 Proposal 后返回 ACK。 -
Leader 得到過(guò)半數(shù)的 ACK(Leader 對(duì)自己默認(rèn)有一個(gè) ACK)后向所有的 Follower 和 Observer 發(fā)送 Commmit。 -
Leader 將處理結(jié)果返回給客戶端。
-
Leader 不需要得到所有 Follower 的 ACK,只要收到過(guò)半的 ACK 即可,同時(shí)Leader 本身對(duì)自己有一個(gè)ACK。上圖中有2個(gè) Follower,只需其中兩個(gè)返回ACK即可,因?yàn)?1+1) / (2+1) > 1/2。 -
Observer 雖然無(wú)投票權(quán),但仍須同步 Leader 的數(shù)據(jù)從而在處理讀請(qǐng)求時(shí)可以返回盡可能新的數(shù)據(jù)。
-
客戶端向 Follower 發(fā)起寫請(qǐng)求, Follower 將寫請(qǐng)求轉(zhuǎn)發(fā)給 Leader 處理; -
其它流程與直接寫 Leader 無(wú)任何區(qū)別。
-
第一階段:Leader數(shù)據(jù)寫入事件作為提案廣播給所有 Follower 結(jié)點(diǎn);可以寫入的Follower結(jié)點(diǎn)返回確認(rèn)信息 ACK。 -
第二階段:Leader 收到一半以上的 ACK 信息后確認(rèn)寫入可以生效,向所有結(jié)點(diǎn)廣播 COMMIT 將提案生效。
-
Leader 每發(fā)起一個(gè)提案,會(huì)將提案的 ZXID 和內(nèi)容放到 outstandingProposals 中,作為待提交的提案; -
Leader收到 Follower 的 ACK 信息后,根據(jù) ACK 中的 ZXID 從 outstandingProposals 中找到對(duì)應(yīng)的提案,對(duì) ACK 計(jì)數(shù); -
執(zhí)行 tryToCommit 嘗試將提案提交:判斷流程是,先判斷當(dāng)前 ZXID 之前是否還有未提交提案,如果有,當(dāng)前提案暫時(shí)不能提交;再判斷提案是否收到半數(shù)以上 ACK,如果達(dá)到半數(shù)則可以提交;如果可以提交,將當(dāng)前 ZXID 從 outstandingProposals 中清除并向 Followers 廣播提交當(dāng)前提案;
-
服務(wù)器 ID(myid):每個(gè) ZooKeeper 服務(wù)器,都需要在數(shù)據(jù)文件夾下創(chuàng)建一個(gè)名為 myid 的文件,該文件包含整個(gè) ZooKeeper 集群唯一的 ID(整數(shù))。該參數(shù)在選舉時(shí)如果無(wú)法通過(guò)其他判斷條件選擇 Leader,那么將該 ID 的大小來(lái)確定優(yōu)先級(jí)。 -
事務(wù) ID(zxid):?jiǎn)握{(diào)遞增,值越大說(shuō)明數(shù)據(jù)越新,權(quán)重越大。 -
邏輯時(shí)鐘(epoch-logicalclock):同一輪投票過(guò)程中的邏輯時(shí)鐘值是相同的,每投完一次值會(huì)增加。 -
ZK 的 leader 選舉存在兩類,一個(gè)是服務(wù)器啟動(dòng)時(shí) leader 選舉,另一個(gè)是運(yùn)行過(guò)程中服務(wù)器宕機(jī)時(shí)的 leader 選舉,下面依次展開介紹。
-
Server1 收到 Server2 和 Server3 的廣播選票后,由于 logicClock 和 zxid 都相等,此時(shí)就比較 myid; -
Server1 收到的兩張選票中 Server3 的 myid 最大,此時(shí) Server1 判斷應(yīng)該遵從 Server3 的投票決定,將自己的票改投給 Server3。接下來(lái) Server1 先清空自己的票箱(票箱中有第一步中投給自己的選票),然后將自己的新投票(1->3)和接收到的 Server3 的(3->3)投票一起存入自己的票箱,再把自己的新投票決定(1->3)廣播出去,此時(shí) Server1 的票箱中有兩票:(1->3),(3->3); -
同理,Server2 收到 Server3 的選票后也將自己的選票更新為(2->3)并存入票箱然后廣播。此時(shí) Server2 票箱內(nèi)的選票為(2->3),(3->3); -
Server3 根據(jù)上述規(guī)則,無(wú)須更新選票,自身的票箱內(nèi)選票仍為(3->3); -
Server1 與 Server2 重新投給 Server3 的選票廣播出去后,由于三個(gè)服務(wù)器最新選票都相同,最后三者的票箱內(nèi)都包含三張投給服務(wù)器 3 的選票。
-
假死:由于心跳超時(shí)(網(wǎng)絡(luò)原因?qū)е碌模┱J(rèn)為 Leader 死了,但其實(shí) Leader 還存活著。 -
腦裂:由于假死會(huì)發(fā)起新的Leader選舉,選舉出一個(gè)新的 Leader,但舊的 Leader 網(wǎng)絡(luò)又通了,導(dǎo)致出現(xiàn)了兩個(gè) Leader ,有的客戶端連接到老的 Leader,而有的客戶端則連接到新的 Leader。
03
-
當(dāng)集群中的服務(wù)啟動(dòng)時(shí),客戶端向 ZK 注冊(cè) watcher 監(jiān)聽特定節(jié)點(diǎn),并從節(jié)點(diǎn)拉取數(shù)據(jù)獲取配置信息; -
當(dāng)發(fā)布者變更配置時(shí),節(jié)點(diǎn)數(shù)據(jù)發(fā)生變化,ZK 會(huì)發(fā)送 watcher 事件給各個(gè)客戶端;客戶端在接收到 watcher 事件后,會(huì)從該節(jié)點(diǎn)重新拉取數(shù)據(jù)獲取最新配置信息。
-
服務(wù)提供者 server 啟動(dòng)時(shí)在 ZK 進(jìn)行服務(wù)注冊(cè)(創(chuàng)建臨時(shí)文件); -
服務(wù)消費(fèi)者 client 啟動(dòng)時(shí),請(qǐng)求 ZK 獲取最新的服務(wù)存活列表并注冊(cè) watcher,然后將獲得服務(wù)列表保存到本地緩存中; -
client 請(qǐng)求 server 時(shí),根據(jù)自己的負(fù)載均衡算法,從服務(wù)器列表選取一個(gè)進(jìn)行通信; -
若在運(yùn)行過(guò)程中,服務(wù)提供者出現(xiàn)異常或人工關(guān)閉不能提供服務(wù),臨時(shí)節(jié)點(diǎn)失效,ZK 探測(cè)到變化更新本地服務(wù)列表并異步通知到服務(wù)消費(fèi)者,服務(wù)消費(fèi)者監(jiān)聽到服務(wù)列表的變化,更新本地緩存。
-
所有 Dubbo 相關(guān)的數(shù)據(jù)都組織在 /dubbo 的根節(jié)點(diǎn)下; -
二級(jí)目錄是服務(wù)名,如 com.foo.BarService ; -
三級(jí)目錄有兩個(gè)子節(jié)點(diǎn),分別是 providers 和 consumers ,表示該服務(wù)的提供者和消費(fèi)者; -
四級(jí)目錄記錄了與該服務(wù)相關(guān)的每一個(gè)應(yīng)用實(shí)例的 URL 信息,在 providers 下的表示該服務(wù)的所有提供者,而在 consumers 下的表示該服務(wù)的所有消費(fèi)者。舉例說(shuō)明, com.foo.BarService 的服務(wù)提供者在啟動(dòng)時(shí)將自己的 URL 信息注冊(cè)到 /dubbo/com.foo.BarService/providers 下;同樣的,服務(wù)消費(fèi)者將自己的信息注冊(cè)到相應(yīng)的 consumers 下,同時(shí),服務(wù)消費(fèi)者會(huì)訂閱其所對(duì)應(yīng)的 providers 節(jié)點(diǎn),以便能夠感知到服務(wù)提供方地址列表的變化。
????歡迎加入騰訊云開發(fā)者社群,享前沿資訊、大咖干貨,找興趣搭子,交同城好友,更有鵝廠招聘機(jī)會(huì)、限量周邊好禮等你來(lái)~
(長(zhǎng)按圖片立即掃碼)
