天天看點

Zookeeper原理與實戰

前言

Zookeeeper介紹

  • 從服務角度來看:Zookeeper是一個分布式協調服務的開源架構,主要用來解決分布式叢集中應用系統的一緻性問題
  • 從資料結構角度來看:ZooKeeper本質上是一個分布式的小檔案存儲系統。提供基于類似于檔案系統的目錄樹方式的資料存儲,并且可以對樹中的節點進行有效管理。進而用來維護和監控你存儲的資料的狀态變化。通過監控這些資料狀态的變化,進而可以達到基于資料的叢集管理
  • 從設計模式角度來看:Zookeeper是一個分布式服務管理架構,它講注冊的服務作為資料進行存儲,一旦某些資料的狀态發生變化,Zookeeper就将負責通知已經在Zookeeper上注冊的那些服務做出相應的反應,進而實作叢集中類似Master/Slave管理模式。

Zookeeper特性

  • 最終資料一緻性:叢集中每個伺服器儲存一份相同的資料副本,client無論連接配接到哪個伺服器,展示的資料最終保證都是一緻的。
  • 可靠性:如果消息被其中一台伺服器接受,那麼将被所有的伺服器接受。
  • 順序性:包括全局有序和偏序兩種:全局有序是指如果在一台伺服器上消息a在消息b前釋出,則在所有Server上消息a都将在消息b前被釋出;偏序是指如果一個消息b在消息a後被同一個發送者釋出,a必将排在b前面。
  • 資料更新原子性:一次資料更新要麼成功(半數以上節點成功),要麼失敗,不存在中間狀态。
  • 實時性:Zookeeper保證用戶端将在一個時間間隔範圍内獲得伺服器的更新資訊,或者伺服器失效的資訊。

Zookeeper使用場景

  • 配置管理:在我們的應用中除了代碼外,還有各種配置檔案,比如資料庫連接配接等。如果我們配置非常多,有很多伺服器都需要這個配置,而且還還需要經常去修改,這個時候往往需要尋找一種集中管理配置的服務。Zookeeper将配置資訊放它的znode 下,當有配置發生改變時,也就是znode發生變化時,可以通過改變它某個目錄節點的内容,并利用watcher通知給各個用戶端,進而更改配置。
  • 名字服務:為了通過網絡通路一個系統,我們得知道對方的IP位址,但是IP位址對人非常不友好,特别是在我們的服務特别多的時候,如果我們在本地儲存服務的位址的時候将非常不友善,但是如果我們隻需要通路一個統一的入口,那麼維護起來将友善得多了。Zookeeper通過建立一個全局的路徑,即是唯一的路徑作為一個名字,指向叢集中提供的服務的位址,或者一個遠端的對象等。
  • 分布式鎖:在一個分布式環境中,為了提高可靠性,我們的叢集的每台伺服器上都部署着同樣的服務。但是,如果叢集中的每個伺服器使用相同的資源的話,那互相之間就要協調,而如果我們隻讓一個服務進行操作,那又存在單點,就可以使用分布式鎖,在某個時刻隻讓一個服務去使用共享資源。zookeeper上的一個znode可以看作是一把鎖,通過createznode的方式來實作。所有用戶端都去建立/distribute_lock 節點,最終成功建立的那個用戶端也即擁有了這把鎖。用完删除掉自己建立的distribute_lock節點就釋放出鎖。
  • 叢集管理:在分布式的叢集中,經常會由于硬體故障,軟體故障,網絡抖動等問題,有些節點會時常上下線切換。這個時候,叢集中其他機器需要感覺到這種變化,然後根據這種變化做出對應的決策。client端會對Zookeeper某個znode注冊一個watcher監聽事件,當該znode發生變化時,這些client會收到Zookeeper的通知,然後client可以根據znode變化來做出業務上的改變等。

Zookeeper原理

Zookeeper中的角色

  • Leader:上司者,負責進行投票的發起和決議,更新系統狀态
  • Learner:
    • Follower:追随者接收客戶請求并向用戶端傳回結果,在選主過程中進行投票
    • Observer:觀察者不參與投票,僅同步leader的狀态。目的是為了擴充系統,提高讀取速度。
  • Client:請求發起方

保證事務的順序一緻性

Zookeeper采用了 遞增的事務Id 來辨別,所有的proposal(提議)都在被提出的時候加上了 zxid ,

