前言
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就狀态同步過程如下:
- Leader election(選舉階段):節點在一開始都處于選舉階段,隻要有一個節點得到超過半數節點的票數,它就可以當選準 Leader。
- Discovery(發現階段):在這個階段,Followers跟準Leader進行通信,同步Followers最近接收的事務提議。
- Synchronization(同步階段):同步階段主要是利用Leader前一階段獲得的最新提議曆史,同步叢集中所有的副本。同步完成之後準Leader才會成為真正的Leader。
- Broadcast(廣播階段): 到了這個階段,Zookeeper叢集才能正式對外提供事務服務,并且Leader 可以進行消息廣播。同時如果有新的節點加入,還需要對新節點進行同步。
watch機制
ZooKeeper支援一種Watch操作,Client可以在某個ZNode上設定一個Watcher,來Watch該ZNode上的變化。如果該ZNode上有相應的變化,就會觸發這個Watcher,把相應的事件通知給設定Watcher的Client。需要注意的是,ZooKeeper中的Watcher是一次性的,即觸發一次就會被取消,如果想繼續Watch的話,需要用戶端重新設定Watcher。
watch機制的特點:
- 一次性觸發資料發生改變時,一個watcher event會被發送到client, 但是client隻會收到一次這樣的資訊。
- watcher event異步發送watcher的通知事件從server發送到client是 異步的,這就存在一個問題,不同的用戶端和伺服器之間通過socket進 行通信,由于網絡延遲或其他因素導緻用戶端在不通的時刻監聽到事件, 由于Zookeeper本身提供了 ordering guarantee,即用戶端監聽事件後, 才會感覺它所監視znode發生了變化。是以我們使用Zookeeper不能期 望能夠監控到節點每次的變化。Zookeeper隻能保證最終的一緻性,而無 法保證強一緻性。
- 資料監視Zookeeper有資料監視和子資料監視getdata() and exists()設定資料監視,getchildren()設定了子節點監視。
- setData()執行成功則會觸發znode上設定的data watch。一個 成功的create()操作會觸發被建立的znode上的資料watch,以及其父節 點上的child watch。而一個成功的delete。操作将會同時觸發一個znode 的data watch和child watch(因為這樣就沒有子節點了),同時也會觸發其 父節點的child watch。
- 當一個用戶端連接配接到一個新的伺服器上時,watch将會被以任意會話 事件觸發。當與一個伺服器失去連接配接的時候,是無法接收到watch的。而 當client重新連接配接時,如果需要的話,所有先前注冊過的watch,都會被 重新注冊。通常這是完全透明的。隻有在一個特殊情況下,watch可能會 丢失:對于一個未建立的znode的exist watch,如果在用戶端斷開連接配接期 間被建立了,并且随後在用戶端連接配接上之前又删除了,這種情況下,這個 watch事件可能會被丢失。
- Watch是輕量級的,其實就是本地JVM的Callback,伺服器端隻是存 了是否有設定了 Watcher的布爾類型
資料複制
Zookeeper作為一個叢集提供一緻的資料服務,自然,它要在所有機器間 做資料複制。
資料複制的好處:
- 容錯:一個節點出錯,不緻于讓整個系統停止工作,别的節點可以接管它的工作;
- 提高系統的擴充能力:把負載分布到多個節點上,或者增加節點來提高系統的負載能力;
- 提高性能:讓用戶端本地通路就近的節點,提高使用者通路速度。
從用戶端讀寫通路的透明度來看,資料複制叢集系統分下面兩種:
- 寫主(WriteMaster):對資料的修改送出給指定的節點。讀無此限制,可 以讀取任何一個節點。這種情況下用戶端需要對讀與寫進行差別,俗稱讀 寫分離;
- 寫任意(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安裝
-
下載下傳
下載下傳位址:https://zookeeper.apache.org/releases.html#download
- 解壓縮(我是放在/usr/local/zk目錄下)
tar -zxvf apache-zookeeper-3.6.3-bin.tar.gz
- 修改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
- 配置環境變量
vi /etc/profile #添加以下内容 #export ZOOKEEPER=/usr/local/zk/zookeeper #export PATH=$PATH:$ZOOKEEPER/bin #使其生效 source /etc/profile
- 建立myid檔案
mkdir -p /usr/local/zk/zookeeper/zkdata cd /usr/local/zk/zookeeper/zkdata echo 1 > myid
- 分發安裝包到其他機器(我這裡在單機模拟叢集)
cp -r zookeeper zookeeper2
cp -r zookeeper zookeeper3
-
修改其他機器的配置檔案
在zookeeper2上:
vim /usr/local/zk/zookeeper2/zkdata/myid
修改myid為:2
在zookeeper3上:
修改myid為:3vim /usr/local/zk/zookeeper2/zkdata/myid
- 啟動zookeeper
zkServer.sh start start-foreground
- 檢視運作狀态
zkServer.sh status
- 關閉Zookeeper服務
zkServer.sh stop