天天看點

分布式專題(六):Zookeeper典型應用場景及實踐一.ZooKeeper典型應用場景實踐1 Zookeeper資料模型2 如何使用Zookeeper3 ZooKeeper 典型的應用場景

一.ZooKeeper典型應用場景實踐

ZooKeeper是一個

高可用的分布式資料管理與系統協調架構

基于對Paxos算法的實作,使該架構保證了分布式環境中資料的強一緻性

,也正是基于這樣的特性,使得ZooKeeper解決很多分布式問題。網上對ZK的應用場景也有不少介紹,本文将介紹比較常用的項目例子,系統地對ZK的應用場景進行一個分門歸類的介紹。

值得注意的是,

ZK并非天生就是為這些應用場景設計的,都是後來衆多開發者根據其架構的特性

,利用其提供的一系列API接口(或者稱為原語集),

摸索出來的典型使用方法

。是以,也非常歡迎讀者分享你在ZK使用上的奇技淫巧。

1 Zookeeper資料模型

Zookeeper 會維護

一個具有層次關系的資料結構

,它非常類似于一個标準的檔案系統,如圖所示:

分布式專題(六):Zookeeper典型應用場景及實踐一.ZooKeeper典型應用場景實踐1 Zookeeper資料模型2 如何使用Zookeeper3 ZooKeeper 典型的應用場景

圖中的每個節點稱為一個znode. 每個znode由3部分組成:

  1. stat. 此為狀态資訊, 描述該

    znode的版本, 權限等資訊

  2. data. 與該znode

    關聯的資料

  3. children. 該znode下的

    子節點

Zookeeper 這種資料結構有如下這些特點:

  1. 每個子目錄項如 NameService 都被稱作為 znode,這個 znode 是被它所在的路徑唯一辨別,如 Server1 這個 znode 的辨別為 /NameService/Server1;
  2. znode 可以有子節點目錄,并且每個 znode 可以存儲資料,注意 

    EPHEMERAL 類型的目錄節點不能有子節點目錄

  3. znode 是有版本的,每個 znode 中存儲的資料可以有多個版本,

    也就是一個通路路徑中可以存儲多份資料

  4. znode 可以是臨時節點,

    一旦建立這個 znode 的用戶端與伺服器失去聯系,這個 znode 也将自動删除

    ,Zookeeper 的用戶端和伺服器通信

    采用長連接配接方式,每個用戶端和伺服器通過心跳來保持連接配接

    ,這個連接配接狀态稱為 session,如果 znode 是臨時節點,這個 session 失效,znode 也就删除了;
  5. znode 的

    目錄名可以自動編号

    ,如 App1 已經存在,再建立的話,将會自動命名為 App2;
  6. 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
​      

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 擷取新的配置資訊應用到系統中

分布式專題(六):Zookeeper典型應用場景及實踐一.ZooKeeper典型應用場景實踐1 Zookeeper資料模型2 如何使用Zookeeper3 ZooKeeper 典型的應用場景

釋出與訂閱模型,即所謂的配置中心,顧名思義就是釋出者将資料釋出到ZK節點上,供訂閱者動态擷取資料,

實作配置資訊的集中式管理和動态更新

。例如全局的配置資訊,服務式服務架構的服務位址清單等就非常适合使用。

配置管理執行個體:

  1. 應用中用到的一些配置資訊放到ZK上進行集中管理

    。這類場景通常是這樣:應用在啟動的時候會主動來擷取一次配置

    ,同時,

    在節點上注冊一個Watcher,這樣一來,以後每次配置有更新的時候,都會實時通知到訂閱的用戶端,進而達到擷取最新配置資訊的目的。
  2. 分布式搜尋服務中

    ,索引的元資訊和伺服器叢集機器的節點狀态存放在ZK的一些指定節點,供各個用戶端訂閱使用。
  3. 分布式日志收集系統

    。這個系統的核心工作是收集分布在不同機器的日志。收集器通常是按照應用來配置設定收集任務單元,是以需要在ZK上建立一個以應用名作為path的節點P,并将這個應用的所有機器ip,以子節點的形式注冊到節點P上,這樣一來就能夠實作機器變動的時候,能夠實時通知到收集器調整任務配置設定。
  4. 系統中有些資訊需要動态擷取,并且還會存在人工手動去修改這個資訊的發問

    。通常是暴露出接口,例如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 容易出現單點故障的問題。

