Zookeeper 介紹 原理


簡介:

         ZooKeeper是一個分布式的,開放源碼的分布式應用程序協調服務。
       它Google的Chubby一個開源的實現,在分布式協調技術方面做得比較好的就是Google的Chubby還有Apache的ZooKeeper
他們都是分布式鎖的實現者。
        有人會問 既然有了Chubby為什么還要弄一個ZooKeeper,難道Chubby做得不夠好嗎?不是這樣的,主要是Chbby是非開源的,Google自家 用。
后來雅虎模仿Chubby開發出了ZooKeeper,也實現了類似的分布式鎖的功能,並且將ZooKeeper作為一種開源的程序捐獻給了 Apache
        Zookeeper在分布式領域久經考驗,它的可靠性,可用性都是經過理論和實踐的驗證的。所以我們 在構建一些分布式系統的時候,就可以以這類系統為起點來構建我們的系統,這將節省不少成本,而且bug也 將更少。

       Zookeeper是Hadoop和Hbase的重要組件。Zookeeper 分布式服務框架是Apache Hadoop 的一個子項目,
它是一個為分布式應用提供一致性服務的軟件,提供的功能包括:配置維護、域名服務、分布式同步、組服務等。
   

        ZooKeeper是一種為分布式應用所設計的高可用、高性能且一致的開源協調服務,它提供了一項基本服務:分布式鎖服務
由於ZooKeeper的開源特性,后來我們的開發者在分布式鎖的基礎上,摸索了出了其他的使用方法:
配置維護、組服務、分布式消息隊列分布式通知/協調等。

 

 

角色

Zookeeper中的角色主要有以下三類,如下表所示:

                         zookeeper簡介

系統模型如圖所示:

                         zookeeper簡介        

 設計目的

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

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

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

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

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

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

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

        為了保證事務的順序一致性,zookeeper采用了遞增的事務id號(zxid)來標識事務。
所有的提議(proposal)都在被提出的時候加上 了zxid。
實現中zxid是一個64位的數字,它高32位是epoch用來標識leader關系是否改變,每次一個leader被選出來,它都會有一個 新的epoch,標識當前屬於那個leader的統治時期。
低32位用於遞增計數。

每個Server在工作過程中有三種狀態:

  • LOOKING:當前Server不知道leader是誰,正在搜尋
  • LEADING:當前Server即為選舉出來的leader
  • FOLLOWING:leader已經選舉出來,當前Server與之同步


選主流程

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

同步流程

選完leader以后,zk就進入狀態同步過程。

        1. leader等待server連接;

        2 .Follower連接leader,將最大的zxid發送給leader;

        3 .Leader根據follower的zxid確定同步點;

        4 .完成同步后通知follower 已經成為uptodate狀態;

        5 .Follower收到uptodate消息后,又可以重新接受client的請求進行服務了。

 

Leader工作流程

Leader主要有三個功能:

        1 .恢復數據;

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

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

        PING消息是指Learner的心跳信息;
        REQUEST消息是Follower發送的提議信息,包括寫請求及同步請求;
        ACK消息是 Follower的對提議的回復,超過半數的Follower通過,則commit該提議; 
        REVALIDATE消息是用來延長SESSION有效時間。
        Leader的工作啟動了三個線程來實現功能。

 

 

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結果到客戶端,這個消息最初由客戶端發起,用來強制得到最新的更新。

 

       Follower的工作是通過5個線程來實現功能的。



 

 

 

原理:

ZooKeeper在實現這些服務時,首先它設計一種新的數據結構——Znode,
然后在該數據結構的基礎上定義了一些原語,也就是一些關於該數據結構的一些操作。
有了這些數據結構和原語還不夠,因為我們的ZooKeeper是工作在一個分布式的環境下,我們的服務是通過消息以網絡的形式發送給我們的分布式應用程序,所以還需要一個通知機制——Watcher機制。
那么總結一下,ZooKeeper所提供的服務主要是通過:數據結構+原語+watcher機制,三個部分來實現的

ZooKeeper數據模型Znode

ZooKeeper擁有一個層次的命名空間,這個和標准的文件系統非常相似,如下圖所示。

