您當前的位置:首頁 > 體育

Zookeeper入門級萬字總結

作者:由 終端研發部 發表于 體育時間:2021-02-23

好久不見

離上一篇文章的釋出也快一個月了,想想已經快一個月沒寫東西了,其中可能有期末考試、課程設計和駕照考試,但這都不是藉口!

一到冬天就懶的不行,望廣大掘友督促我 ✍️✍️。

文章很長,先贊後看,養成習慣。❤️

什麼是ZooKeeper

ZooKeeper

Yahoo

開發,後來捐贈給了

Apache

,現已成為

Apache

頂級專案。

ZooKeeper

是一個開源的分散式應用程式協調伺服器,其為分散式系統提供一致性服務。其一致性是透過基於

Paxos

演算法的

ZAB

協議完成的。其主要功能包括:配置維護、分散式同步、叢集管理、分散式事務等。

Zookeeper入門級萬字總結

簡單來說,

ZooKeeper

是一個

分散式協調服務框架

。分散式?協調服務?這啥玩意?

其實解釋到分散式這個概念的時候,我發現有些同學並不是能把 **分散式和叢集 **這兩個概念很好的理解透。前段時間有同學和我探討起分散式的東西,他說分散式不就是加機器嗎?一臺機器不夠用再加一臺抗壓唄。當然加機器這種說法也無可厚非,你一個分散式系統必定涉及到多個機器,但是你別忘了,計算機學科中還有一個相似的概念——

Cluster

,叢集不也是加機器嗎?但是 叢集 和 分散式 其實就是兩個完全不同的概念。

比如,我現在有一個秒殺服務,併發量太大單機系統承受不住,那我加幾臺伺服器也

一樣

提供秒殺服務,這個時候就是

Cluster

叢集

Zookeeper入門級萬字總結

但是,我現在換一種方式,我將一個秒殺服務

拆分成多個子服務

,比如建立訂單服務,增加積分服務,扣優惠券服務等等,

然後我將這些子服務都部署在不同的伺服器上

,這個時候就是

Distributed

分散式

Zookeeper入門級萬字總結

而我為什麼反駁同學所說的分散式就是加機器呢?因為我認為加機器更加適用於構建叢集,因為它真是隻有加機器。而對於分散式來說,你首先需要將業務進行拆分,然後再加機器(不僅僅是加機器那麼簡單),同時你還要去解決分散式帶來的一系列問題。

Zookeeper入門級萬字總結

比如各個分散式元件如何協調起來,如何減少各個系統之間的耦合度,分散式事務的處理,如何去配置整個分散式系統等等。

ZooKeeper

主要就是解決這些問題的。

一致性問題

設計一個分散式系統必定會遇到一個問題——

因為分割槽容忍性(partition tolerance)的存在,就必定要求我們需要在系統可用性(availability)和資料一致性(consistency)中做出權衡

。這就是著名的

CAP

定理。

理解起來其實很簡單,比如說把一個班級作為整個系統,而學生是系統中的一個個獨立的子系統。這個時候班裡的小紅小明偷偷談戀愛被班裡的大嘴巴小花發現了,小花欣喜若狂告訴了周圍的人,然後小紅小明談戀愛的訊息在班級裡傳播起來了。當在訊息的傳播(散佈)過程中,你抓到一個同學問他們的情況,如果回答你不知道,那麼說明整個班級系統出現了資料不一致的問題(因為小花已經知道這個訊息了)。而如果他直接不回答你,因為整個班級有訊息在進行傳播(為了保證一致性,需要所有人都知道才可提供服務),這個時候就出現了系統的可用性問題。

Zookeeper入門級萬字總結

而上述前者就是

Eureka

的處理方式,它保證了AP(可用性),後者就是我們今天所要將的

ZooKeeper

的處理方式,它保證了CP(資料一致性)。

一致性協議和演算法

而為了解決資料一致性問題,在科學家和程式設計師的不斷探索中,就出現了很多的一致性協議和演算法。比如 2PC(兩階段提交),3PC(三階段提交),Paxos演算法等等。

這時候請你思考一個問題,同學之間如果採用傳紙條的方式去傳播訊息,那麼就會出現一個問題——我咋知道我的小紙條有沒有傳到我想要傳遞的那個人手中呢?萬一被哪個小傢伙給劫持篡改了呢,對吧?

Zookeeper入門級萬字總結

這個時候就引申出一個概念——

拜占庭將軍問題

。它意指