分布式專題(六):Zookeeper典型應用場景及實踐一.ZooKeeper典型應用場景實踐1 Zookeeper資料模型2 如何使用Zookeeper3 ZooKeeper 典型的應用場景

1. 叢集機器監控

這通常用于那種

對叢集中機器狀态,機器線上率有較高要求的場景

,能夠快速對叢集中機器變化作出響應。這樣的場景中,往往有一個監控系統,實時檢測叢集機器是否存活。過去的做法通常是:監控系統通過某種手段(比如ping)定時檢測每個機器,或者每個機器自己定時向監控系統彙報“我還活着”。 這種做法可行,但是存在兩個比較明顯的問題:

  1. 叢集中機器有變動的時候,牽連修改的東西比較多。
  2. 有一定的延時。

利用ZooKeeper有兩個特性,就可以實作另一種叢集機器存活性監控系統:

  1. 用戶端在節點 x 上注冊一個Watcher,那麼如果 x 的子節點變化了,會通知該用戶端

  2. 建立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的單點問題。

分布式專題(六):Zookeeper典型應用場景及實踐一.ZooKeeper典型應用場景實踐1 Zookeeper資料模型2 如何使用Zookeeper3 ZooKeeper 典型的應用場景

3.4 共享鎖(Locks)

共享鎖在同一個程序中很容易實作,但是在跨程序或者在不同 Server 之間就不好實作了。Zookeeper 卻很容易實作這個功能,實作方式也是

需要獲得鎖的 Server 建立一個 EPHEMERAL_SEQUENTIAL 目錄節點,然後調用 getChildren方法擷取目前的目錄節點清單中最小的目錄節點是不是就是自己建立的目錄節點,如果正是自己建立的,那麼它就獲得了這個鎖,如果不是那麼它就調用 exists(String path, boolean watch) 方法并監控 Zookeeper 上目錄節點清單的變化,一直到自己建立的節點是清單中最小編号的目錄節點,進而獲得鎖,釋放鎖很簡單,隻要删除前面它自己所建立的目錄節點就行了

分布式鎖,這個主要得益于ZooKeeper為我們保證了資料的強一緻性。鎖服務可以分為兩類,

一個是保持獨占,另一個是控制時序

  1. 所謂保持獨占,就是所有試圖來擷取這個鎖的用戶端,最終隻有一個可以成功獲得這把鎖。通常的做法是把zk上的一個znode看作是一把鎖,通過create znode的方式來實作。

    所有用戶端都去建立 /distribute_lock 節點,最終成功建立的那個用戶端也即擁有了這把鎖

  2. 控制時序,就是所有視圖來擷取這個鎖的用戶端,最終都是會被安排執行,隻是有個全局時序了。做法和上面基本類似,隻是這裡 /distribute_lock 已經預先存在,用戶端在它下面建立臨時有序節點(這個可以通過節點的屬性控制:CreateMode.EPHEMERAL_SEQUENTIAL來指定)。Zk的父節點(/distribute_lock)維持一份sequence,保證子節點建立的時序性,進而也形成了每個用戶端的全局時序。
分布式專題(六):Zookeeper典型應用場景及實踐一.ZooKeeper典型應用場景實踐1 Zookeeper資料模型2 如何使用Zookeeper3 ZooKeeper 典型的應用場景
分布式專題(六):Zookeeper典型應用場景及實踐一.ZooKeeper典型應用場景實踐1 Zookeeper資料模型2 如何使用Zookeeper3 ZooKeeper 典型的應用場景

3.5 隊列管理

Zookeeper 可以處理兩種類型的隊列:

  1. 當一個隊列的成員都聚齊時,這個隊列才可用,否則一直等待所有成員到達

    ,這種是同步隊列。
  2. 隊列按照 FIFO 方式進行入隊和出隊操作

    ,例如實作生産者和消費者模型。

同步隊列用 Zookeeper 實作的實作思路如下:

