Zookeeper到底是干嘛的


在Zookeeper的官網上有這么一句話:ZooKeeper is a centralized service for maintaining configuration information, naming, providing distributed synchronization, and providing group services. 

這大概描述了Zookeeper主要可以干哪些事情:配置管理,名字服務,提供分布式同步以及集群管理。那這些服務又到底是什么呢?我們為什么需要這樣的服務?我們又為什么要使用Zookeeper來實現呢,使用Zookeeper有什么優勢?接下來我會挨個介紹這些到底是什么,以及有哪些開源系統中使用了。

配置管理

在我們的應用中除了代碼外,還有一些就是各種配置。比如數據庫連接等。一般我們都是使用配置文件的方式,在代碼中引入這些配置文件。但是當我們只有一種配置,只有一台服務器,並且不經常修改的時候,使用配置文件是一個很好的做法,但是如果我們配置非常多,有很多服務器都需要這個配置,而且還可能是動態的話使用配置文件就不是個好主意了。這個時候往往需要尋找一種集中管理配置的方法,我們在這個集中的地方修改了配置,所有對這個配置感興趣的都可以獲得變更。比如我們可以把配置放在數據庫里,然后所有需要配置的服務都去這個數據庫讀取配置。但是,因為很多服務的正常運行都非常依賴這個配置,所以需要這個集中提供配置服務的服務具備很高的可靠性。一般我們可以用一個集群來提供這個配置服務,但是用集群提升可靠性,那如何保證配置在集群中的一致性呢? 這個時候就需要使用一種實現了一致性協議的服務了。Zookeeper就是這種服務,它使用Zab這種一致性協議來提供一致性。現在有很多開源項目使用Zookeeper來維護配置,比如在HBase中,客戶端就是連接一個Zookeeper,獲得必要的HBase集群的配置信息,然后才可以進一步操作。還有在開源的消息隊列Kafka中,也使用Zookeeper來維護broker的信息。在Alibaba開源的SOA框架Dubbo中也廣泛的使用Zookeeper管理一些配置來實現服務治理。

名字服務

名字服務這個就很好理解了。比如為了通過網絡訪問一個系統,我們得知道對方的IP地址,但是IP地址對人非常不友好,這個時候我們就需要使用域名來訪問。但是計算機是不能是別域名的。怎么辦呢?如果我們每台機器里都備有一份域名到IP地址的映射,這個倒是能解決一部分問題,但是如果域名對應的IP發生變化了又該怎么辦呢?於是我們有了DNS這個東西。我們只需要訪問一個大家熟知的(known)的點,它就會告訴你這個域名對應的IP是什么。在我們的應用中也會存在很多這類問題,特別是在我們的服務特別多的時候,如果我們在本地保存服務的地址的時候將非常不方便,但是如果我們只需要訪問一個大家都熟知的訪問點,這里提供統一的入口,那么維護起來將方便得多了。

分布式鎖

其實在第一篇文章中已經介紹了Zookeeper是一個分布式協調服務。這樣我們就可以利用Zookeeper來協調多個分布式進程之間的活動。比如在一個分布式環境中,為了提高可靠性,我們的集群的每台服務器上都部署着同樣的服務。但是,一件事情如果集群中的每個服務器都進行的話,那相互之間就要協調,編程起來將非常復雜。而如果我們只讓一個服務進行操作,那又存在單點。通常還有一種做法就是使用分布式鎖,在某個時刻只讓一個服務去干活,當這台服務出問題的時候鎖釋放,立即fail over到另外的服務。這在很多分布式系統中都是這么做,這種設計有一個更好聽的名字叫Leader Election(leader選舉)。比如HBase的Master就是采用這種機制。但要注意的是分布式鎖跟同一個進程的鎖還是有區別的,所以使用的時候要比同一個進程里的鎖更謹慎的使用。

集群管理

在分布式的集群中,經常會由於各種原因,比如硬件故障,軟件故障,網絡問題,有些節點會進進出出。有新的節點加入進來,也有老的節點退出集群。這個時候,集群中其他機器需要感知到這種變化,然后根據這種變化做出對應的決策。比如我們是一個分布式存儲系統,有一個中央控制節點負責存儲的分配,當有新的存儲進來的時候我們要根據現在集群目前的狀態來分配存儲節點。這個時候我們就需要動態感知到集群目前的狀態。還有,比如一個分布式的SOA架構中,服務是一個集群提供的,當消費者訪問某個服務時,就需要采用某種機制發現現在有哪些節點可以提供該服務(這也稱之為服務發現,比如Alibaba開源的SOA框架Dubbo就采用了Zookeeper作為服務發現的底層機制)。還有開源的Kafka隊列就采用了Zookeeper作為Cosnumer的上下線管理。

 

