1. 程式人生 > >Zookeeper學習中的疑難問題總結,很受用!

Zookeeper學習中的疑難問題總結,很受用!

1、ZooKeeper是什麼

ZooKeeper是一個分散式的,開放原始碼的分散式應用程式協調服務,是Google的Chubby一個開源的實現,它是叢集的管理者,監視著叢集中各個節點的狀態根據節點提交的反饋進行下一步合理操作。最終,將簡單易用的介面和效能高效、功能穩定的系統提供給使用者。
   
客戶端的讀請求可以被叢集中的任意一臺機器處理,如果讀請求在節點上註冊了監聽器,這個監聽器也是由所連線的zookeeper機器來處理。對於寫請求,這些請求會同時發給其他zookeeper機器並且達成一致後,請求才會返回成功。因此,隨著zookeeper的叢集機器增多,讀請求的吞吐會提高但是寫請求的吞吐會下降。


有序性是zookeeper中非常重要的一個特性,所有的更新都是全域性有序的,每個更新都有一個唯一的時間戳,這個時間戳稱為zxid(Zookeeper Transaction Id)。而讀請求只會相對於更新有序,也就是讀請求的返回結果中會帶有這個zookeeper最新的zxid。

2、ZooKeeper提供了什麼?

1檔案系統
2通知機制

以上2點是整個zookeeper的核心所在。

2.1、Zookeeper檔案系統

Zookeeper提供一個多層級的節點名稱空間(節點稱為znode)。與檔案系統不同的是,這些節點都可以設定關聯的資料,而檔案系統中只有檔案節點可以存放資料而目錄節點不行。

Zookeeper為了保證高吞吐和低延遲,在記憶體中維護了這個樹狀的目錄結構,這種特性使得Zookeeper不能用於存放大量的資料,每個節點的存放資料上限為1M

2.1.1、四種類型的znode

1PERSISTENT-持久化目錄節點 
客戶端與zookeeper斷開連線後,該節點依舊存在 
2、
PERSISTENT_SEQUENTIAL-持久化順序編號目錄節點
客戶端與zookeeper斷開連線後,該節點依舊存在,只是Zookeeper給該節點名稱進行順序編號 
3、
EPHEMERAL-臨時目錄節點
客戶端與zookeeper斷開連線後,該節點被刪除 
4、
EPHEMERAL_SEQUENTIAL

-臨時順序編號目錄節點
客戶端與zookeeper斷開連線後,該節點被刪除,只是Zookeeper給該節點名稱進行順序編號

2.2、Zookeeper通知機制

client端會對某個znode建立一個watcher事件,當該znode發生變化時,這些client會收到zookeeper的通知,然後client可以根據znode變化來做出業務上的改變等。

3、Zookeeper做了什麼?

1、命名服務
2、配置管理
3、叢集管理
4、分散式鎖
5、佇列管理

3.1、zookeeper的命名服務(基於檔案系統)

命名服務是指通過指定的名字來獲取資源或者服務的地址,利用zookeeper建立一個全域性的路徑,即是唯一的路徑,這個路徑就可以作為一個名字,指向叢集中的叢集,提供的服務的地址,或者一個遠端的物件等等。

3.2、zookeeper的配置管理(基於檔案系統、通知機制)

程式分散式的部署在不同的機器上,將程式的配置資訊放在zookeeperznode下,當有配置發生改變時,也就是znode發生變化時,可以通過改變zookeeper中某個目錄節點的內容,利用watcher通知給各個客戶端,從而更改配置。

3.3、Zookeeper的叢集管理(基於檔案系統、通知機制)

所謂叢集管理無在乎兩點:是否有機器退出和加入、選舉master。 
對於第一點,所有機器約定在父目錄下
建立臨時目錄節點,然後監聽父目錄節點的子節點變化訊息。一旦有機器掛掉,該機器與 zookeeper的連線斷開,其所建立的臨時目錄節點被刪除,所有其他機器都收到通知:某個兄弟目錄被刪除,於是,所有人都知道:它下船了。
新機器加入也是類似,
所有機器收到通知:新兄弟目錄加入,highcount又有了,對於第二點,我們稍微改變一下,所有機器建立臨時順序編號目錄節點,每次選取編號最小的機器作為master就好(也是為了資料安全考慮,一般都是預設最大值是作為master的。)