從圖中我們可以看出ZooKeeper的數據模型,在結構上和標准文件系統的非常相似,都是采用這種樹形層次結構,
ZooKeeper樹中的每個節點被稱為—Znode。和文件系統的目錄樹一樣,ZooKeeper樹中的每個節點可以擁有子節點。但也有不同之處:

(1) 引用方式

Zonde通過路徑引用,如同Unix中的文件路徑。路徑必須是絕對的,必須是唯一的
路徑由Unicode字符串組成,並且有一些限制。
字符串"/zookeeper"用以保存管理信息,比如關鍵配額信息。

(2) Znode結構

ZooKeeper命名空間中的Znode,兼具文件和目錄兩種特點。
既像文件一樣維護着數據、元信息、ACL、時間戳等數據結構,又像目錄一樣可以作為路徑標識的一部分。
圖中的每個節點稱為一個Znode。 每個Znode由3部分組成:

 stat:此為狀態信息, 描述該Znode的版本, 權限等信息

 data:與該Znode關聯的數據

 children:該Znode下的子節點

ZooKeeper雖然可以關聯一些數據,但並沒有被設計為常規的數據庫或者大數據存儲
相反的是,它用來管理調度數據,比如分布式應用中的配置文件信息、狀態信息、匯集位置等等。
這些數據的共同特性就是它們都是很小的數據,通常以KB為大小單位。
ZooKeeper的服務器和客戶端都被設計為嚴格檢查並限制每個Znode的數據大小至多1M,但常規使用中應該遠小於此值。

(3) 數據訪問

ZooKeeper中的每個節點存儲的數據要被原子性的操作。也就是說讀操作將獲取與節點相關的所有數據,寫操作也將替換掉節點的所有數據。
另外,每一個節點都擁有自己的ACL(訪問控制列表),這個列表規定了用戶的權限,即限定了特定用戶對目標節點可以執行的操作。

(4) 節點類型

ZooKeeper中的節點有兩種,分別為臨時節點永久節點
節點的類型在創建時即被確定,並且不能改變。

① 臨時節點該節點的生命周期依賴於創建它們的會話。一旦會話(Session)結束,臨時節點將被自動刪除,當然可以也可以手動刪除。
雖然每個臨時的Znode都會綁定到一個客戶端會話,但他們對所有的客戶端還是可見的。另外,ZooKeeper的臨時節點不允許擁有子節點。

② 永久節點該節點的生命周期不依賴於會話,並且只有在客戶端顯示執行刪除操作的時候,他們才能被刪除。

(5) 順序節點

當創建Znode的時候,用戶可以請求在ZooKeeper的路徑結尾添加一個遞增的計數
這個計數對於此節點的父節點來說是唯一的,
它的格式為"%10d"(10位數字,沒有數值的數位用0補充,例如"0000000001")。當計數值大於232-1時,計數器將溢出。

(6) 觀察

客戶端可以在節點上設置watch,我們稱之為監視器
當節點狀態發生改變時(Znode的增、刪、改)將會觸發watch所對應的操作。當watch被觸發時,ZooKeeper將會向客戶端發送且僅發送一條通知,
因為watch只能被觸發一次,這樣可以減少網絡流量。

 

 

ZooKeeper中的時間

 

ZooKeeper有多種記錄時間的形式,其中包含以下幾個主要屬性:

 

(1) Zxid

 

致使ZooKeeper節點狀態改變的每一個操作都將使節點接收到一個Zxid格式的時間戳,並且這個時間戳全局有序。
也就是說,每個對 節點的改變都將產生一個唯一的Zxid。
如果Zxid1的值小於Zxid2的值,那么Zxid1所對應的事件發生在Zxid2所對應的事件之前。

實際 上,ZooKeeper的每個節點維護者三個Zxid值,為別為:cZxid、mZxid、pZxid

 

 cZxid: 是節點的創建時間所對應的Zxid格式時間戳。
② mZxid:是節點的修改時間所對應的Zxid格式時間戳。

 

實際中Zxid是一個64為的數字,它高32位是epoch用來標識leader關系是否改變,每次一個leader被選出來,它都會有一個 新的epoch。
低32位是個遞增計數

 (2) 版本號

 