自己的一些理解:

Leader可以接受client請求,也接收其他Server轉發的寫請求,負責更新系統狀態。 Follower也可以接收client請求,如果是寫請求將轉發給Leader來更新系統狀態,讀請求則由Follower的內存數據庫直接響應。

                                          

 

 

事物操作    
在ZooKeeper中,能改變ZooKeeper服務器狀態的操作稱為事務操作。一般包括數據節點創建與刪除、數據內容更新和客戶端會話創建與失效等操作。對應每一個事務請求,ZooKeeper都會為其分配一個全局唯一的事務ID,用 ZXID 表示,通常是一個64位的數字。每一個 ZXID對應一次更新操作,從這些 ZXID 中可以間接地識別出 ZooKeeper 處理這些事務操作請求的
全局順序。

 

Watcher(事件監聽器)
ZooKeeper 中一個很重要的特性。ZooKeeper允許用戶在指定節點上注冊一些 Watcher,並且在一些特定事件觸發的時候,ZooKeeper服務端會將事件通知到感興趣的客戶端上去。該機制是 ZooKeeper 實現分布式協調服務的重要特性。

 

ZooKeeper數據模型的結構整體上可以看作是一棵樹,每個節點稱做一個ZNode。每個ZNode都可以通過其路徑唯一標識在每個ZNode上可存儲少量數據(默認是1M, 可以通過配置修改, 通常不建議在ZNode上存儲大量的數據)

 

ZooKeeper支持一種Watch操作,Client可以在某個ZNode上設置一個Watcher,來Watch該ZNode上的變化。如果該ZNode上有相應的變化,就會觸發這個Watcher,把相應的事件通知給設置Watcher的Client。需要注意的是,ZooKeeper中的Watcher是一次性的,即觸發一次就會被取消,如果想繼續Watch的話,需要客戶端重新設置Watcher

 

節點狀態
每個集群中的節點都有一個狀態 LOOKING, FOLLOWING, LEADING, OBSERVING。都屬於這4種,每個節點啟動的時候都是LOOKING狀態,如果這個節點參與選舉但最后不是leader,則狀態是FOLLOWING,如果不參與選舉則是OBSERVING,leader的狀態是LEADING。

 

關於ZooKeeper集群服務器數
ZooKeeper 官方確實給出了關於奇數的建議,但絕大部分 ZooKeeper 用戶對於這個建議認識有偏差。一個 ZooKeeper 集群如果要對外提供可用的服務,那么集群中必須要有過半的機器正常工作並且彼此之間能夠正常通信。基於這個特性,如果想搭建一個能夠允許 N 台機器 down 掉的集群,那么就要部署一個由 2*N+1 台服務器構成的 ZooKeeper 集群。因此,一個由 3 台機器構成的 ZooKeeper 集群,能夠在掛掉 1 台機器后依然正常工作,而對於一個由 5 台服務器構成的 ZooKeeper 集群,能夠對 2 台機器掛掉的情況進行容災。注意,如果是一個由6台服務器構成的 ZooKeeper 集群,同樣只能夠掛掉 2 台機器,因為如果掛掉 3 台,剩下的機器就無法實現過半了。

zookeeper有這樣一個特性:

【集群中只要有超過過半的機器是正常工作的,那么整個集群對外就是可用的】

 也就是說如果有2個zookeeper,那么只要有1個死了zookeeper就不能用了,因為1沒有過半,所以2個zookeeper的死亡容忍度為0;

同理,要是有3個zookeeper,一個死了,還剩下2個正常的,過半了,所以3個zookeeper的容忍度為1;

同理你多列舉幾個:2->0;3->1;4->1;5->2;6->2會發現一個規律,2n和2n-1的容忍度是一樣的,

都是n-1,所以為了更加高效,何必增加那一個不必要的zookeeper呢。

 

 

ZooKeeper可伸縮性
那么,ZooKeeper為什么要引入Observer這個角色呢?其實在ZooKeeper中引入Observer,主要是為了使ZooKeeper具有更好的可伸縮性。那么,何為可伸縮性?關於伸縮性,對於不同的人意味着不同的事情。 而在這里是說,如果我們的工作負載可以通過給系統分配更多的資源來分擔,那么這個系統就是可伸縮的;一個不可伸縮的系統卻無法通過增加資源來提升性能,甚至會在工作負載增加時,性能會急劇下降。
在Observer出現以前,ZooKeeper的伸縮性由Follower來實現,我們可以通過添加Follower節點的數量來保證ZooKeeper服務的讀性能。但是隨着Follower節點數量的增加,ZooKeeper服務的寫性能受到了影響。

 