3.4、Zookeeper的分散式鎖(基於檔案系統、通知機制)

有了zookeeper的一致性檔案系統(唯一性),鎖的問題變得容易。鎖服務可以分為兩類,一個是保持獨佔,另一個是控制時序。 
保持獨佔:我們將zookeeper上的一個
znode看作是一把鎖通過createznode的方式來實現。所有客戶端都去建立 /distribute_lock 節點,最終成功建立的那個客戶端也即擁有了這把鎖。用完刪除掉自己建立的distribute_lock 節點就釋放出鎖。 
控制時序:如果 /distribute_lock 已經預先存在,所有客戶端在它下面建立臨時順序編號目錄節點,和選master一樣,
編號最小的獲得鎖,用完刪除,依次方便。

3.4.1、獲取分散式鎖的流程(基於檔案系統、通知機制)


在獲取分散式鎖的時候在locker節點下建立臨時順序節點,釋放鎖的時候刪除該臨時節點。客戶端呼叫createNode方法在locker下建立臨時順序節點,然後呼叫getChildren(“locker”)來獲取locker下面的所有子節點,注意此時不用設定任何Watcher。客戶端獲取到所有的子節點path之後,如果發現自己建立的節點在所有建立的子節點序號最小,那麼就認為該客戶端獲取到了鎖。如果發現自己建立的節點並非locker所有子節點中最小的,說明自己還沒有獲取到鎖,此時客戶端需要找到比自己小的那個節點,然後對其呼叫exist()方法,同時對其註冊事件監聽器。之後,讓這個被關注的節點刪除,則客戶端的Watcher會收到相應通知,此時再次判斷自己建立的節點是否是locker子節點中序號最小的,如果是則獲取到了鎖,如果不是則重複以上步驟繼續獲取到比自己小的一個節點並註冊監聽。當前這個過程中還需要許多的邏輯判斷。

程式碼的實現主要是基於互斥鎖,獲取分散式鎖的重點邏輯在BaseDistributedLock,實現了基於Zookeeper實現分散式鎖的細節。

3.4.2、Zookeeper佇列管理(檔案系統、通知機制)

兩種型別的佇列:
1、
同步佇列,當一個佇列的成員都聚齊時,這個佇列才可用,否則一直等待所有成員到達。 
2、
佇列按照,FIFO (先進先出)方式進行入隊和出隊操作。 
第一類,在約定目錄下建立臨時目錄節點,監聽節點數目是否是我們要求的數目。 
第二類,和分散式鎖服務中的控制時序場景基本原理一致,入列有編號,出列按編號。在特定的目錄下建立
PERSISTENT_SEQUENTIAL(持久化順序)節點,建立成功時Watcher通知等待的佇列,佇列刪除序列號最小的節點用以消費。此場景下Zookeeper的znode用於訊息儲存,znode儲存的資料就是訊息佇列中的訊息內容,SEQUENTIAL序列號就是訊息的編號,按序取出即可。由於建立的節點是持久化的,所以不必擔心佇列訊息的丟失問題

3.5、Zookeeper資料複製

Zookeeper作為一個叢集提供一致的資料服務,自然,它要在所有機器間做資料複製。資料複製的好處: 
1、
容錯:一個節點出錯,不致於讓整個系統停止工作,別的節點可以接管它的工作; 
2、
提高系統的擴充套件能力 :把負載分佈到多個節點上,或者增加節點來提高系統的負載能力; 
3、
提高效能讓客戶端本地訪問就近的節點,提高使用者訪問速度

從客戶端讀寫訪問的透明度來看,資料複製集群系統分下面兩種: 
1、
寫主(Write Master) 對資料的修改提交給指定的節點讀無此限制,可以讀取任何一個節點。這種情況下客戶端需要對讀與寫進行區別,俗稱讀寫分離; 
2、
寫任意(Write Any)對資料的修改可提交給任意的節點,跟讀一樣。這種情況下,客戶端對叢集節點的角色與變化透明。對zookeeper來說,它採用的方式是寫任意。通過增加機器,它的讀吞吐能力和響應能力擴充套件性非常好,而寫,隨著機器的增多吞吐能力肯定下降(這也是它建立observer的原因),而響應能力則取決於具體實現方式,是延遲複製保持最終一致性,還是立即複製快速響應