在不可靠通道上試圖透過訊息傳遞的方式達到一致性是不可能的

, 所以所有的一致性演算法的

必要前提

就是安全可靠的訊息通道。

而為什麼要去解決資料一致性的問題?你想想,如果一個秒殺系統將服務拆分成了下訂單和加積分服務,這兩個服務部署在不同的機器上了,萬一在訊息的傳播過程中積分系統宕機了,總不能你這邊下了訂單卻沒加積分吧?你總得保證兩邊的資料需要一致吧?

2PC(兩階段提交)

兩階段提交是一種保證分散式系統資料一致性的協議,現在很多資料庫都是採用的兩階段提交協議來完成

分散式事務

的處理。

在介紹2PC之前,我們先來想想分散式事務到底有什麼問題呢?

還拿秒殺系統的下訂單和加積分兩個系統來舉例吧(我想你們可能都吐了 ),我們此時下完訂單會發個訊息給積分系統告訴它下面該增加積分了。如果我們僅僅是傳送一個訊息也不收回復,那麼我們的訂單系統怎麼能知道積分系統的收到訊息的情況呢?如果我們增加一個收回復的過程,那麼當積分系統收到訊息後返回給訂單系統一個

Response

,但在中間出現了網路波動,那個回覆訊息沒有傳送成功,訂單系統是不是以為積分系統訊息接收失敗了?它是不是會回滾事務?但此時積分系統是成功收到訊息的,它就會去處理訊息然後給使用者增加積分,這個時候就會出現積分加了但是訂單沒下成功。

所以我們所需要解決的是在分散式系統中,整個呼叫鏈中,我們所有服務的資料處理要麼都成功要麼都失敗,即所有服務的

原子性問題

在兩階段提交中,主要涉及到兩個角色,分別是協調者和參與者。

第一階段:當要執行一個分散式事務的時候,事務發起者首先向協調者發起事務請求,然後協調者會給所有參與者傳送

prepare

請求(其中包括事務內容)告訴參與者你們需要執行事務了,如果能執行我發的事務內容那麼就先執行但不提交,執行後請給我回復。然後參與者收到

prepare

訊息後,他們會開始執行事務(但不提交),並將

Undo

Redo

資訊記入事務日誌中,之後參與者就向協調者反饋是否準備好了。

第二階段:第二階段主要是協調者根據參與者反饋的情況來決定接下來是否可以進行事務的提交操作,即提交事務或者回滾事務。

比如這個時候

所有的參與者

都返回了準備好了的訊息,這個時候就進行事務的提交,協調者此時會給所有的參與者傳送

Commit

請求

,當參與者收到

Commit

請求的時候會執行前面執行的事務的

提交操作

,提交完畢之後將給協調者傳送提交成功的響應。

而如果在第一階段並不是所有參與者都返回了準備好了的訊息,那麼此時協調者將會給所有參與者傳送

回滾事務的

rollback

請求

,參與者收到之後將會

回滾它在第一階段所做的事務處理

,然後再將處理情況返回給協調者,最終協調者收到響應後便給事務發起者返回處理失敗的結果。

Zookeeper入門級萬字總結

個人覺得 2PC 實現得還是比較雞肋的,因為事實上它只解決了各個事務的原子性問題,隨之也帶來了很多的問題。

Zookeeper入門級萬字總結

單點故障問題

,如果協調者掛了那麼整個系統都處於不可用的狀態了。

阻塞問題

,即當協調者傳送

prepare

請求,參與者收到之後如果能處理那麼它將會進行事務的處理但並不提交,這個時候會一直佔用著資源不釋放,如果此時協調者掛了,那麼這些資源都不會再釋放了,這會極大影響效能。

資料不一致問題

,比如當第二階段,協調者只發送了一部分的

commit

請求就掛了,那麼也就意味著,收到訊息的參與者會進行事務的提交,而後面沒收到的則不會進行事務提交,那麼這時候就會產生資料不一致性問題。

3PC(三階段提交)

因為2PC存在的一系列問題,比如單點,容錯機制缺陷等等,從而產生了

3PC(三階段提交)

。那麼這三階段又分別是什麼呢?

千萬不要吧PC理解成個人電腦了,其實他們是 phase-commit 的縮寫,即階段提交。

CanCommit階段

:協調者向所有參與者傳送

CanCommit

請求,參與者收到請求後會根據自身情況檢視是否能執行事務,如果可以則返回 YES 響應並進入預備狀態,否則返回 NO 。

PreCommit階段

:協調者根據參與者返回的響應來決定是否可以進行下面的

PreCommit

操作。如果上面參與者返回的都是 YES,那麼協調者將向所有參與者傳送

PreCommit

預提交請求,

參與者收到預提交請求後,會進行事務的執行操作,並將

Undo

Redo

資訊寫入事務日誌中

,最後如果參與者順利執行了事務則給協調者返回成功的響應。如果在第一階段協調者收到了

任何一個 NO

的資訊,或者

在一定時間內

並沒有收到全部的參與者的響應,那麼就會中斷事務,它會向所有參與者傳送中斷請求(abort),參與者收到中斷請求之後會立即中斷事務,或者在一定時間內沒有收到協調者的請求,它也會中斷事務。

DoCommit階段

:這個階段其實和

2PC

的第二階段差不多,如果協調者收到了所有參與者在

PreCommit

階段的 YES 響應,那麼協調者將會給所有參與者傳送

DoCommit

請求,

參與者收到

DoCommit

請求後則會進行事務的提交工作

,完成後則會給協調者返回響應,協調者收到所有參與者返回的事務提交成功的響應之後則完成事務。若協調者在

PreCommit

階段

收到了任何一個 NO 或者在一定時間內沒有收到所有參與者的響應

,那麼就會進行中斷請求的傳送,參與者收到中斷請求後則會

透過上面記錄的回滾日誌

來進行事務的回滾操作,並向協調者反饋回滾狀況,協調者收到參與者返回的訊息後,中斷事務。

Zookeeper入門級萬字總結

3PC流程

這裡是

3PC

在成功的環境下的流程圖,你可以看到

3PC

在很多地方進行了超時中斷的處理,比如協調者在指定時間內為收到全部的確認訊息則進行事務中斷的處理,這樣能

減少同步阻塞的時間

。還有需要注意的是,

3PC

DoCommit

階段參與者如未收到協調者傳送的提交事務的請求,它會在一定時間內進行事務的提交

。為什麼這麼做呢?是因為這個時候我們肯定

保證了在第一階段所有的協調者全部返回了可以執行事務的響應

,這個時候我們有理由

相信其他系統都能進行事務的執行和提交

,所以

不管

協調者有沒有發訊息給參與者,進入第三階段參與者都會進行事務的提交操作。

總之,

3PC

透過一系列的超時機制很好的緩解了阻塞問題,但是最重要的一致性並沒有得到根本的解決,比如在

PreCommit

階段,當一個參與者收到了請求之後其他參與者和協調者掛了或者出現了網路分割槽,這個時候收到訊息的參與者都會進行事務提交,這就會出現資料不一致性問題。

所以,要解決一致性問題還需要靠

Paxos

演算法⭐️ ⭐️ ⭐️ 。

Paxos

演算法

Paxos

演算法是基於

訊息傳遞且具有高度容錯特性的一致性演算法

,是目前公認的解決分散式一致性問題最有效的演算法之一,

其解決的問題就是在分散式系統中如何就某個值(決議)達成一致

Paxos

中主要有三個角色,分別為

Proposer提案者

Acceptor表決者

Learner學習者

Paxos

演算法和

2PC

一樣,也有兩個階段,分別為

Prepare

accept

階段。

prepare 階段

Proposer提案者

:負責提出

proposal

,每個提案者在提出提案時都會首先獲取到一個

具有全域性唯一性的、遞增的提案編號N

,即在整個叢集中是唯一的編號 N,然後將該編號賦予其要提出的提案,在

第一階段是隻將提案編號傳送給所有的表決者

Acceptor表決者

:每個表決者在

accept

某提案後,會將該提案編號N記錄在本地,這樣每個表決者中儲存的已經被 accept 的提案中會存在一個

編號最大的提案

,其編號假設為

maxN

。每個表決者僅會

accept

編號大於自己本地

maxN

的提案,在批准提案時表決者會將以前接受過的最大編號的提案作為響應反饋給

Proposer

下面是

prepare

階段的流程圖,你可以對照著參考一下。

Zookeeper入門級萬字總結

accept 階段

當一個提案被

Proposer

提出後,如果

Proposer

收到了超過半數的

Acceptor

的批准(

Proposer

本身同意),那麼此時

Proposer

會給所有的

Acceptor

傳送真正的提案(你可以理解為第一階段為試探),這個時候

Proposer

就會發送提案的內容和提案編號。

表決者收到提案請求後會再次比較本身已經批准過的最大提案編號和該提案編號,如果該提案編號

大於等於

已經批准過的最大提案編號,那麼就

accept

該提案(此時執行提案內容但不提交),隨後將情況返回給

Proposer

。如果不滿足則不迴應或者返回 NO 。

Zookeeper入門級萬字總結

image。png

Proposer

收到超過半數的

accept

,那麼它這個時候會向所有的

acceptor

傳送提案的提交請求。需要注意的是,因為上述僅僅是超過半數的

acceptor

批准執行了該提案內容,其他沒有批准的並沒有執行該提案內容,所以這個時候需要

向未批准的

acceptor

傳送提案內容和提案編號並讓它無條件執行和提交

,而對於前面已經批准過該提案的

acceptor

來說

僅僅需要傳送該提案的編號

,讓

acceptor

執行提交就行了。

Zookeeper入門級萬字總結

而如果

Proposer

如果沒有收到超過半數的

accept

那麼它將會將

遞增

Proposal

的編號,然後

重新進入

Prepare

階段

對於

Learner

來說如何去學習

Acceptor

批准的提案內容,這有很多方式,讀者可以自己去了解一下,這裡不做過多解釋。

paxos

演算法的死迴圈問題

其實就有點類似於兩個人吵架,小明說我是對的,小紅說我才是對的,兩個人據理力爭的誰也不讓誰 。

比如說,此時提案者 P1 提出一個方案 M1,完成了

Prepare

階段的工作,這個時候

acceptor

則批准了 M1,但是此時提案者 P2 同時也提出了一個方案 M2,它也完成了

Prepare

階段的工作。然後 P1 的方案已經不能在第二階段被批准了(因為

acceptor

已經批准了比 M1 更大的 M2),所以 P1 自增方案變為 M3 重新進入

Prepare

階段,然後

acceptor

,又批准了新的 M3 方案,它又不能批准 M2 了,這個時候 M2 又自增進入

Prepare

階段。。。

就這樣無休無止的永遠提案下去,這就是

paxos

演算法的死迴圈問題。

Zookeeper入門級萬字總結

那麼如何解決呢?很簡單,人多了容易吵架,我現在

就允許一個能提案

就行了。

引出

ZAB

Zookeeper

架構

作為一個優秀高效且可靠的分散式協調框架,

ZooKeeper

在解決分散式資料一致性問題時並沒有直接使用

Paxos

,而是專門定製了一致性協議叫做

ZAB(ZooKeeper Automic Broadcast)

原子廣播協議,該協議能夠很好地支援

崩潰恢復

Zookeeper入門級萬字總結

ZAB

中的三個角色

和介紹

Paxos

一樣,在介紹

ZAB

協議之前,我們首先來了解一下在

ZAB

中三個主要的角色,

Leader 領導者

Follower跟隨者

Observer觀察者

Leader

:叢集中

唯一的寫請求處理者

,能夠發起投票(投票也是為了進行寫請求)。

Follower

:能夠接收客戶端的請求,如果是讀請求則可以自己處理,

如果是寫請求則要轉發給

Leader

。在選舉過程中會參與投票,

有選舉權和被選舉權

Observer

:就是沒有選舉權和被選舉權的

Follower

ZAB

協議中對

zkServer

(即上面我們說的三個角色的總稱) 還有兩種模式的定義,分別是

訊息廣播

崩潰恢復

訊息廣播模式

說白了就是

ZAB

協議是如何處理寫請求的,上面我們不是說只有

Leader

能處理寫請求嘛?那麼我們的

Follower

Observer

是不是也需要

同步更新資料

呢?總不能資料只在

Leader

中更新了,其他角色都沒有得到更新吧?

不就是

在整個叢集中保持資料的一致性

嘛?如果是你,你會怎麼做呢?

Zookeeper入門級萬字總結

廢話,第一步肯定需要

Leader

將寫請求

廣播

出去呀,讓

Leader

問問

Followers

是否同意更新,如果超過半數以上的同意那麼就進行

Follower

Observer

的更新(和

Paxos

一樣)。當然這麼說有點虛,畫張圖理解一下。

Zookeeper入門級萬字總結

訊息廣播

嗯。。。看起來很簡單,貌似懂了 。這兩個

Queue

哪冒出來的?答案是

ZAB

需要讓

Follower

Observer

保證順序性

。何為順序性,比如我現在有一個寫請求A,此時

Leader

將請求A廣播出去,因為只需要半數同意就行,所以可能這個時候有一個

Follower