簡單來說,Zab協議規定:來自Client的所有寫請求,都要轉發給ZK服務中唯一的ServerLeader,由Leader根據該請求發起一個Proposal(請求)。然后,其他的Server對該Proposal(請求)進行Vote(投票)。之后,Leader對Vote(投票)進行收集,當Vote數量過半時Leader會向所有的Server發送一個通知消息。最后,當Client所連接的Server收到該消息時,會把該操作更新到內存中並對Client的寫請求做出回應。

                              ZK 寫請求工作流程圖

                                                                               

從圖中我們可以看出, ZooKeeper 服務器在上述協議中實際扮演了兩個職能。它們一方面從客戶端接受連接與操作請求,另一方面對操作結果進行投票。這兩個職能在 ZooKeeper集群擴展的時候彼此制約。例如,當我們希望增加 ZK服務中Client數量的時候,那么我們就需要增加Server的數量,來支持這么多的客戶端。然而,從Zab協議對寫請求的處理過程中我們可以發現,增加服務器的數量,則增加了對協議中投票過程的壓力。因為Leader節點必須等待集群中過半Server響應投票,於是節點的增加使得部分計算機運行較慢,從而拖慢整個投票過程的可能性也隨之提高,寫操作也會隨之下降。這正是我們在實際操作中看到的問題——隨着 ZooKeeper 集群變大,寫操作的吞吐量會下降。

 所以,我們不得不得在增加Client數量的期望和我們希望保持較好吞吐性能的期望間進行權衡。要打破這一耦合關系,我們引入了不參與投票的服務器,稱為Observer。 Observer可以接受客戶端的連接,並將寫請求轉發給Leader節點。但是,Leader節點不會要求 Observer參加投票。相反,Observer不參與投票過程,僅僅在上述第3歩那樣,和其他服務節點一起得到投票結果。

 

Zookeeper工作流程-Leader

1 .恢復數據; 

2 .維持與Learner的心跳,接收Learner請求並判斷Learner的請求消息類型; 

3 .Learner的消息類型主要有PING消息、REQUEST消息、ACK消息、REVALIDATE消息,根據不同的消息類型,進行不同的處理。 

PING 消息是指Learner的心跳信息;

REQUEST消息是Follower發送的提議信息,包括寫請求及同步請求;

ACK消息是 Follower的對提議的回復,超過半數的Follower通過,則commit該提議;

REVALIDATE消息是用來延長SESSION有效時間。

                                                  

Zookeeper工作流程-Follower

Follower主要有四個功能: 

1.向Leader發送請求(PING消息、REQUEST消息、ACK消息、REVALIDATE消息); 

2.接收Leader消息並進行處理; 

3.接收Client的請求,如果為寫請求,發送給Leader進行投票;

4.返回Client結果。 


Follower的消息循環處理如下幾種來自Leader的消息: 

1 .PING消息: 心跳消息; 

2 .PROPOSAL消息:Leader發起的提案,要求Follower投票; 

3 .COMMIT消息:服務器端最新一次提案的信息; 

4 .UPTODATE消息:表明同步完成; 

5 .REVALIDATE消息:根據Leader的REVALIDATE結果,關閉待revalidate的session還是允許其接受消息; 

6 .SYNC消息:返回SYNC結果到客戶端,這個消息最初由客戶端發起,用來強制得到最新的更新。

                                         

 

Zookeeper節點數據操作流程

                             

注:

1.在Client向Follwer發出一個寫的請求

2.Follwer把請求發送給Leader

3.Leader接收到以后開始發起投票並通知Follwer進行投票

4.Follwer把投票結果發送給Leader

5.Leader將結果匯總后如果需要寫入,則開始寫入同時把寫入操作通知給Leader,然后commit;

6.Follwer把請求結果返回給Client

 

 

 

 Zookeeper設計目的

1.最終一致性:client不論連接到哪個Server,展示給它都是同一個視圖,這是zookeeper最重要的性能。 

2.可靠性:具有簡單、健壯、良好的性能,如果消息被到一台服務器接受,那么它將被所有的服務器接受。 

3.實時性:Zookeeper保證客戶端將在一個時間間隔范圍內獲得服務器的更新信息,或者服務器失效的信息。但由於網絡延時等原因,Zookeeper不能保證兩個客戶端能同時得到剛更新的數據,如果需要最新數據,應該在讀數據之前調用sync()接口。 

4.等待無關(wait-free):慢的或者失效的client不得干預快速的client的請求,使得每個client都能有效的等待。 

5.原子性:更新只能成功或者失敗,沒有中間狀態。 

6.順序性:包括全局有序和偏序兩種:全局有序是指如果在一台服務器上消息a在消息b前發布,則在所有Server上消息a都將在消息b前被發布;偏序是指如果一個消息b在消息a后被同一個發送者發布,a必將排在b前面。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM