Zookeeper是為了解決大型分布式系統(tǒng)的一系列問題而開發(fā)的一套協(xié)調(diào)服務(wù),它將復(fù)雜且容易出錯的分布一致性服務(wù)封裝起來,構(gòu)成了一個高效可靠的原語集,并以簡單易用的接口提供給用戶使用。它提供了發(fā)布/訂閱、分布式協(xié)調(diào)/通知、配置管理、集群管理、主從協(xié)調(diào)、分布式鎖等功能。
要明白Zookeeper的功能,先了解下分布式系統(tǒng)和這樣的系統(tǒng)遇到的問題
分布式系統(tǒng)
硬件/軟件組件分布在不同的計算機上,彼此之間通過消息進行協(xié)調(diào)和通信的系統(tǒng)即稱為分布式系統(tǒng)
分布式系統(tǒng)的特點
分布性? 分布式系統(tǒng)的布置在不同的服務(wù)器、不同的機架甚至不同的機房、不同的城市,它的物理位置很分散,而且可能由于環(huán)境因素隨時發(fā)生變化
對等性? 分布式系統(tǒng)無主從之分,每臺服務(wù)器都是相對公平的提供服務(wù),這樣方便實現(xiàn)數(shù)據(jù)及服務(wù)的高可用
并發(fā)性? ? 分布式系統(tǒng)可能出現(xiàn)同時對同一份數(shù)據(jù)進行操作的情況
缺乏全局時鐘? ? 各個服務(wù)之間的時間先后順序很難定義
故障隨時發(fā)生? ? 由于系統(tǒng)服務(wù)器眾多,故障也就隨時有可能發(fā)生
分布式系統(tǒng)的問題
通信異常? ? 由于分布式系統(tǒng)分布式的特點,網(wǎng)絡(luò)不可避免的會出現(xiàn)問題
網(wǎng)絡(luò)分區(qū)? ? 俗稱腦裂,由于分布式系統(tǒng)部分服務(wù)器的延遲,集群中只有部分節(jié)點能處理請求,而這樣就有可能造成斷網(wǎng)的節(jié)點也在處理請求,而斷網(wǎng)的節(jié)點間可能構(gòu)成了新的分布式系統(tǒng)對外提供服務(wù),這樣就導(dǎo)致了數(shù)據(jù)不一致
三態(tài)? ? 單機系統(tǒng)對一個請求的處理有成功、失敗兩種很明確的狀態(tài),但分布式系統(tǒng)還可以有超時狀態(tài)
節(jié)點故障
分布式系統(tǒng)-CAP定理
CAP即Consistency(一致性)、Availability(可用性)、Partition tolerance(分區(qū)容錯性),CAP定理指出:任何系統(tǒng) 不可能同時滿足上述三個特性,最多只能滿足其中兩項
而分區(qū)容錯性對于分布式系統(tǒng)來說是必須解決的問題,因而對于系統(tǒng)的優(yōu)化主要集中在一致性和可用性兩個方面
Zookeeper使用了最終一致性來使一致性和可用性達到了平衡,它在在解決分區(qū)容錯性和可用性的同時,保證數(shù)據(jù)達到最終一致性
Zookeeper特性
最終一致性? ? 系統(tǒng)數(shù)據(jù)經(jīng)過一定時間后,最終能達到一致
順序性? ? 從同一個客戶端發(fā)起的事務(wù)請求,最終會嚴格地按照其發(fā)送順序被應(yīng)用到Zookeeper中
可靠性? ? 一旦服務(wù)器成功應(yīng)用一個事務(wù)并完成了客戶端的響應(yīng),那么該事務(wù)所引起的服務(wù)端狀態(tài)變更會一直被保留下去
實時性? ??不能保證兩個客戶端能同時得到剛更新的數(shù)據(jù),如果需要最新數(shù)據(jù),需要在讀數(shù)據(jù)前調(diào)用sync()接口
原子性? ? 一次數(shù)據(jù)更新要么成功,要么失敗,沒有中間狀態(tài)
單一視圖? ? 無論客戶端連接到哪臺服務(wù)器,看到的數(shù)據(jù)模型都是一致的
Zookeeper的架構(gòu)

Zookeeper角色簡介
Leader : 從所有的follower中選舉出來,為客戶端提供讀寫服務(wù)及發(fā)起投票和決議,處理事務(wù)請求,更新系統(tǒng)狀態(tài)
Follower: Leaner角色的一種,負責客戶端的讀請求服務(wù),如果收到寫請求,則轉(zhuǎn)發(fā)給Leader,同時負責選舉
Observer:Leaner角色的一種,負責客戶端的讀請求服務(wù),將客戶端的寫請求轉(zhuǎn)發(fā)給Leader,但不負責選舉,Observer存在的目的是為了擴展系統(tǒng),提高讀寫速度。
Client: 請求發(fā)起方
Zookeeper寫入
數(shù)據(jù)寫入的核心算法:ZAB算法

1. 客戶端向Follower發(fā)送寫請求
2. Follower收到客戶端的寫請求后,將寫請求發(fā)送給Leader
3.Leader接收到以后開始發(fā)起投票并通知Follwer進行投票
4.Follwer把投票結(jié)果發(fā)送給Leader
5.Leader將結(jié)果匯總后如果需要寫入,則開始寫入同時把寫入操作通知給Leader,然后commit;
6.Follwer把請求結(jié)果返回給Client
Zookeeper選舉簡介
服務(wù)器四種狀態(tài)
LOOKING :尋找leader狀態(tài),處于該狀態(tài)需要進入選舉流程
LEADING: 領(lǐng)導(dǎo)者狀態(tài),Leader已經(jīng)選舉出來,表明當前服務(wù)角色為Leader
FOLLOWING: 跟隨者狀態(tài),Leader已經(jīng)選舉出來,表明當前服務(wù)角色為Follower
OBSERVER: 觀察者狀態(tài),表明當前服務(wù)角色Observer
事務(wù)ID:用ZXID表示,為一個64位數(shù)字,由Leader統(tǒng)一分配,全局唯一,不斷遞增
選舉(全新啟動)

每個Server發(fā)出一個投票,內(nèi)容為(myid,ZXID)(兩臺機器啟動)
接收每個Server的投票
處理投票(與自己的廣播消息進行對比,事務(wù)ID、myid等)
統(tǒng)計投票
改變服務(wù)器狀態(tài)
選舉(運行期間,Leader掛)
與全新啟動狀態(tài)類似
數(shù)據(jù)模型Znode
Zookeeper特有的數(shù)據(jù)節(jié)點,視圖結(jié)構(gòu)類似于Linux文件系統(tǒng),沒有目錄和文件的概念
Znode是Zookeeper中數(shù)據(jù)的最小單元
Znode上可以保存數(shù)據(jù)通過掛載子節(jié)點構(gòu)成一個樹狀的層次化命名空間
Znode樹的根由“/”開始

Znode的生命周期取決于其節(jié)點類型
節(jié)點類型
持久節(jié)點
臨時節(jié)點
順序節(jié)點
組合節(jié)點
持久節(jié)點
持久順序節(jié)點
臨時節(jié)點(不允許在臨時節(jié)點中創(chuàng)建子節(jié)點,臨時節(jié)點的生命周期和客戶端會話綁定,如果客戶端與會話失效,節(jié)點會被Zookeeper自動刪除,會話由客戶端與zookeeper通過心跳保持周期性聯(lián)系)
臨時順序節(jié)點
Znode版本
版本類型
dataVersion:當前數(shù)據(jù)節(jié)點的版本號
cVersion:當前數(shù)據(jù)節(jié)點子節(jié)點的版本號
aVersion:當前數(shù)據(jù)節(jié)點ACL權(quán)限變更版本號
如何保證分布式數(shù)據(jù)原子性操作
悲觀鎖 事務(wù)執(zhí)行的整個過程中對數(shù)據(jù)進行加鎖
樂觀鎖 事務(wù)進行更新/提交之前對數(shù)據(jù)進行檢查,如果數(shù)據(jù)有其他數(shù)據(jù)進行修改,則回滾,如果沒有,則提交(適合數(shù)據(jù)并發(fā)量不大的情況)
zookeeper中使用version實現(xiàn)樂觀鎖的寫入校驗:
// 獲取當前請求的Version
version = setDataRequest.getVersion();
// 獲取當前服務(wù)器上該數(shù)據(jù)的最新Versionint
currentVersion = nodeRecord.stat.getVersion();
if(version != -1 && version != currentVersion) {? ?
? ? throw new KeeperException.BadVersionException(path);
}
version = currentVersion + 1;
Znode狀態(tài)

Znode監(jiān)聽機制

客戶端在集群中注冊監(jiān)聽事件,集群中的WatchManager存儲此監(jiān)聽,如果監(jiān)聽事件發(fā)生,則執(zhí)行相應(yīng)的回調(diào)函數(shù)
Zookeeper的應(yīng)用場景
統(tǒng)一命名服務(wù)
對應(yīng)用/服務(wù)進行統(tǒng)一命名,便于識別不同服務(wù)
按照層次結(jié)構(gòu)組織服務(wù)/應(yīng)用名稱? ? 可將服務(wù)名稱及地址信息寫到Zookeeper上,客戶端通過Zookeeper獲取可用服務(wù)列表
全局唯一ID? 依靠Zookeeper的順序節(jié)點可以輕松生成全局唯一ID
配置管理
配置信息寫入Zookeeper的一個Znode上;
各節(jié)點監(jiān)聽Znode
Znode數(shù)據(jù)變化,通知各節(jié)點更新
集群管理
實時掌握每個節(jié)點的狀態(tài)? ? 將節(jié)點信息寫入到一個Znode上,監(jiān)聽這個Znode可獲取它的實時狀態(tài)變化
分布式鎖
Zookeeper強一致? ? 多個客戶端在Zookeeper上創(chuàng)建相同的Znode,只有一個創(chuàng)建成功
鎖的獨占性? ? 多個客戶端同時在Zookeeper上創(chuàng)建相同的Znode,創(chuàng)建成功的那個客戶端得到鎖,其他客戶端等待
鎖的時序? ? 各個客戶端在某個Znode下創(chuàng)建臨時znode(類型為CreateMode.EPHEMERAL_SEQUENTIAL),這樣該Znode可掌握全局訪問時序
分布式隊列
先入先出隊列
同步隊列? ? 當一個隊列的成員都聚齊時,這個隊列才可用,否則一直等待所有成員到達,比如一個聚合計算開始之前需要并行計算的很多子任務(wù)全部完成才能進行,可以為這個聚合計算創(chuàng)建一個Znode,同時監(jiān)聽這個Znode,其他并行子任務(wù)在這個Znode下創(chuàng)建臨時節(jié)點,每次Znode都會發(fā)出通知,接收到通知統(tǒng)計子節(jié)點個數(shù),直到達到所有的子任務(wù)數(shù)
Zookeeper的介紹就先這些,如有疏漏,敬請指正