#0 系列目錄#node
Zookeeper系列算法
【Zookeeper系列六】Zookeeper 工做原理session
Zookeeper源碼數據結構
Zookeeper應用
ZooKeeper是一個高可用的分佈式數據管理與系統協調框架
。基於對Paxos算法的實現,使該框架保證了分佈式環境中數據的強一致性
,也正是基於這樣的特性,使得ZooKeeper解決不少分佈式問題。網上對ZK的應用場景也有很多介紹,本文將介紹比較經常使用的項目例子,系統地對ZK的應用場景進行一個分門歸類的介紹。
值得注意的是,ZK並不是天生就是爲這些應用場景設計的,都是後來衆多開發者根據其框架的特性
,利用其提供的一系列API接口(或者稱爲原語集),摸索出來的典型使用方法
。所以,也很是歡迎讀者分享你在ZK使用上的奇技淫巧。
#1 Zookeeper數據模型# Zookeeper 會維護一個具備層次關係的數據結構
,它很是相似於一個標準的文件系統,如圖所示:
圖中的每一個節點稱爲一個znode. 每一個znode由3部分組成:
znode的版本, 權限等信息
;關聯的數據
;子節點
;Zookeeper 這種數據結構有以下這些特色:
每一個子目錄項如 NameService 都被稱做爲 znode,這個 znode 是被它所在的路徑惟一標識,如 Server1 這個 znode 的標識爲 /NameService/Server1;
znode 能夠有子節點目錄,而且每一個 znode 能夠存儲數據,注意 EPHEMERAL 類型的目錄節點不能有子節點目錄
;
znode 是有版本的,每一個 znode 中存儲的數據能夠有多個版本,也就是一個訪問路徑中能夠存儲多份數據
;
znode 能夠是臨時節點,一旦建立這個 znode 的客戶端與服務器失去聯繫,這個 znode 也將自動刪除
,Zookeeper 的客戶端和服務器通訊採用長鏈接方式,每一個客戶端和服務器經過心跳來保持鏈接
,這個鏈接狀態稱爲 session,若是 znode 是臨時節點,這個 session 失效,znode 也就刪除了;
znode 的目錄名能夠自動編號
,如 App1 已經存在,再建立的話,將會自動命名爲 App2;
znode 能夠被監控,包括這個目錄節點中存儲的數據的修改,子節點目錄的變化等
,一旦變化能夠通知設置監控的客戶端,這個是 Zookeeper 的核心特性
,Zookeeper 的不少功能都是基於這個特性實現的,後面在典型的應用場景中會有實例介紹;
znode節點的狀態信息:
使用get命令獲取指定節點的數據時, 同時也將返回該節點的狀態信息, 稱爲Stat
. 其包含以下字段:
czxid. 節點建立時的zxid; mzxid. 節點最新一次更新發生時的zxid; ctime. 節點建立時的時間戳; mtime. 節點最新一次更新發生時的時間戳; dataVersion. 節點數據的更新次數; cversion. 其子節點的更新次數; aclVersion. 節點ACL(受權信息)的更新次數; ephemeralOwner. 若是該節點爲ephemeral節點, ephemeralOwner值表示與該節點綁定的session id. 若是該節點不是ephemeral節點, ephemeralOwner值爲0. 至於什麼是ephemeral節點; dataLength. 節點數據的字節數; numChildren. 子節點個數;
zxid:
znode節點的狀態信息中包含czxid和mzxid, 那麼什麼是zxid呢?
ZooKeeper狀態的每一次改變, 都對應着一個遞增的Transaction id, 該id稱爲zxid
. 因爲zxid的遞增性質, 若是zxid1小於zxid2, 那麼zxid1確定先於zxid2發生. 建立任意節點, 或者更新任意節點的數據, 或者刪除任意節點, 都會致使Zookeeper狀態發生改變, 從而致使zxid的值增長
.
session:
在client和server通訊以前, 首先須要創建鏈接, 該鏈接稱爲session. 鏈接創建後, 若是發生鏈接超時, 受權失敗, 或者顯式關閉鏈接, 鏈接便處於CLOSED狀態, 此時session結束.
節點類型:
講述節點狀態的ephemeralOwner字段時, 提到過有的節點是ephemeral節點, 而有的並非. 那麼節點都具備哪些類型呢? 每種類型的節點又具備哪些特色呢?
persistent. persistent節點不和特定的session綁定
, 不會隨着建立該節點的session的結束而消失, 而是一直存在, 除非該節點被顯式刪除.
ephemeral. ephemeral節點是臨時性的, 若是建立該節點的session結束了, 該節點就會被自動刪除
. ephemeral節點不能擁有子節點
. 雖然ephemeral節點與建立它的session綁定, 但只要該該節點沒有被刪除, 其餘session就能夠讀寫該節點中關聯的數據. 使用-e參數指定建立ephemeral節點
.
create -e /xing/ei world
sequence. 嚴格的說, sequence並不是節點類型中的一種
. sequence節點既能夠是ephemeral的, 也能夠是persistent的. 建立sequence節點時, ZooKeeper server會在指定的節點名稱後加上一個數字序列, 該數字序列是遞增的
. 所以能夠屢次建立相同的sequence節點, 而獲得不一樣的節點. 使用-s參數指定建立sequence節點
.
[zk: localhost:4180(CONNECTED) 0] create -s /xing/item world Created /xing/item0000000001 [zk: localhost:4180(CONNECTED) 1] create -s /xing/item world Created /xing/item0000000002 [zk: localhost:4180(CONNECTED) 2] create -s /xing/item world Created /xing/item0000000003 [zk: localhost:4180(CONNECTED) 3] create -s /xing/item world Created /xing/item0000000004
watch:
watch的意思是監聽感興趣的事件
. 在命令行中, 如下幾個命令能夠指定是否監聽相應的事件.
ls命令. ls命令的第一個參數指定znode, 第二個參數若是爲true, 則說明監聽該znode的子節點的增減, 以及該znode自己的刪除事件.
[zk: localhost:4180(CONNECTED) 21] ls /xing true [] [zk: localhost:4180(CONNECTED) 22] create /xing/item item000 WATCHER:: WatchedEvent state:SyncConnected type:NodeChildrenChanged path:/xing Created /xing/item
get命令. get命令的第一個參數指定znode, 第二個參數若是爲true, 則說明監聽該znode的更新和刪除事件
.
[zk: localhost:4180(CONNECTED) 39] get /xing true world cZxid = 0x100000066 ctime = Fri May 17 22:30:01 CST 2013 mZxid = 0x100000066 mtime = Fri May 17 22:30:01 CST 2013 pZxid = 0x100000066 cversion = 0 dataVersion = 0 aclVersion = 0 ephemeralOwner = 0x0 dataLength = 5 numChildren = 0 [zk: localhost:4180(CONNECTED) 40] create /xing/item item000 Created /xing/item [zk: localhost:4180(CONNECTED) 41] rmr /xing WATCHER:: WatchedEvent state:SyncConnected type:NodeDeleted path:/xing
stat命令. stat命令用於獲取znode的狀態信息. 第一個參數指定znode, 若是第二個參數爲true, 則監聽該node的更新和刪除事件.
#2 如何使用Zookeeper# Zookeeper 做爲一個分佈式的服務框架,主要用來解決分佈式集羣中應用系統的一致性問題
,它能提供基於相似於文件系統的目錄節點樹方式的數據存儲,可是 Zookeeper 並非用來專門存儲數據的,它的做用主要是用來維護和監控你存儲的數據的狀態變化
。經過監控這些數據狀態的變化,從而能夠達到基於數據的集羣管理
,後面將會詳細介紹 Zookeeper 可以解決的一些典型問題,這裏先介紹一下,Zookeeper 的操做接口和簡單使用示例。
##2.1 經常使用接口操做## 客戶端要鏈接 Zookeeper 服務器能夠經過建立 org.apache.zookeeper.ZooKeeper
的一個實例對象,而後調用這個類提供的接口來和服務器交互。
前面說了 ZooKeeper 主要是用來維護和監控一個目錄節點樹中存儲的數據的狀態
,全部咱們可以操做 ZooKeeper 的也和操做目錄節點樹大致同樣,如建立一個目錄節點,給某個目錄節點設置數據,獲取某個目錄節點的全部子目錄節點,給某個目錄節點設置權限和監控這個目錄節點的狀態變化。
ZooKeeper 基本的操做示例:
public class ZkDemo { public static void main(String[] args) throws IOException, KeeperException, InterruptedException { // 建立一個與服務器的鏈接 ZooKeeper zk = new ZooKeeper("127.0.0.1:2180", 60000, new Watcher() { // 監控全部被觸發的事件 // 當對目錄節點監控狀態打開時,一旦目錄節點的狀態發生變化,Watcher 對象的 process 方法就會被調用。 public void process(WatchedEvent event) { System.out.println("EVENT:" + event.getType()); } }); // 查看根節點 // 獲取指定 path 下的全部子目錄節點,一樣 getChildren方法也有一個重載方法能夠設置特定的 watcher 監控子節點的狀態 System.out.println("ls / => " + zk.getChildren("/", true)); // 判斷某個 path 是否存在,並設置是否監控這個目錄節點,這裏的 watcher 是在建立 ZooKeeper 實例時指定的 watcher; // exists方法還有一個重載方法,能夠指定特定的 watcher if (zk.exists("/node", true) == null) { // 建立一個給定的目錄節點 path, 並給它設置數據; // CreateMode 標識有四種形式的目錄節點,分別是: // PERSISTENT:持久化目錄節點,這個目錄節點存儲的數據不會丟失; // PERSISTENT_SEQUENTIAL:順序自動編號的目錄節點,這種目錄節點會根據當前已近存在的節點數自動加 1,而後返回給客戶端已經成功建立的目錄節點名; // EPHEMERAL:臨時目錄節點,一旦建立這個節點的客戶端與服務器端口也就是 session 超時,這種節點會被自動刪除; // EPHEMERAL_SEQUENTIAL:臨時自動編號節點 zk.create("/node", "conan".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); System.out.println("create /node conan"); // 查看/node節點數據 System.out.println("get /node => " + new String(zk.getData("/node", false, null))); // 查看根節點 System.out.println("ls / => " + zk.getChildren("/", true)); } // 建立一個子目錄節點 if (zk.exists("/node/sub1", true) == null) { zk.create("/node/sub1", "sub1".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); System.out.println("create /node/sub1 sub1"); // 查看node節點 System.out.println("ls /node => " + zk.getChildren("/node", true)); } // 修改節點數據 if (zk.exists("/node", true) != null) { // 給 path 設置數據,能夠指定這個數據的版本號,若是 version 爲 -1 怎能夠匹配任何版本 zk.setData("/node", "changed".getBytes(), -1); // 查看/node節點數據 // 獲取這個 path 對應的目錄節點存儲的數據,數據的版本等信息能夠經過 stat 來指定,同時還能夠設置是否監控這個目錄節點數據的狀態 System.out.println("get /node => " + new String(zk.getData("/node", false, null))); } // 刪除節點 if (zk.exists("/node/sub1", true) != null) { // 刪除 path 對應的目錄節點,version 爲 -1 能夠匹配任何版本,也就刪除了這個目錄節點全部數據 zk.delete("/node/sub1", -1); zk.delete("/node", -1); // 查看根節點 System.out.println("ls / => " + zk.getChildren("/", true)); } // 關閉鏈接 zk.close(); } }
#3 ZooKeeper 典型的應用場景# Zookeeper 從設計模式角度來看,是一個基於觀察者模式設計的分佈式服務管理框架,它負責存儲和管理你們都關心的數據,而後接受觀察者的註冊,一旦這些數據的狀態發生變化,Zookeeper 就將負責通知已經在 Zookeeper 上註冊的那些觀察者作出相應的反應,從而實現集羣中相似 Master/Slave 管理模式
,關於 Zookeeper 的詳細架構等內部細節能夠閱讀 Zookeeper 的源碼。
下面詳細介紹這些典型的應用場景,也就是 Zookeeper 到底能幫咱們解決那些問題?下面將給出答案。
##3.1 統一命名服務(Name Service)## 分佈式應用中,一般須要有一套完整的命名規則,既可以產生惟一的名稱又便於人識別和記住,一般狀況下用樹形的名稱結構是一個理想的選擇,樹形的名稱結構是一個有層次的目錄結構,既對人友好又不會重複
。說到這裏你可能想到了 JNDI,沒錯 Zookeeper 的 Name Service 與 JNDI 可以完成的功能是差很少的,它們都是將有層次的目錄結構關聯到必定資源上,可是 Zookeeper 的 Name Service 更加是普遍意義上的關聯,也許你並不須要將名稱關聯到特定資源上,你可能只須要一個不會重複名稱,就像數據庫中產生一個惟一的數字主鍵同樣。
Name Service 已是 Zookeeper 內置的功能
,你只要調用 Zookeeper 的 API 就能實現。如調用 create 接口就能夠很容易建立一個目錄節點。
命名服務也是分佈式系統中比較常見的一類場景。在分佈式系統中,經過使用命名服務,客戶端應用可以根據指定名字來獲取資源或服務的地址,提供者等信息
。被命名的實體一般能夠是集羣中的機器,提供的服務地址,遠程對象等等——這些咱們均可以統稱他們爲名字(Name)
。其中較爲常見的就是一些分佈式服務框架中的服務地址列表。經過調用ZK提供的建立節點的API,可以很容易建立一個全局惟一的path,這個path就能夠做爲一個名稱。
命名服務實例:
阿里巴巴集團開源的分佈式服務框架Dubbo中使用ZooKeeper來做爲其命名服務,維護全局的服務地址列表,在Dubbo實現中:
服務提供者在啓動的時候
,向ZK上的指定節點/dubbo/${serviceName}/providers
目錄下寫入本身的URL地址,這個操做就完成了服務的發佈。
服務消費者啓動的時候
,訂閱/dubbo/${serviceName}/providers
目錄下的提供者URL地址, 並向/dubbo/${serviceName} /consumers
目錄下寫入本身的URL地址。
注意,全部向ZK上註冊的地址都是臨時節點,這樣就可以保證服務提供者和消費者可以自動感應資源的變化
。 另外,Dubbo還有針對服務粒度的監控,方法是訂閱/dubbo/${serviceName}目錄下全部提供者和消費者的信息
。
##3.2 配置管理(Configuration Management)## 配置的管理在分佈式應用環境中很常見,例如同一個應用系統須要多臺 PC Server 運行,可是它們運行的應用系統的某些配置項是相同的,若是要修改這些相同的配置項,那麼就必須同時修改每臺運行這個應用系統的 PC Server,這樣很是麻煩並且容易出錯。
像這樣的配置信息徹底能夠交給 Zookeeper 來管理,將配置信息保存在 Zookeeper 的某個目錄節點中,而後將全部須要修改的應用機器監控配置信息的狀態,一旦配置信息發生變化,每臺應用機器就會收到 Zookeeper 的通知,而後從 Zookeeper 獲取新的配置信息應用到系統中
。
發佈與訂閱模型,即所謂的配置中心
,顧名思義就是發佈者將數據發佈到ZK節點上,供訂閱者動態獲取數據,實現配置信息的集中式管理和動態更新
。例如全局的配置信息,服務式服務框架的服務地址列表等就很是適合使用。
配置管理實例:
應用中用到的一些配置信息放到ZK上進行集中管理
。這類場景一般是這樣:應用在啓動的時候會主動來獲取一次配置
,同時,在節點上註冊一個Watcher
,這樣一來,之後每次配置有更新的時候,都會實時通知到訂閱的客戶端,歷來達到獲取最新配置信息的目的。
分佈式搜索服務中
,索引的元信息和服務器集羣機器的節點狀態存放在ZK的一些指定節點,供各個客戶端訂閱使用。
分佈式日誌收集系統
。這個系統的核心工做是收集分佈在不一樣機器的日誌。收集器一般是按照應用來分配收集任務單元,所以須要在ZK上建立一個以應用名做爲path的節點P,並將這個應用的全部機器ip,以子節點的形式註冊到節點P上,這樣一來就可以實現機器變更的時候,可以實時通知到收集器調整任務分配。
系統中有些信息須要動態獲取,而且還會存在人工手動去修改這個信息的發問
。一般是暴露出接口,例如JMX接口,來獲取一些運行時的信息。引入ZK以後,就不用本身實現一套方案了,只要將這些信息存放到指定的ZK節點上便可。
注意:在上面提到的應用場景中,有個默認前提是:數據量很小,可是數據更新可能會比較快的場景
。
##3.3 集羣管理(Group Membership)## Zookeeper 可以很容易的實現集羣管理的功能,若有多臺 Server 組成一個服務集羣,那麼必需要一個「總管」知道當前集羣中每臺機器的服務狀態,一旦有機器不能提供服務,集羣中其它集羣必須知道
,從而作出調整從新分配服務策略。一樣當增長集羣的服務能力時,就會增長一臺或多臺 Server,一樣也必須讓「總管」知道
。
Zookeeper 不只可以幫你維護當前的集羣中機器的服務狀態,並且可以幫你選出一個「總管」,讓這個總管來管理集羣,這就是 Zookeeper 的另外一個功能 Leader Election
。
它們的實現方式都是在 Zookeeper 上建立一個 EPHEMERAL 類型的目錄節點
,而後每一個 Server 在它們建立目錄節點的父目錄節點上調用 getChildren(String path, boolean watch) 方法並設置 watch 爲 true
,因爲是 EPHEMERAL 目錄節點,當建立它的 Server 死去,這個目錄節點也隨之被刪除,因此 Children 將會變化,這時 getChildren上的 Watch 將會被調用,因此其它 Server 就知道已經有某臺 Server 死去了。新增 Server 也是一樣的原理。
Zookeeper 如何實現 Leader Election,也就是選出一個 Master Server。和前面的同樣每臺 Server 建立一個 EPHEMERAL 目錄節點,不一樣的是它仍是一個 SEQUENTIAL 目錄節點,因此它是個 EPHEMERAL_SEQUENTIAL 目錄節點
。之因此它是 EPHEMERAL_SEQUENTIAL 目錄節點,是由於咱們能夠給每臺 Server 編號,咱們能夠選擇當前是最小編號的 Server 爲 Master
,假如這個最小編號的 Server 死去,因爲是 EPHEMERAL 節點,死去的 Server 對應的節點也被刪除,因此當前的節點列表中又出現一個最小編號的節點,咱們就選擇這個節點爲當前 Master
。這樣就實現了動態選擇 Master,避免了傳統意義上單 Master 容易出現單點故障的問題。
1. 集羣機器監控
這一般用於那種對集羣中機器狀態,機器在線率有較高要求的場景
,可以快速對集羣中機器變化做出響應。這樣的場景中,每每有一個監控系統,實時檢測集羣機器是否存活。過去的作法一般是:監控系統經過某種手段(好比ping)定時檢測每一個機器,或者每一個機器本身定時向監控系統彙報「我還活着」。 這種作法可行,可是存在兩個比較明顯的問題:
利用ZooKeeper有兩個特性,就能夠實現另外一種集羣機器存活性監控系統:
客戶端在節點 x 上註冊一個Watcher,那麼若是 x 的子節點變化了,會通知該客戶端
。建立EPHEMERAL類型的節點,一旦客戶端和服務器的會話結束或過時,那麼該節點就會消失
。例如
:監控系統在 /clusterServers 節點上註冊一個Watcher,之後每動態加機器,那麼就往 /clusterServers 下建立一個 EPHEMERAL類型的節點:/clusterServers/{hostname}. 這樣,監控系統就可以實時知道機器的增減狀況,至於後續處理就是監控系統的業務了。
2. Master選舉則是zookeeper中最爲經典的應用場景了
在分佈式環境中,相同的業務應用分佈在不一樣的機器上,有些業務邏輯(例如一些耗時的計算,網絡I/O處理),每每只須要讓整個集羣中的某一臺機器進行執行,其他機器能夠共享這個結果
,這樣能夠大大減小重複勞動,提升性能,因而這個master選舉即是這種場景下的碰到的主要問題
。
利用ZooKeeper的強一致性,可以保證在分佈式高併發狀況下節點建立的全局惟一性
,即:同時有多個客戶端請求建立 /currentMaster 節點,最終必定只有一個客戶端請求可以建立成功。利用這個特性,就能很輕易的在分佈式環境中進行集羣選取了。
另外,這種場景演化一下,就是動態Master選舉
。這就要用到EPHEMERAL_SEQUENTIAL類型節點的特性了
。
上文中提到,全部客戶端建立請求,最終只有一個可以建立成功。在這裏稍微變化下,就是**容許全部請求都可以建立成功,可是得有個建立順序
**,因而全部的請求最終在ZK上建立結果的一種可能狀況是這樣: /currentMaster/{sessionId}-1 ,/currentMaster/{sessionId}-2,/currentMaster/{sessionId}-3 ….. 每次選取序列號最小的那個機器做爲Master,若是這個機器掛了,因爲他建立的節點會立刻消失,那麼以後最小的那個機器就是Master了
。
**3. 在搜索系統中,若是集羣中每一個機器都生成一份全量索引,不只耗時,並且不能保證彼此之間索引數據一致。**所以讓集羣中的Master來進行全量索引的生成,而後同步到集羣中其它機器。另外,Master選舉的容災措施是,能夠隨時進行手動指定master,就是說應用在zk在沒法獲取master信息時,能夠經過好比http方式,向一個地方獲取master。
**4. 在Hbase中,也是使用ZooKeeper來實現動態HMaster的選舉。**在Hbase實現中,會在ZK上存儲一些ROOT表的地址和HMaster的地址,HRegionServer也會把本身以臨時節點(Ephemeral)的方式註冊到Zookeeper中,使得HMaster能夠隨時感知到各個HRegionServer的存活狀態,同時,一旦HMaster出現問題,會從新選舉出一個HMaster來運行,從而避免了HMaster的單點問題。
##3.4 共享鎖(Locks)## 共享鎖在同一個進程中很容易實現,可是在跨進程或者在不一樣 Server 之間就很差實現了。Zookeeper 卻很容易實現這個功能,實現方式也是須要得到鎖的 Server 建立一個 EPHEMERAL_SEQUENTIAL 目錄節點,而後調用 getChildren方法獲取當前的目錄節點列表中最小的目錄節點是否是就是本身建立的目錄節點,若是正是本身建立的,那麼它就得到了這個鎖,若是不是那麼它就調用 exists(String path, boolean watch) 方法並監控 Zookeeper 上目錄節點列表的變化,一直到本身建立的節點是列表中最小編號的目錄節點,從而得到鎖,釋放鎖很簡單,只要刪除前面它本身所建立的目錄節點就好了
。
分佈式鎖,這個主要得益於ZooKeeper爲咱們保證了數據的強一致性。鎖服務能夠分爲兩類,一個是保持獨佔,另外一個是控制時序
。
所謂保持獨佔,就是全部試圖來獲取這個鎖的客戶端,最終只有一個能夠成功得到這把鎖
。一般的作法是把zk上的一個znode看做是一把鎖,經過create znode的方式來實現。全部客戶端都去建立 /distribute_lock 節點,最終成功建立的那個客戶端也即擁有了這把鎖
。
控制時序,就是全部視圖來獲取這個鎖的客戶端,最終都是會被安排執行,只是有個全局時序了
。作法和上面基本相似,只是這裏 /distribute_lock 已經預先存在,客戶端在它下面建立臨時有序節點(這個能夠經過節點的屬性控制:CreateMode.EPHEMERAL_SEQUENTIAL來指定)。Zk的父節點(/distribute_lock)維持一份sequence,保證子節點建立的時序性,從而也造成了每一個客戶端的全局時序。
##3.5 隊列管理## Zookeeper 能夠處理兩種類型的隊列:
當一個隊列的成員都聚齊時,這個隊列纔可用,不然一直等待全部成員到達
,這種是同步隊列。
隊列按照 FIFO 方式進行入隊和出隊操做
,例如實現生產者和消費者模型。
同步隊列用 Zookeeper 實現的實現思路以下:
建立一個父目錄 /synchronizing,每一個成員都監控標誌(Set Watch)位目錄 /synchronizing/start 是否存在,而後每一個成員都加入這個隊列,加入隊列的方式就是建立 /synchronizing/member_i 的臨時目錄節點,而後每一個成員獲取 / synchronizing 目錄的全部目錄節點,也就是 member_i。判斷 i 的值是否已是成員的個數,若是小於成員個數等待 /synchronizing/start 的出現,若是已經相等就建立 /synchronizing/start
。
FIFO 隊列用 Zookeeper 實現思路以下:
實現的思路也很是簡單,就是在特定的目錄下建立 SEQUENTIAL 類型的子目錄 /queue_i,這樣就能保證全部成員加入隊列時都是有編號的
,出隊列時經過 getChildren( ) 方法能夠返回當前全部的隊列中的元素,而後消費其中最小的一個,這樣就能保證 FIFO。
##3.6 負載均衡## 這裏說的負載均衡是指軟負載均衡
。在分佈式環境中,爲了保證高可用性,一般同一個應用或同一個服務的提供方都會部署多份,達到對等服務。而消費者就需要在這些對等的服務器中選擇一個來執行相關的業務邏輯,其中比較典型的是消息中間件中的生產者,消費者負載均衡
。
消息中間件中發佈者和訂閱者的負載均衡
,linkedin開源的KafkaMQ和阿里開源的metaq都是經過zookeeper來作到生產者、消費者的負載均衡
。這裏以metaq爲例如講下:
生產者負載均衡
:metaq發送消息的時候,生產者在發送消息的時候必須選擇一臺broker上的一個分區來發送消息,所以metaq在運行過程當中,會把全部broker和對應的分區信息所有註冊到ZK指定節點上,默認的策略是一個依次輪詢的過程,生產者在經過ZK獲取分區列表以後,會按照brokerId和partition的順序排列組織成一個有序的分區列表,發送的時候按照從頭至尾循環往復的方式選擇一個分區來發送消息。
消費負載均衡
: 在消費過程當中,一個消費者會消費一個或多個分區中的消息,可是一個分區只會由一個消費者來消費。MetaQ的消費策略是:
在某個消費者故障或者重啓等狀況下,其餘消費者會感知到這一變化(經過 zookeeper watch消費者列表),而後從新進行負載均衡,保證全部的分區都有消費者進行消費。
##3.7 分佈式通知/協調## ZooKeeper中特有watcher註冊與異步通知機制,可以很好的實現分佈式環境下不一樣系統之間的通知與協調,實現對數據變動的實時處理
。使用方法一般是不一樣系統都對ZK上同一個znode進行註冊,監聽znode的變化(包括znode自己內容及子節點的),其中一個系統update了znode,那麼另外一個系統可以收到通知,並做出相應處理。
另外一種心跳檢測機制
:檢測系統和被檢測系統之間並不直接關聯起來,而是經過zk上某個節點關聯,大大減小系統耦合。另外一種系統調度模式
:某系統有控制檯和推送系統兩部分組成,控制檯的職責是控制推送系統進行相應的推送工做。管理人員在控制檯做的一些操做,其實是修改了ZK上某些節點的狀態,而ZK就把這些變化通知給他們註冊Watcher的客戶端,即推送系統,因而,做出相應的推送任務。另外一種工做彙報模式
:一些相似於任務分發系統,子任務啓動後,到zk來註冊一個臨時節點,而且定時將本身的進度進行彙報(將進度寫回這個臨時節點),這樣任務管理者就可以實時知道任務進度。總之,使用zookeeper來進行分佈式通知和協調可以大大下降系統之間的耦合。