建立一個父目錄 /synchronizing,每個成員都監控标志(Set Watch)位目錄 /synchronizing/start 是否存在,然後每個成員都加入這個隊列,加入隊列的方式就是建立 /synchronizing/member_i 的臨時目錄節點,然後每個成員擷取 / synchronizing 目錄的所有目錄節點,也就是 member_i。判斷 i 的值是否已經是成員的個數,如果小于成員個數等待 /synchronizing/start 的出現,

如果已經相等就建立 /synchronizing/start

分布式專題(六):Zookeeper典型應用場景及實踐一.ZooKeeper典型應用場景實踐1 Zookeeper資料模型2 如何使用Zookeeper3 ZooKeeper 典型的應用場景
分布式專題(六):Zookeeper典型應用場景及實踐一.ZooKeeper典型應用場景實踐1 Zookeeper資料模型2 如何使用Zookeeper3 ZooKeeper 典型的應用場景

FIFO 隊列用 Zookeeper 實作思路如下:

實作的思路也非常簡單,就是在特定的目錄下建立 SEQUENTIAL 類型的子目錄 /queue_i,這樣就能保證所有成員加入隊列時都是有編号的`,出隊列時通過 getChildren( ) 方法可以傳回目前所有的隊列中的元素,然後消費其中最小的一個,這樣就能保證 FIFO。

分布式專題(六):Zookeeper典型應用場景及實踐一.ZooKeeper典型應用場景實踐1 Zookeeper資料模型2 如何使用Zookeeper3 ZooKeeper 典型的應用場景

3.6 負載均衡

這裡說的負載均衡是指軟負載均衡。在分布式環境中,為了保證高可用性,通常同一個應用或同一個服務的提供方都會部署多份,達到對等服務。而消費者就須要在這些對等的伺服器中選擇一個來執行相關的業務邏輯,

其中比較典型的是消息中間件中的生産者,消費者負載均衡

消息中間件中釋出者和訂閱者的負載均衡

,linkedin開源的KafkaMQ和阿裡開源的metaq都是通過zookeeper來做到生産者、消費者的負載均衡`。這裡以metaq為例如講下:

生産者負載均衡

:metaq發送消息的時候,生産者在發送消息的時候必須選擇一台broker上的一個分區來發送消息,是以metaq在運作過程中,會把所有broker和對應的分區資訊全部注冊到ZK指定節點上,預設的政策是一個依次輪詢的過程,生産者在通過ZK擷取分區清單之後,會按照brokerId和partition的順序排列組織成一個有序的分區清單,發送的時候按照從頭到尾循環往複的方式選擇一個分區來發送消息。

消費負載均衡

: 在消費過程中,一個消費者會消費一個或多個分區中的消息,但是一個分區隻會由一個消費者來消費。MetaQ的消費政策是:

  1. 每個分區針對同一個group隻挂載一個消費者。
  2. 如果同一個group的消費者數目大于分區數目,則多出來的消費者将不參與消費。
  3. 如果同一個group的消費者數目小于分區數目,則有部分消費者需要額外承擔消費任務。

    在某個消費者故障或者重新開機等情況下,其他消費者會感覺到這一變化(通過 zookeeper watch消費者清單),然後重新進行負載均衡,保證所有的分區都有消費者進行消費。

3.7 分布式通知/協調

ZooKeeper中特有watcher注冊與異步通知機制,能夠很好的實作分布式環境下不同系統之間的通知與協調,實作對資料變更的實時處理

。使用方法通常是不同系統都對ZK上同一個znode進行注冊,監聽znode的變化(包括znode本身内容及子節點的),其中一個系統update了znode,那麼另一個系統能夠收到通知,并作出相應處理。

  1. 另一種心跳檢測機制

    :檢測系統和被檢測系統之間并不直接關聯起來,而是通過zk上某個節點關聯,大大減少系統耦合。
  2. 另一種系統排程模式

    :某系統有控制台和推送系統兩部分組成,控制台的職責是控制推送系統進行相應的推送工作。管理人員在控制台作的一些操作,實際上是修改了ZK上某些節點的狀态,而ZK就把這些變化通知給他們注冊Watcher的用戶端,即推送系統,于是,作出相應的推送任務。
  3. 另一種工作彙報模式

    :一些類似于任務分發系統,子任務啟動後,到zk來注冊一個臨時節點,并且定時将自己的進度進行彙報(将進度寫回這個臨時節點),這樣任務管理者就能夠實時知道任務進度。