F1因為網路原因沒有收到,而

Leader

又廣播了一個請求B,因為網路原因,F1竟然先收到了請求B然後才收到了請求A,這個時候請求處理的順序不同就會導致資料的不同,從而

產生資料不一致問題

所以在

Leader

這端,它為每個其他的

zkServer

準備了一個

佇列

,採用先進先出的方式傳送訊息。由於協議是 **透過

TCP

**來進行網路通訊的,保證了訊息的傳送順序性,接受順序性也得到了保證。

除此之外,在

ZAB

中還定義了一個

全域性單調遞增的事務ID

ZXID

,它是一個64位long型,其中高32位表示

epoch

年代,低32位表示事務id。

epoch

是會根據

Leader

的變化而變化的,當一個

Leader

掛了,新的

Leader

上位的時候,年代(

epoch

)就變了。而低32位可以簡單理解為遞增的事務id。

定義這個的原因也是為了順序性,每個

proposal

Leader

中生成後需要

透過其

ZXID

來進行排序

,才能得到處理。

崩潰恢復模式

說到崩潰恢復我們首先要提到

ZAB

中的

Leader

選舉演算法,當系統出現崩潰影響最大應該是

Leader

的崩潰,因為我們只有一個

Leader

,所以當

Leader

出現問題的時候我們勢必需要重新選舉

Leader

Leader

選舉可以分為兩個不同的階段,第一個是我們提到的

Leader

宕機需要重新選舉,第二則是當

Zookeeper

啟動時需要進行系統的

Leader

初始化選舉。下面我先來介紹一下

ZAB

是如何進行初始化選舉的。

假設我們叢集中有3臺機器,那也就意味著我們需要兩臺以上同意(超過半數)。比如這個時候我們啟動了

server1

,它會首先

投票給自己

,投票內容為伺服器的

myid

ZXID

,因為初始化所以

ZXID

都為0,此時

server1

發出的投票為 (1,0)。但此時

server1

的投票僅為1,所以不能作為

Leader

,此時還在選舉階段所以整個叢集處於

Looking

狀態

接著

server2

啟動了,它首先也會將投票選給自己(2,0),並將投票資訊廣播出去(

server1

也會,只是它那時沒有其他的伺服器了),

server1

在收到

server2

的投票資訊後會將投票資訊與自己的作比較。

首先它會比較

ZXID

,ZXID

大的優先為

Leader,如果相同則比較

myid,myid

大的優先作為

Leader

。所以此時

server1

發現

server2

更適合做

Leader

,它就會將自己的投票資訊更改為(2,0)然後再廣播出去,之後

server2

收到之後發現和自己的一樣無需做更改,並且自己的

投票已經超過半數

,則

確定

server2

Leader

server1

也會將自己伺服器設定為

Following

變為

Follower

。整個伺服器就從

Looking

變為了正常狀態。

server3

啟動發現叢集沒有處於

Looking

狀態時,它會直接以

Follower

的身份加入叢集。

還是前面三個

server

的例子,如果在整個叢集執行的過程中

server2

掛了,那麼整個叢集會如何重新選舉

Leader

呢?其實和初始化選舉差不多。

首先毫無疑問的是剩下的兩個

Follower

會將自己的狀態

Following

變為

Looking

狀態

,然後每個

server

會向初始化投票一樣首先給自己投票(這不過這裡的

zxid

可能不是0了,這裡為了方便隨便取個數字)。

假設

server1

給自己投票為(1,99),然後廣播給其他

server

server3

首先也會給自己投票(3,95),然後也廣播給其他

server

server1

server3

此時會收到彼此的投票資訊,和一開始選舉一樣,他們也會比較自己的投票和收到的投票(

zxid

大的優先,如果相同那麼就

myid

大的優先)。這個時候

server1

收到了

server3

的投票發現沒自己的合適故不變,

server3

收到

server1

的投票結果後發現比自己的合適於是更改投票為(1,99)然後廣播出去,最後

server1

收到了發現自己的投票已經超過半數就把自己設為

Leader

server3

也隨之變為

Follower

請注意

ZooKeeper

為什麼要設定奇數個結點?比如這裡我們是三個,掛了一個我們還能正常工作,掛了兩個我們就不能正常工作了(已經沒有超過半數的節點數了,所以無法進行投票等操作了)。而假設我們現在有四個,掛了一個也能工作,

但是掛了兩個也不能正常工作了

,這是和三個一樣的,而三個比四個還少一個,帶來的效益是一樣的,所以

Zookeeper

推薦奇數個

server

那麼說完了

ZAB

中的

Leader

選舉方式之後我們再來了解一下

崩潰恢復

是什麼玩意?

其實主要就是

當叢集中有機器掛了,我們整個叢集如何保證資料一致性?

如果只是

Follower

掛了,而且掛的沒超過半數的時候,因為我們一開始講了在

Leader

中會維護佇列,所以不用擔心後面的資料沒接收到導致資料不一致性。

如果

Leader

掛了那就麻煩了,我們肯定需要先暫停服務變為

Looking

狀態然後進行

Leader

的重新選舉(上面我講過了),但這個就要分為兩種情況了,分別是

確保已經被Leader提交的提案最終能夠被所有的Follower提交

跳過那些已經被丟棄的提案

確保已經被Leader提交的提案最終能夠被所有的Follower提交是什麼意思呢?

假設

Leader (server2)

傳送

commit

請求(忘了請看上面的訊息廣播模式),他傳送給了

server3

,然後要發給

server1

的時候突然掛了。這個時候重新選舉的時候我們如果把

server1

作為

Leader

的話,那麼肯定會產生資料不一致性,因為

server3

肯定會提交剛剛

server2

傳送的

commit

請求的提案,而

server1

根本沒收到所以會丟棄。

Zookeeper入門級萬字總結

那怎麼解決呢?

聰明的同學肯定會質疑,

這個時候

server1

已經不可能成為

Leader

了,因為

server1

server3

進行投票選舉的時候會比較

ZXID

,而此時

server3

ZXID

肯定比

server1

的大了

。(不理解可以看前面的選舉演算法)

那麼跳過那些已經被丟棄的提案又是什麼意思呢?

假設

Leader (server2)

此時同意了提案N1,自身提交了這個事務並且要傳送給所有

Follower

commit

的請求,卻在這個時候掛了,此時肯定要重新進行

Leader

的選舉,比如說此時選

server1

Leader

(這無所謂)。但是過了一會,這個

掛掉的

Leader

又重新恢復了

,此時它肯定會作為

Follower

的身份進入叢集中,需要注意的是剛剛

server2

已經同意提交了提案N1,但其他

server

並沒有收到它的

commit

資訊,所以其他

server

不可能再提交這個提案N1了,這樣就會出現資料不一致性問題了,所以

該提案N1最終需要被拋棄掉

Zookeeper入門級萬字總結

Zookeeper的幾個理論知識

瞭解了

ZAB

協議還不夠,它僅僅是

Zookeeper

內部實現的一種方式,而我們如何透過

Zookeeper

去做一些典型的應用場景呢?比如說叢集管理,分散式鎖,

Master

選舉等等。

這就涉及到如何使用

Zookeeper

了,但在使用之前我們還需要掌握幾個概念。比如

Zookeeper

資料模型

會話機制

ACL

Watcher機制

等等。

資料模型

zookeeper

資料儲存結構與標準的

Unix

檔案系統非常相似,都是在根節點下掛很多子節點(樹型)。但是

zookeeper

中沒有檔案系統中目錄與檔案的概念,而是

使用了

znode

作為資料節點

znode

zookeeper

中的最小資料單元,每個

znode

上都可以儲存資料,同時還可以掛載子節點,形成一個樹形化名稱空間。

Zookeeper入門級萬字總結

每個

znode

都有自己所屬的

節點型別

節點狀態

其中節點型別可以分為

持久節點

持久順序節點

臨時節點

臨時順序節點

持久節點:一旦建立就一直存在,直到將其刪除。

持久順序節點:一個父節點可以為其子節點

維護一個建立的先後順序

,這個順序體現在

節點名稱

上,是節點名稱後自動新增一個由 10 位數字組成的數字串,從 0 開始計數。

臨時節點:臨時節點的生命週期是與

客戶端會話

繫結的,

會話消失則節點消失

。臨時節點

只能做葉子節點

,不能建立子節點。

臨時順序節點:父節點可以建立一個維持了順序的臨時節點(和前面的持久順序性節點一樣)。

節點狀態中包含了很多節點的屬性比如

czxid

mzxid

等等,在

zookeeper

中是使用

Stat

這個類來維護的。下面我列舉一些屬性解釋。

czxid

Created ZXID

,該資料節點被

建立

時的事務ID。

mzxid

Modified ZXID

,節點

最後一次被更新時

的事務ID。

ctime

Created Time

,該節點被建立的時間。