對節點的每一個操作都將致使這個節點的版本號增加。每個節點維護着三個版本號,他們分別為:

 

① version:節點數據版本號
② cversion:子節點版本號
③ aversion:節點所擁有的ACL版本號

 

4.3 ZooKeeper節點屬性

 

通過前面的介紹,我們可以了解到,一個節點自身擁有表示其狀態的許多重要屬性,如下圖所示。

 

圖 4.2 Znode節點屬性結構

 

 


ZooKeeper服務中操作

 

在ZooKeeper中有9個基本操作,如下圖所示:

 

圖 5.1 ZooKeeper類方法描述

 

 

更新ZooKeeper操作是有限制的。
delete或setData必須明確要更新的Znode的版本號,我們可以調用exists找到。
如果版本號不匹配,更新將會失敗。

 

更新ZooKeeper操作是非阻塞式的。
因此客戶端如果失去了一個更新(由於另一個進程在同時更新這個Znode),
他可以在不阻塞其他進程執行的情況下,選擇重新嘗試或進行其他操作。

 

盡管ZooKeeper可以被看做是一個文件系統,但是處於便利,摒棄了一些文件系統地操作原語。
因為文件非常的小並且使整體讀寫的,所以不需要打開、關閉或是尋地的操作。

 

Watch觸發器

(1) watch概述

ZooKeeper可以為所有的讀操作設置watch,這些讀操作包括:exists()、getChildren()及getData()。
watch事件是一次性的觸發器,當watch的對象狀態發生改變時,將會觸發此對象上watch所對應的事件。
watch事件將被異步地發送給客戶端,並且ZooKeeper為watch機制提供了有序的一致性保證
理論上,客戶端接收watch事件的時間要快於其看到watch對象狀態變化的時間。

(2) watch類型

ZooKeeper所管理的watch可以分為兩類:

 數據watch(data  watches):getDataexists負責設置數據watch
② 孩子watch(child watches):getChildren負責設置孩子watch

我們可以通過操作返回的數據來設置不同的watch:

① getData和exists:返回關於節點的數據信息
② getChildren:返回孩子列表

因此

① 一個成功的setData操作將觸發Znode的數據watch

 一個成功的create操作將觸發Znode的數據watch以及孩子watch

③ 一個成功的delete操作將觸發Znode的數據watch以及孩子watch

(3) watch注冊與處觸發

圖 6.1 watch設置操作及相應的觸發器如圖下圖所示:

① exists操作上的watch,在被監視的Znode創建刪除數據更新時被觸發。
 getData操作上的watch,在被監視的Znode刪除數據更新時被觸發。在被創建時不能被觸發,因為只有Znode一定存在,getData操作才會成功。
 getChildren操作上的watch,在被監視的Znode的子節點創建刪除,或是這個Znode自身被刪除時被觸發。可以通過查看watch事件類型來區分是Znode,還是他的子節點被刪除:NodeDelete表示Znode被刪除,NodeDeletedChanged表示子節點被刪除。

Watch由客戶端所連接的ZooKeeper服務器在本地維護,因此watch可以非常容易地設置、管理和分派。當客戶端連接到一個新的服務器 時,任何的會話事件都將可能觸發watch。另外,當從服務器斷開連接的時候,watch將不會被接收。但是,當一個客戶端重新建立連接的時候,任何先前 注冊過的watch都會被重新注冊。

(4) 需要注意的幾點

Zookeeper的watch實際上要處理兩類事件:

① 連接狀態事件(type=None, path=null)

這類事件不需要注冊,也不需要我們連續觸發,我們只要處理就行了。

② 節點事件

節點的建立,刪除,數據的修改。它是one time trigger,我們需要不停的注冊觸發,還可能發生事件丟失的情況。

上面2類事件都在Watch中處理,也就是重載的process(Event event)

節點事件的觸發,通過函數exists,getData或getChildren來處理這類函數,有雙重作用:

① 注冊觸發事件

② 函數本身的功能

函數的本身的功能又可以用異步的回調函數來實現,重載processResult()過程中處理函數本身的的功能。

 


免責聲明!

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



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