總之,使用zookeeper來進行分布式通知和協調能夠大大降低系統之間的耦合。

二:典型場景描述總結

資料釋出與訂閱(配置管理)

釋出與訂閱即所謂的配置管理,顧名思義就是将資料釋出到zk節點上,供訂閱者動态擷取資料,實作配置資訊的集中式管理和動态更新。

例如全局的配置資訊,位址清單等就非常适合使用。

釋出/訂閱系統一般有兩種設計模式,分别是推(Push)模式和拉(Pull)模式。

推模式

服務端主動将資料更新發送給所有訂閱的用戶端。

拉模式

用戶端通過采用定時輪詢拉取。

ZooKeeper采用的是推拉相結合的方式:用戶端向服務端注冊自己需要關注的節點,一旦該節點的資料發生變更,那麼服務端就會向相應的用戶端發送Watcher事件通知,用戶端接收到這個消息通知之後,需要主動到服務端擷取最新的資料。

負載均衡

負載均衡(Load Balance)是一種相當常見的計算機網絡技術,用來對多個計算機(計算機叢集)、網絡連接配接、CPU、硬碟驅動器或其他資源進行配置設定負載,以達到優化資源使用、最大化吞吐率、最小化響應時間和避免過載的目的。通常,負載均衡可以分為硬體和軟體負載均衡兩類

分布通知/協調

分布式協調/通知是将不同的分布式元件有機結合起來的關鍵所在。對于一個在多台機器上部署運作的應用而言,通常需要一個協調者(Coordinator)來控制整個系統的運作流程,例如分布式事務的處理、機器間的互相協調等。同時,引入這樣一個協調者,便于将分布式協調的職責從應用中分離出來,進而大大減少系統之間的耦合性,而且能夠顯著提高系統的可擴充性。

ZooKeeper 中特有watcher注冊與異步通知機制,能夠很好的實作分布式環境下不同系統之間的通知與協調,實作對資料變更的實時處理。

使用方法通常是不同系統都對 ZK上同一個znode進行注冊,監聽znode的變化(包括znode本身内容及子節點的),其中一個系統update了znode,那麼另一個系統能 夠收到通知,并作出相應處理。

\1. 另一種心跳檢測機制:檢測系統和被檢測系統之間并不直接關聯起來,而是通過zk上某個節點關聯,大大減少系統耦合。

\2. 另一種系統排程模式:某系統有控制台和推送系統兩部分組成,控制台的職責是控制推送系統進行相應的推送工作。管理人員在控制台作的一些操作,實際上是修改 了ZK上某些節點的狀态,而zk就把這些變化通知給他們注冊Watcher的用戶端,即推送系統,于是,作出相應的推送任務。

\3. 另一種工作彙報模式:一些類似于任務分發系統,子任務啟動後,到zk來注冊一個臨時節點,并且定時将自己的進度進行彙報(将進度寫回這個臨時節點),這樣任務管理者就能夠實時知道任務進度。總之,使用zookeeper來進行分布式通知和協調能夠大大降低系統之間的耦合。

命名服務

在分布式系統中,被命名的實體通常是叢集中的機器、提供的服務位址或遠端對象等--這些我們都可以統稱他們為名字,其中比較常見的就是一些分布式服務架構(RPC、RMI)中的服務位址清單,通過使用命名服務,用戶端應用能夠根據指定名字來擷取資源的實體、服務位址和提供者資訊等。

ZooKeeper提供的命名服務功能與JNDI技術有類似的地方,都能夠幫助應用系統通過一個資源引用的方式來實作對資源的定位與使用。另外,廣義上命名服務的資源定位都不是真正意義的實體資源--在分布式環境中,上層應用僅僅需要一個全局唯一的名字,類似于資料庫的唯一主鍵。,通過調用zk的create node api,能夠很容易建立一個全局唯一的path,這個path就可以作為一個名稱。所謂ID,就是一個能唯一辨別某個對象的辨別符。