mtime

Modified Time

,該節點最後一次被修改的時間。

version

:節點的版本號。

cversion

子節點

的版本號。

aversion

:節點的

ACL

版本號。

ephemeralOwner

:建立該節點的會話的

sessionID

,如果該節點為持久節點,該值為0。

dataLength

:節點資料內容的長度。

numChildre

:該節點的子節點個數,如果為臨時節點為0。

pzxid

:該節點子節點列表最後一次被修改時的事務ID,注意是子節點的

列表

,不是內容。

會話

我想這個對於後端開發的朋友肯定不陌生,不就是

session

嗎?只不過

zk

客戶端和服務端是透過

TCP

長連線

維持的會話機制,其實對於會話來說你可以理解為

保持連線狀態

zookeeper

中,會話還有對應的事件,比如

CONNECTION_LOSS 連線丟失事件

SESSION_MOVED 會話轉移事件

SESSION_EXPIRED 會話超時失效事件

ACL

ACL

Access Control Lists

,它是一種許可權控制。在

zookeeper

中定義了5種許可權,它們分別為:

CREATE

:建立子節點的許可權。

READ

:獲取節點資料和子節點列表的許可權。

WRITE

:更新節點資料的許可權。

DELETE

:刪除子節點的許可權。

ADMIN

:設定節點 ACL 的許可權。

Watcher機制

Watcher

為事件監聽器,是

zk

非常重要的一個特性,很多功能都依賴於它,它有點類似於訂閱的方式,即客戶端向服務端

註冊

指定的

watcher

,當服務端符合了

watcher

的某些事件或要求則會

向客戶端傳送事件通知

,客戶端收到通知後找到自己定義的

Watcher

然後

執行相應的回撥方法

Zookeeper入門級萬字總結

Zookeeper的幾個典型應用場景

前面說了這麼多的理論知識,你可能聽得一頭霧水,這些玩意有啥用?能幹啥事?別急,聽我慢慢道來。

Zookeeper入門級萬字總結

選主

還記得上面我們的所說的臨時節點嗎?因為

Zookeeper

的強一致性,能夠很好地在保證

在高併發的情況下保證節點建立的全域性唯一性

(即無法重複建立同樣的節點)。

利用這個特性,我們可以

讓多個客戶端建立一個指定的節點

,建立成功的就是

master

但是,如果這個

master

掛了怎麼辦???

你想想為什麼我們要建立臨時節點?還記得臨時節點的生命週期嗎?

master

掛了是不是代表會話斷了?會話斷了是不是意味著這個節點沒了?還記得

watcher

嗎?我們是不是可以

讓其他不是

master

的節點監聽節點的狀態

,比如說我們監聽這個臨時節點的父節點,如果子節點個數變了就代表

master

掛了,這個時候我們

觸發回撥函式進行重新選舉

,或者我們直接監聽節點的狀態,我們可以透過節點是否已經失去連線來判斷

master

是否掛了等等。

Zookeeper入門級萬字總結

總的來說,我們可以完全

利用 臨時節點、節點狀態 和

watcher

來實現選主的功能

,臨時節點主要用來選舉,節點狀態和

watcher

可以用來判斷

master

的活性和進行重新選舉。

分散式鎖

分散式鎖的實現方式有很多種,比如

Redis

、資料庫 、

zookeeper

等。個人認為

zookeeper

在實現分散式鎖這方面是非常非常簡單的。

上面我們已經提到過了

zk在高併發的情況下保證節點建立的全域性唯一性

,這玩意一看就知道能幹啥了。實現互斥鎖唄,又因為能在分散式的情況下,所以能實現分散式鎖唄。

如何實現呢?這玩意其實跟選主基本一樣,我們也可以利用臨時節點的建立來實現。

首先肯定是如何獲取鎖,因為建立節點的唯一性,我們可以讓多個客戶端同時建立一個臨時節點,

建立成功的就說明獲取到了鎖

。然後沒有獲取到鎖的客戶端也像上面選主的非主節點建立一個

watcher

進行節點狀態的監聽,如果這個互斥鎖被釋放了(可能獲取鎖的客戶端宕機了,或者那個客戶端主動釋放了鎖)可以呼叫回撥函式重新獲得鎖。

zk

中不需要向

redis

那樣考慮鎖得不到釋放的問題了,因為當客戶端掛了,節點也掛了,鎖也釋放了。是不是很簡答?

那能不能使用

zookeeper

同時實現

共享鎖和獨佔鎖