4、Zookeeper工作原理

Zookeeper 的核心是原子廣播,這個機制保證了各個Server之間的同步。實現這個機制的協議叫做Zab協議。Zab協議有兩種模式,它們分別是恢復模式(選主)廣播模式(同步)。當服務啟動或者在領導者崩潰後,Zab就進入了恢復模式,當領導者被選舉出來,且大多數Server完成了和 leader的狀態同步以後,恢復模式就結束了。狀態同步保證了leader和Server具有相同的系統狀態。

5、zookeeper是如何保證事務的順序一致性的?

zookeeper採用了遞增的事務Id來標識,所有的proposal(提議)都在被提出的時候加上了zxid,zxid實際上是一個64位的數字,高32位是epoch(時期; 紀元; 世; 新時代)用來標識leader是否發生改變,如果有新的leader產生出來,epoch會自增,低32位用來遞增計數。當新產生proposal的時候,會依據資料庫的兩階段過程,首先會向其他的server發出事務執行請求,如果超過半數的機器都能執行並且能夠成功,那麼就會開始執行。

6、Zookeeper 下 Server工作狀態

每個Server在工作過程中有三種狀態: 
LOOKING:當前Server不知道leader是誰,正在搜尋
LEADING:當前Server即為選舉出來的leader
FOLLOWING:leader已經選舉出來,當前Server與之同步

7、zookeeper是如何選取主leader的?

當leader崩潰或者leader失去大多數的follower,這時zookeeper進入恢復模式,恢復模式需要重新選舉出一個新的leader,讓所有的Server都恢復到一個正確的狀態。Zookeeper的選舉演算法有兩種:一種是基於basic paxos實現的,另外一種是基於fast paxos演算法實現的。系統預設的選舉演算法為fast paxos

7.1、Zookeeper選主流程(basic paxos)
(1)選舉執行緒由當前Server發起選舉的執行緒擔任,其主要功能是對投票結果進行統計,並選出推薦的Server; 
(2)選舉執行緒首先向所有Server發起一次詢問(包括自己); 
(3)選舉執行緒收到回覆後,驗證是否是自己發起的詢問(驗證zxid是否一致),然後獲取對方的id(myid),並存儲到當前詢問物件列表中,最後獲取對方提議的leader相關資訊(id,zxid),並將這些資訊儲存到當次選舉的投票記錄表中; 
(4)收到所有Server回覆以後,就計算出zxid最大的那個Server,並將這個Server相關資訊設定成下一次要投票的Server; 
(5)執行緒將當前zxid最大的Server設定為當前Server要推薦的Leader,如果此時獲勝的Server獲得n/2 + 1的Server票數,設定當前推薦的leader為獲勝的Server,將根據獲勝的Server相關資訊設定自己的狀態,否則,繼續這個過程,直到leader被選舉出來。 通過流程分析我們可以得出:要使Leader獲得多數Server的支援,則Server總數必須是奇數2n+1,且存活的Server的數目不得少於n+1. 每個Server啟動後都會重複以上流程。在恢復模式下,如果是剛從崩潰狀態恢復的或者剛啟動的server還會從磁碟快照中恢復資料和會話資訊,zookeeper會記錄事務日誌並定期進行快照,方便在恢復時進行狀態恢復。

7.2、Zookeeper選主流程(basic paxos)
fast paxos流程是在選舉過程中,某Server首先向所有Server提議自己要成為leader,當其它Server收到提議以後,解決epoch和 zxid的衝突,並接受對方的提議,然後向對方傳送接受提議完成的訊息,重複這個流程,最後一定能選舉出Leader。

8、Zookeeper同步流程