zxid實際上是一個64位的數字,高32位是 epoch用來辨別leader是否發生改變,如果有新的leader産生出來,epoch會自增,低32位用來遞增計數。當新産生 proposal的時候,會依據資料庫的兩階段過程,首先會向其他的server發出事務執行請求,如果超過半數的機器都能執行并且能夠成功,那麼就會 開始執行。

選舉機制

當leader崩潰或者leader失去大多數的follower,這時zookeeper進入恢複模式,恢複模式需要重新選舉出一個新的leader,讓所有的Server都恢複到一個正确的狀态。

Zk的選舉算法有兩種,一種是基于basic paxos 實作的,另外一種是基于 fast paxos 算法實作的。系統預設的選舉算法為fast paxos 。

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

同步機制

Zookeeper使用了Zab協定來保證各個Server之間的同步。

Zab協定有兩種模式,它們分别是 **恢複模式(選主) **和廣播模式(同步)。

當服務啟動或者在上司者崩潰後,Zab就進入了恢複模式,當上司者被選舉出來,且大多數Server完成了和leader的狀态同步以後,恢複模式就結束了。狀态同步保證了leader和Server 具有相同的系統狀态。

Zookeeper就狀态同步過程如下:

  1. Leader election(選舉階段):節點在一開始都處于選舉階段,隻要有一個節點得到超過半數節點的票數,它就可以當選準 Leader。
  2. Discovery(發現階段):在這個階段,Followers跟準Leader進行通信,同步Followers最近接收的事務提議。
  3. Synchronization(同步階段):同步階段主要是利用Leader前一階段獲得的最新提議曆史,同步叢集中所有的副本。同步完成之後準Leader才會成為真正的Leader。
  4. Broadcast(廣播階段): 到了這個階段,Zookeeper叢集才能正式對外提供事務服務,并且Leader 可以進行消息廣播。同時如果有新的節點加入,還需要對新節點進行同步。

watch機制

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

watch機制的特點:

  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. setData()執行成功則會觸發znode上設定的data watch。一個 成功的create()操作會觸發被建立的znode上的資料watch,以及其父節 點上的child watch。而一個成功的delete。操作将會同時觸發一個znode 的data watch和child watch(因為這樣就沒有子節點了),同時也會觸發其 父節點的child watch。
  5. 當一個用戶端連接配接到一個新的伺服器上時,watch将會被以任意會話 事件觸發。當與一個伺服器失去連接配接的時候,是無法接收到watch的。而 當client重新連接配接時,如果需要的話,所有先前注冊過的watch,都會被 重新注冊。通常這是完全透明的。隻有在一個特殊情況下,watch可能會 丢失:對于一個未建立的znode的exist watch,如果在用戶端斷開連接配接期 間被建立了,并且随後在用戶端連接配接上之前又删除了,這種情況下,這個 watch事件可能會被丢失。
  6. Watch是輕量級的,其實就是本地JVM的Callback,伺服器端隻是存 了是否有設定了 Watcher的布爾類型

資料複制

Zookeeper作為一個叢集提供一緻的資料服務,自然,它要在所有機器間 做資料複制。

資料複制的好處:

  1. 容錯:一個節點出錯,不緻于讓整個系統停止工作,别的節點可以接管它的工作;
  2. 提高系統的擴充能力:把負載分布到多個節點上,或者增加節點來提高系統的負載能力;
  3. 提高性能:讓用戶端本地通路就近的節點,提高使用者通路速度。

從用戶端讀寫通路的透明度來看,資料複制叢集系統分下面兩種:

  1. 寫主(WriteMaster):對資料的修改送出給指定的節點。讀無此限制,可 以讀取任何一個節點。這種情況下用戶端需要對讀與寫進行差別,俗稱讀 寫分離;
  2. 寫任意(Write Any):對資料的修改可送出給任意的節點,跟讀一樣。這 種情況下,用戶端對叢集節點的角色與變化透明。

對Zookeeper來說,它采用的方式是寫任意。通過增加機器,它的讀吞吐 能力和響應能力擴充性非常好,而寫,随着機器的增多吞吐能力肯定下降(這也是它建立observer的原因),而響應能力則取決于具體實作方式,是延遲複制保持最終一緻性,還是立即複制快速響應。

資料存儲

  • DataTree:DataTree是記憶體資料存儲的核心,是一個樹結構,代表了記憶體中一份完整的資料。DataTree不包含任何與網絡、用戶端連接配接及請求處理相關的業務邏輯,是一個獨立的元件。
  • DataNode:DataNode是資料存儲的最小單元,其内部除了儲存了結點的資料内容、ACL清單、節點狀态之外,還記錄了父節點的引用和子節點清單兩個屬性,其也提供了對子節點清單進行操作的接口。
  • ZKDatabase:Zookeeper的記憶體資料庫,管理Zookeeper的所有會話、DataTree存儲和事務日志。ZKDatabase會定時向磁盤dump快照資料,同時在Zookeeper啟動時,會通過磁盤的事務日志和快照檔案恢複成一個完整的記憶體資料庫。

ZooKeeper Client API

ZooKeeper Client Library提供了豐富直覺的API供使用者程式使用,下面是一些常用的API:

  • create(path, data, flags): 建立一個ZNode, path是其路徑,data是要存儲在該ZNode上的資料,flags常用的有: PERSISTEN, PERSISTENT_SEQUENTAIL, EPHEMERAL, EPHEMERAL_SEQUENTAIL
  • delete(path, version): 删除一個ZNode,可以通過version删除指定的版本, 如果version是-1的話,表示删除所有的版本
  • exists(path, watch): 判斷指定ZNode是否存在,并設定是否Watch這個ZNode。這裡如果要設定Watcher的話,Watcher是在建立ZooKeeper執行個體時指定的,如果要設定特定的Watcher的話,可以調用另一個重載版本的exists(path, watcher)。以下幾個帶watch參數的API也都類似
  • getData(path, watch): 讀取指定ZNode上的資料,并設定是否watch這個ZNode
  • setData(path, watch): 更新指定ZNode的資料,并設定是否Watch這個ZNode
  • getChildren(path, watch): 擷取指定ZNode的所有子ZNode的名字,并設定是否Watch這個ZNode
  • sync(path): 把所有在sync之前的更新操作都進行同步,達到每個請求都在半數以上的ZooKeeper Server上生效。path參數目前沒有用
  • setAcl(path, acl): 設定指定ZNode的Acl資訊
  • getAcl(path): 擷取指定ZNode的Acl資訊

Zookeeper安裝

  1. 下載下傳

    下載下傳位址:https://zookeeper.apache.org/releases.html#download

  2. 解壓縮(我是放在/usr/local/zk目錄下)

    tar -zxvf apache-zookeeper-3.6.3-bin.tar.gz

  3. 修改zookeeper配置
    #zookeeper根目錄改個名字
    mv apache-zookeeper-3.6.3-bin zookeeper
    #進入conf目錄下
    cd zookeeper/conf
    #zookeeper的配置檔案zoo.cfg,可複制conf/zoo_sample.cfg
    cp zoo_sample.cfg zoo.cfg
    vim zoo.cfg
    #修改内容
    dataDir=/usr/local/zk/zookeeper/data  
    dataLogDir=/usr/local/zk/zookeeper/log  
    #添加内容(節點位址:同步端口:選舉端口) 
    server.1=192.168.124.18:2881:3881  
    server.2=192.168.124.18:2882:3882  
    server.3=192.168.124.18:2883:3883
               
  4. 配置環境變量
    vi /etc/profile
    #添加以下内容
    #export ZOOKEEPER=/usr/local/zk/zookeeper
    #export PATH=$PATH:$ZOOKEEPER/bin
    
    #使其生效
    source /etc/profile
               
  5. 建立myid檔案
    mkdir -p /usr/local/zk/zookeeper/zkdata
    cd  /usr/local/zk/zookeeper/zkdata  
    echo 1 > myid
               
  6. 分發安裝包到其他機器(我這裡在單機模拟叢集)

    cp -r zookeeper zookeeper2

    cp -r zookeeper zookeeper3

  7. 修改其他機器的配置檔案

    在zookeeper2上:

    vim /usr/local/zk/zookeeper2/zkdata/myid

    修改myid為:2

    在zookeeper3上:

    vim /usr/local/zk/zookeeper2/zkdata/myid

    修改myid為:3
  8. 啟動zookeeper

    zkServer.sh start start-foreground

  9. 檢視運作狀态

    zkServer.sh status

  10. 關閉Zookeeper服務

    zkServer.sh stop

繼續閱讀