呢?答案是可以的,不過稍微有點複雜而已。

還記得

有序的節點

嗎?

這個時候我規定所有建立節點必須有序,當你是讀請求(要獲取共享鎖)的話,如果

沒有比自己更小的節點,或比自己小的節點都是讀請求

,則可以獲取到讀鎖,然後就可以開始讀了。

若比自己小的節點中有寫請求

,則當前客戶端無法獲取到讀鎖,只能等待前面的寫請求完成。

如果你是寫請求(獲取獨佔鎖),若

沒有比自己更小的節點

,則表示當前客戶端可以直接獲取到寫鎖,對資料進行修改。若發現

有比自己更小的節點,無論是讀操作還是寫操作,當前客戶端都無法獲取到寫鎖

,等待所有前面的操作完成。

這就很好地同時實現了共享鎖和獨佔鎖,當然還有最佳化的地方,比如當一個鎖得到釋放它會通知所有等待的客戶端從而造成

羊群效應

。此時你可以透過讓等待的節點只監聽他們前面的節點。

具體怎麼做呢?其實也很簡單,你可以讓

讀請求監聽比自己小的最後一個寫請求節點,寫請求只監聽比自己小的最後一個節點

,感興趣的小夥伴可以自己去研究一下。

命名服務

如何給一個物件設定ID,大家可能都會想到

UUID

,但是

UUID

最大的問題就在於它太長了。。。(太長不一定是好事,嘿嘿嘿)。那麼在條件允許的情況下,我們能不能使用

zookeeper

來實現呢?

我們之前提到過

zookeeper

是透過

樹形結構

來儲存資料節點的,那也就是說,對於每個節點的

全路徑

,它必定是唯一的,我們可以使用節點的全路徑作為命名方式了。而且更重要的是,路徑是我們可以自己定義的,這對於我們對有些有語意的物件的ID設定可以更加便於理解。

叢集管理和註冊中心

看到這裡是不是覺得

zookeeper

實在是太強大了,它怎麼能這麼能幹!

別急,它能幹的事情還很多呢。可能我們會有這樣的需求,我們需要了解整個叢集中有多少機器在工作,我們想對及群眾的每臺機器的執行時狀態進行資料採集,對叢集中機器進行上下線操作等等。

zookeeper

天然支援的

watcher

和 臨時節點能很好的實現這些需求。我們可以為每條機器建立臨時節點,並監控其父節點,如果子節點列表有變動(我們可能建立刪除了臨時節點),那麼我們可以使用在其父節點繫結的

watcher

進行狀態監控和回撥。

Zookeeper入門級萬字總結

至於註冊中心也很簡單,我們同樣也是讓

服務提供者

zookeeper

中建立一個臨時節點並且將自己的

ip、port、呼叫方式

寫入節點,當

服務消費者

需要進行呼叫的時候會

透過註冊中心找到相應的服務的地址列表(IP埠什麼的)

,並快取到本地(方便以後呼叫),當消費者呼叫服務時,不會再去請求註冊中心,而是直接透過負載均衡演算法從地址列表中取一個服務提供者的伺服器呼叫服務。

當服務提供者的某臺伺服器宕機或下線時,相應的地址會從服務提供者地址列表中移除。同時,註冊中心會將新的服務地址列表傳送給服務消費者的機器並快取在消費者本機(當然你可以讓消費者進行節點監聽,我記得

Eureka

會先試錯,然後再更新)。

Zookeeper入門級萬字總結

總結

看到這裡的同學實在是太有耐心了 ,如果覺得我寫得不錯的話點個贊哈。

不知道大家是否還記得我講了什麼 。

Zookeeper入門級萬字總結

這篇文章中我帶大家入門了

zookeeper

這個強大的分散式協調框架。現在我們來簡單梳理一下整篇文章的內容。

分散式與叢集的區別

2PC

3PC

以及

paxos

演算法這些一致性框架的原理和實現。

zookeeper

專門的一致性演算法

ZAB

原子廣播協議的內容(

Leader

選舉、崩潰恢復、訊息廣播)。

zookeeper

中的一些基本概念,比如

ACL

,資料節點,會話,

watcher

機制等等。

zookeeper

的典型應用場景,比如選主,註冊中心等等。

如果忘了可以回去看看再次理解一下,如果有疑問和建議歡迎提出 。

作者:FrancisQ

連結:

https://

juejin。cn/post/68449040

45283377165

標簽: 節點  Leader  協調者  提案  事務