選完Leader以後,zookeeper就進入狀態同步過程。 
1、Leader等待server連線; 
2、Follower連線leader,將最大的zxid傳送給leader; 
3、Leader根據follower的zxid確定同步點; 
4、完成同步後通知follower 已經成為uptodate狀態; 
5、Follower收到uptodate訊息後,又可以重新接受client的請求進行服務了。

9、分散式通知和協調

對於系統排程來說:操作人員傳送通知實際是通過控制檯改變某個節點的狀態然後zookeeper將這些變化傳送給註冊了這個節點的watcher的所有客戶端
對於執行情況彙報:每個工作程序都在某個目錄下建立一個臨時節點並攜帶工作的進度資料,這樣彙總的程序可以監控目錄子節點的變化獲得工作進度的實時的全域性情況

10、機器中為什麼會有leader?

在分散式環境中,有些業務邏輯只需要叢集中的某一臺機器進行執行,其他的機器可以共享這個結果,這樣可以大大減少重複計算提高效能,於是就需要進行leader選舉。

11、zookeeper節點宕機如何處理?

1、Zookeeper本身也是叢集,推薦配置不少於3個伺服器(至少2*N+1)。Zookeeper自身也要保證當一個節點宕機時,其他節點會繼續提供服務。
2、如果是一個Follower宕機,還有2臺伺服器提供訪問,因為Zookeeper上的資料是有多個副本的,資料並不會丟失;
3、如果是一個Leader宕機,Zookeeper會選舉出新的Leader。
Zookeeper叢集的機制是隻要超過半數的節點正常,叢集就能正常提供服務。只有在Zookeeper節點掛得太多,只剩一半或不到一半節點能工作,叢集才失效。
所以3個節點的cluster可以掛掉1個節點(leader可以得到2票>1.5)
2個節點的cluster就不能掛掉任何1個節點了(leader可以得到1票<=1)

12、zookeeper負載均衡和nginx負載均衡區別

zookeeper的負載均衡(模仿DNS的域名解析)是可以調控,nginx只是能調權重,其他需要可控的都需要自己寫外掛;但是nginx的吞吐量比zookeeper大很多,應該說按業務選擇用哪種方式。

13、zookeeper watch機制

Watch機制官方宣告:一個Watch事件是一個一次性的觸發器,當被設定了Watch的資料發生了改變的時候,則伺服器將這個改變傳送給設定了Watch的客戶端,以便通知它們。
Zookeeper機制的特點:
1、一次性觸發資料發生改變時,一個watcher event會被髮送到client,但是client只會收到一次這樣的資訊
2、watcher event非同步傳送watcher的通知事件從server傳送到client是
非同步的,這就存在一個問題,不同的客戶端和伺服器之間通過socket進行通訊,由於網路延遲或其他因素導致客戶端在不通的時刻監聽到事件,由於Zookeeper本身提供了ordering guarantee,即客戶端監聽事件後,才會感知它所監視znode發生了變化。所以我們使用Zookeeper不能期望能夠監控到節點每次的變化。Zookeeper只能保證最終的一致性,而無法保證強一致性
3、資料監視Zookeeper有資料監視和子資料監視
getdata() and exists()設定資料監視,getchildren()設定了子節點監視。
4、註冊watcher getData、exists、getChildren
5、觸發watcher create、delete、setData
6、setData()會觸發znode上設定的data watch(如果set成功的話)。一個成功的create() 操作會觸發被建立的znode上的資料watch,以及其父節點上的child watch。而一個成功的delete()操作將會同時觸發一個znode的data watch和child watch(因為這樣就沒有子節點了),同時也會觸發其父節點的child watch。
7、當一個客戶端連線到一個新的伺服器上時,watch將會被以任意會話事件觸發。當與一個伺服器失去連線的時候,是無法接收到watch的。而當client重新連線時,如果需要的話,所有先前註冊過的watch,都會被重新註冊。通常這是完全透明的。只有在一個特殊情況下,watch可能會丟失對於一個未建立的znode的exist watch,如果在客戶端斷開連線期間被建立了,並且隨後在客戶端連線上之前又刪除了,這種情況下,這個watch事件可能會被丟失
8、Watch是輕量級的,其實就是本地JVM的Callback,伺服器端只是存了是否有設定了Watcher的布林型別