分布式鎖

分布式鎖,這個主要得益于ZooKeeper為我們保證了資料的強一緻性,即使用者隻要完全相信每時每刻,zk叢集中任意節點(一個zk server)上的相同znode的資料是一定是相同的。鎖服務可以分為兩類,一個是保持獨占,另一個是控制時序。

保持獨占:就是所有試圖來擷取這個鎖的用戶端,最終隻有一個可以成功獲得這把鎖。通常的做法是把zk上的一個znode看作是一把鎖,通過create znode的方式來實作。所有用戶端都去建立 /distribute_lock 節點,最終成功建立的那個用戶端也即擁有了這把鎖。

控制時序:就是所有視圖來擷取這個鎖的用戶端,最終都是會被安排執行,隻是有個全局時序了。

做法和上面基本類似,隻是這裡 /distribute_lock 已經預先存在,用戶端在它下面建立臨時有序節點

(這個可以通過節點的屬性控制:CreateMode.EPHEMERAL_SEQUENTIAL來指定)。Zk的父節點(/distribute_lock)維持一份sequence,保證子節點建立的時序性,進而也形成了每個用戶端的全局時序。

原理:(借助Zookeeper 可以實作這種分布式鎖:需要獲得鎖的 Server 建立一個 EPHEMERAL_SEQUENTIAL 目錄節點,然後調用 getChildren()方法擷取清單中最小的目錄節點,如果最小節點就是自己建立的目錄節點,那麼它就獲得了這個鎖,如果不是那麼它就調用 exists() 方法并監控前一節點的變化,一直到自己建立的節點成為清單中最小編号的目錄節點,進而獲得鎖。釋放鎖很簡單,隻要删除它自己所建立的目錄節點就行了。)

叢集管理

\1. 叢集機器監控:這通常用于那種對叢集中機器狀态,機器線上率有較高要求的場景,能夠快速對叢集中機器變化作出響應。這樣的場景中,往往有一個監控系統,實時檢測叢集機器是否存活。過去的做法通常是:監控系統通過某種手段(比如ping)定時檢測每個機器,或者每個機器自己定時向監控系統彙報“我還活着”。 這種做法可行,但是存在兩個比較明顯的問題:

\1. 叢集中機器有變動的時候,牽連修改的東西比較多。

\2. 有一定的延時。利用ZooKeeper有兩個特性,就可以實時另一種叢集機器存活性監控系統:

a. 用戶端在節點 x 上注冊一個Watcher,那麼如果 x 的子節點變化了,會通知該用戶端。

b. 建立EPHEMERAL類型的節點,一旦用戶端和伺服器的會話結束或過期,那麼該節點就會消失。

應用:

Master選舉

則是zookeeper中最為經典的使用場景了。

在分布式環境中,相同的業務應用分布在不同的機器上,有些業務邏輯(例如一些耗時的計算,網絡I/O處理),往往隻需要讓整個叢集中的某一台機器進行執行, 其餘機器可以共享這個結果,這樣可以大大減少重複勞動,提高性能,于是這個master選舉便是這種場景下的碰到的主要問題。

利用ZooKeeper的強一緻性,能夠保證在分布式高并發情況下節點建立的全局唯一性,即:同時有多個用戶端請求建立 /currentMaster 節點,最終一定隻有一個用戶端請求能夠建立成功。

分布式隊列

兩種類型的隊列:

1、 同步隊列,當一個隊列的成員都聚齊時,這個隊列才可用,否則一直等待所有成員到達。

2、隊列按照 FIFO 方式進行入隊和出隊操作。

第一類,在約定目錄下建立臨時目錄節點,監聽節點數目是否是我們要求的數目。

第二類,和分布式鎖服務中的控制時序場景基本原理一緻,入列有編号,出列按編号。

同步隊列。一個job由多個task組成,隻有所有任務完成後,job才運作完成。可為job建立一個/job目錄,然後在該目錄下,為每個完成的task建立一個臨時znode,一旦臨時節點數目達到task總數,則job運作完成。

繼續閱讀