<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    ivaneeo's blog

    自由的力量,自由的生活。

      BlogJava :: 首頁 :: 聯(lián)系 :: 聚合  :: 管理
      669 Posts :: 0 Stories :: 64 Comments :: 0 Trackbacks

    簡介: Zookeeper 分布式服務框架是 Apache Hadoop 的一個子項目,它主要是用來解決分布式應用中經(jīng)常遇到的一些數(shù)據(jù)管理問題,如:統(tǒng)一命名服務、狀態(tài)同步服務、集群管理、分布式應用配置項的管理等。本文將從使用者角度詳細介紹 Zookeeper 的安裝和配置文件中各個配置項的意義,以及分析 Zookeeper 的典型的應用場景(配置文件的管理、集群管理、同步鎖、Leader 選舉、隊列管理等),用 Java 實現(xiàn)它們并給出示例代碼。

    本文的標簽:  zookeeper集群

    發(fā)布日期: 2010 年 11 月 18 日 
    級別: 初級 
    訪問情況 8920 次瀏覽 
    建議: 1 (查看或添加評論)

    1 star2 stars3 stars4 stars5 stars 平均分 (共 16 個評分 )

    安裝和配置詳解

    本文介紹的 Zookeeper 是以 3.2.2 這個穩(wěn)定版本為基礎(chǔ),最新的版本可以通過官網(wǎng) http://hadoop.apache.org/zookeeper/來獲取,Zookeeper 的安裝非常簡單,下面將從單機模式和集群模式兩個方面介紹 Zookeeper 的安裝和配置。

    單機模式

    單機安裝非常簡單,只要獲取到 Zookeeper 的壓縮包并解壓到某個目錄如:/home/zookeeper-3.2.2 下,Zookeeper 的啟動腳本在 bin 目錄下,Linux 下的啟動腳本是 zkServer.sh,在 3.2.2 這個版本 Zookeeper 沒有提供 windows 下的啟動腳本,所以要想在 windows 下啟動 Zookeeper 要自己手工寫一個,如清單 1 所示:


    清單 1. Windows 下 Zookeeper 啟動腳本
                setlocal
                set ZOOCFGDIR=%~dp0%..\conf
                set ZOO_LOG_DIR=%~dp0%..
                set ZOO_LOG4J_PROP=INFO,CONSOLE
                set CLASSPATH=%ZOOCFGDIR%
                set CLASSPATH=%~dp0..\*;%~dp0..\lib\*;%CLASSPATH%
                set CLASSPATH=%~dp0..\build\classes;%~dp0..\build\lib\*;%CLASSPATH%
                set ZOOCFG=%ZOOCFGDIR%\zoo.cfg
                set ZOOMAIN=org.apache.zookeeper.server.ZooKeeperServerMain
                java "-Dzookeeper.log.dir=%ZOO_LOG_DIR%" "-Dzookeeper.root.logger=%ZOO_LOG4J_PROP%"
                -cp "%CLASSPATH%" %ZOOMAIN% "%ZOOCFG%" %*
                endlocal
                

    在你執(zhí)行啟動腳本之前,還有幾個基本的配置項需要配置一下,Zookeeper 的配置文件在 conf 目錄下,這個目錄下有 zoo_sample.cfg 和 log4j.properties,你需要做的就是將 zoo_sample.cfg 改名為 zoo.cfg,因為 Zookeeper 在啟動時會找這個文件作為默認配置文件。下面詳細介紹一下,這個配置文件中各個配置項的意義。

     tickTime=2000
                dataDir=D:/devtools/zookeeper-3.2.2/build
                clientPort=2181
                

    • tickTime:這個時間是作為 Zookeeper 服務器之間或客戶端與服務器之間維持心跳的時間間隔,也就是每個 tickTime 時間就會發(fā)送一個心跳。
    • dataDir:顧名思義就是 Zookeeper 保存數(shù)據(jù)的目錄,默認情況下,Zookeeper 將寫數(shù)據(jù)的日志文件也保存在這個目錄里。
    • clientPort:這個端口就是客戶端連接 Zookeeper 服務器的端口,Zookeeper 會監(jiān)聽這個端口,接受客戶端的訪問請求。

    當這些配置項配置好后,你現(xiàn)在就可以啟動 Zookeeper 了,啟動后要檢查 Zookeeper 是否已經(jīng)在服務,可以通過 netstat – ano 命令查看是否有你配置的 clientPort 端口號在監(jiān)聽服務。

    集群模式

    Zookeeper 不僅可以單機提供服務,同時也支持多機組成集群來提供服務。實際上 Zookeeper 還支持另外一種偽集群的方式,也就是可以在一臺物理機上運行多個 Zookeeper 實例,下面將介紹集群模式的安裝和配置。

    Zookeeper 的集群模式的安裝和配置也不是很復雜,所要做的就是增加幾個配置項。集群模式除了上面的三個配置項還要增加下面幾個配置項:

     initLimit=5
                syncLimit=2
                server.1=192.168.211.1:2888:3888
                server.2=192.168.211.2:2888:3888
                

    • initLimit:這個配置項是用來配置 Zookeeper 接受客戶端(這里所說的客戶端不是用戶連接 Zookeeper 服務器的客戶端,而是 Zookeeper 服務器集群中連接到 Leader 的 Follower 服務器)初始化連接時最長能忍受多少個心跳時間間隔數(shù)。當已經(jīng)超過 10 個心跳的時間(也就是 tickTime)長度后 Zookeeper 服務器還沒有收到客戶端的返回信息,那么表明這個客戶端連接失敗。總的時間長度就是 5*2000=10 秒
    • syncLimit:這個配置項標識 Leader 與 Follower 之間發(fā)送消息,請求和應答時間長度,最長不能超過多少個 tickTime 的時間長度,總的時間長度就是 2*2000=4 秒
    • server.A=B:C:D:其中 A 是一個數(shù)字,表示這個是第幾號服務器;B 是這個服務器的 ip 地址;C 表示的是這個服務器與集群中的 Leader 服務器交換信息的端口;D 表示的是萬一集群中的 Leader 服務器掛了,需要一個端口來重新進行選舉,選出一個新的 Leader,而這個端口就是用來執(zhí)行選舉時服務器相互通信的端口。如果是偽集群的配置方式,由于 B 都是一樣,所以不同的 Zookeeper 實例通信端口號不能一樣,所以要給它們分配不同的端口號。

    除了修改 zoo.cfg 配置文件,集群模式下還要配置一個文件 myid,這個文件在 dataDir 目錄下,這個文件里面就有一個數(shù)據(jù)就是 A 的值,Zookeeper 啟動時會讀取這個文件,拿到里面的數(shù)據(jù)與 zoo.cfg 里面的配置信息比較從而判斷到底是那個 server。

    數(shù)據(jù)模型

    Zookeeper 會維護一個具有層次關(guān)系的數(shù)據(jù)結(jié)構(gòu),它非常類似于一個標準的文件系統(tǒng),如圖 1 所示:


    圖 1 Zookeeper 數(shù)據(jù)結(jié)構(gòu)
    圖 1 Zookeeper 數(shù)據(jù)結(jié)構(gòu) 

    Zookeeper 這種數(shù)據(jù)結(jié)構(gòu)有如下這些特點:

    1. 每個子目錄項如 NameService 都被稱作為 znode,這個 znode 是被它所在的路徑唯一標識,如 Server1 這個 znode 的標識為 /NameService/Server1
    2. znode 可以有子節(jié)點目錄,并且每個 znode 可以存儲數(shù)據(jù),注意 EPHEMERAL 類型的目錄節(jié)點不能有子節(jié)點目錄
    3. znode 是有版本的,每個 znode 中存儲的數(shù)據(jù)可以有多個版本,也就是一個訪問路徑中可以存儲多份數(shù)據(jù)
    4. znode 可以是臨時節(jié)點,一旦創(chuàng)建這個 znode 的客戶端與服務器失去聯(lián)系,這個 znode 也將自動刪除,Zookeeper 的客戶端和服務器通信采用長連接方式,每個客戶端和服務器通過心跳來保持連接,這個連接狀態(tài)稱為 session,如果 znode 是臨時節(jié)點,這個 session 失效,znode 也就刪除了
    5. znode 的目錄名可以自動編號,如 App1 已經(jīng)存在,再創(chuàng)建的話,將會自動命名為 App2
    6. znode 可以被監(jiān)控,包括這個目錄節(jié)點中存儲的數(shù)據(jù)的修改,子節(jié)點目錄的變化等,一旦變化可以通知設置監(jiān)控的客戶端,這個是 Zookeeper 的核心特性,Zookeeper 的很多功能都是基于這個特性實現(xiàn)的,后面在典型的應用場景中會有實例介紹

    如何使用

    Zookeeper 作為一個分布式的服務框架,主要用來解決分布式集群中應用系統(tǒng)的一致性問題,它能提供基于類似于文件系統(tǒng)的目錄節(jié)點樹方式的數(shù)據(jù)存儲,但是 Zookeeper 并不是用來專門存儲數(shù)據(jù)的,它的作用主要是用來維護和監(jiān)控你存儲的數(shù)據(jù)的狀態(tài)變化。通過監(jiān)控這些數(shù)據(jù)狀態(tài)的變化,從而可以達到基于數(shù)據(jù)的集群管理,后面將會詳細介紹 Zookeeper 能夠解決的一些典型問題,這里先介紹一下,Zookeeper 的操作接口和簡單使用示例。

    常用接口列表

    客戶端要連接 Zookeeper 服務器可以通過創(chuàng)建 org.apache.zookeeper. ZooKeeper 的一個實例對象,然后調(diào)用這個類提供的接口來和服務器交互。

    前面說了 ZooKeeper 主要是用來維護和監(jiān)控一個目錄節(jié)點樹中存儲的數(shù)據(jù)的狀態(tài),所有我們能夠操作 ZooKeeper 的也和操作目錄節(jié)點樹大體一樣,如創(chuàng)建一個目錄節(jié)點,給某個目錄節(jié)點設置數(shù)據(jù),獲取某個目錄節(jié)點的所有子目錄節(jié)點,給某個目錄節(jié)點設置權(quán)限和監(jiān)控這個目錄節(jié)點的狀態(tài)變化。

    這些接口如下表所示:


    表 1 org.apache.zookeeper. ZooKeeper 方法列表
    方法名方法功能描述
    String create(String path, byte[] data, List<ACL> acl,CreateMode createMode) 創(chuàng)建一個給定的目錄節(jié)點 path, 并給它設置數(shù)據(jù),CreateMode 標識有四種形式的目錄節(jié)點,分別是 PERSISTENT:持久化目錄節(jié)點,這個目錄節(jié)點存儲的數(shù)據(jù)不會丟失;PERSISTENT_SEQUENTIAL:順序自動編號的目錄節(jié)點,這種目錄節(jié)點會根據(jù)當前已近存在的節(jié)點數(shù)自動加 1,然后返回給客戶端已經(jīng)成功創(chuàng)建的目錄節(jié)點名;EPHEMERAL:臨時目錄節(jié)點,一旦創(chuàng)建這個節(jié)點的客戶端與服務器端口也就是 session 超時,這種節(jié)點會被自動刪除;EPHEMERAL_SEQUENTIAL:臨時自動編號節(jié)點
    Stat exists(String path, boolean watch) 判斷某個 path 是否存在,并設置是否監(jiān)控這個目錄節(jié)點,這里的 watcher 是在創(chuàng)建 ZooKeeper 實例時指定的 watcher,exists方法還有一個重載方法,可以指定特定的 watcher
    Stat exists(String path,Watcher watcher) 重載方法,這里給某個目錄節(jié)點設置特定的 watcher,Watcher 在 ZooKeeper 是一個核心功能,Watcher 可以監(jiān)控目錄節(jié)點的數(shù)據(jù)變化以及子目錄的變化,一旦這些狀態(tài)發(fā)生變化,服務器就會通知所有設置在這個目錄節(jié)點上的 Watcher,從而每個客戶端都很快知道它所關(guān)注的目錄節(jié)點的狀態(tài)發(fā)生變化,而做出相應的反應
    void delete(String path, int version) 刪除 path 對應的目錄節(jié)點,version 為 -1 可以匹配任何版本,也就刪除了這個目錄節(jié)點所有數(shù)據(jù)
    List<String>getChildren(String path, boolean watch) 獲取指定 path 下的所有子目錄節(jié)點,同樣 getChildren方法也有一個重載方法可以設置特定的 watcher 監(jiān)控子節(jié)點的狀態(tài)
    Stat setData(String path, byte[] data, int version) 給 path 設置數(shù)據(jù),可以指定這個數(shù)據(jù)的版本號,如果 version 為 -1 怎可以匹配任何版本
    byte[] getData(String path, boolean watch, Stat stat) 獲取這個 path 對應的目錄節(jié)點存儲的數(shù)據(jù),數(shù)據(jù)的版本等信息可以通過 stat 來指定,同時還可以設置是否監(jiān)控這個目錄節(jié)點數(shù)據(jù)的狀態(tài)
    voidaddAuthInfo(String scheme, byte[] auth) 客戶端將自己的授權(quán)信息提交給服務器,服務器將根據(jù)這個授權(quán)信息驗證客戶端的訪問權(quán)限。
    Stat setACL(String path,List<ACL> acl, int version) 給某個目錄節(jié)點重新設置訪問權(quán)限,需要注意的是 Zookeeper 中的目錄節(jié)點權(quán)限不具有傳遞性,父目錄節(jié)點的權(quán)限不能傳遞給子目錄節(jié)點。目錄節(jié)點 ACL 由兩部分組成:perms 和 id。
    Perms 有 ALL、READ、WRITE、CREATE、DELETE、ADMIN 幾種 
    而 id 標識了訪問目錄節(jié)點的身份列表,默認情況下有以下兩種:
    ANYONE_ID_UNSAFE = new Id("world", "anyone") 和 AUTH_IDS = new Id("auth", "") 分別表示任何人都可以訪問和創(chuàng)建者擁有訪問權(quán)限。
    List<ACL>getACL(String path,Stat stat) 獲取某個目錄節(jié)點的訪問權(quán)限列表

    除了以上這些上表中列出的方法之外還有一些重載方法,如都提供了一個回調(diào)類的重載方法以及可以設置特定 Watcher 的重載方法,具體的方法可以參考 org.apache.zookeeper. ZooKeeper 類的 API 說明。

    基本操作

    下面給出基本的操作 ZooKeeper 的示例代碼,這樣你就能對 ZooKeeper 有直觀的認識了。下面的清單包括了創(chuàng)建與 ZooKeeper 服務器的連接以及最基本的數(shù)據(jù)操作:


    清單 2. ZooKeeper 基本的操作示例
                // 創(chuàng)建一個與服務器的連接
                ZooKeeper zk = new ZooKeeper("localhost:" + CLIENT_PORT,
                ClientBase.CONNECTION_TIMEOUT, new Watcher() {
                // 監(jiān)控所有被觸發(fā)的事件
                public void process(WatchedEvent event) {
                System.out.println("已經(jīng)觸發(fā)了" + event.getType() + "事件!");
                }
                });
                // 創(chuàng)建一個目錄節(jié)點
                zk.create("/testRootPath", "testRootData".getBytes(), Ids.OPEN_ACL_UNSAFE,
                CreateMode.PERSISTENT);
                // 創(chuàng)建一個子目錄節(jié)點
                zk.create("/testRootPath/testChildPathOne", "testChildDataOne".getBytes(),
                Ids.OPEN_ACL_UNSAFE,CreateMode.PERSISTENT);
                System.out.println(new String(zk.getData("/testRootPath",false,null)));
                // 取出子目錄節(jié)點列表
                System.out.println(zk.getChildren("/testRootPath",true));
                // 修改子目錄節(jié)點數(shù)據(jù)
                zk.setData("/testRootPath/testChildPathOne","modifyChildDataOne".getBytes(),-1);
                System.out.println("目錄節(jié)點狀態(tài):["+zk.exists("/testRootPath",true)+"]");
                // 創(chuàng)建另外一個子目錄節(jié)點
                zk.create("/testRootPath/testChildPathTwo", "testChildDataTwo".getBytes(),
                Ids.OPEN_ACL_UNSAFE,CreateMode.PERSISTENT);
                System.out.println(new String(zk.getData("/testRootPath/testChildPathTwo",true,null)));
                // 刪除子目錄節(jié)點
                zk.delete("/testRootPath/testChildPathTwo",-1);
                zk.delete("/testRootPath/testChildPathOne",-1);
                // 刪除父目錄節(jié)點
                zk.delete("/testRootPath",-1);
                // 關(guān)閉連接
                zk.close();
                

    輸出的結(jié)果如下:

    已經(jīng)觸發(fā)了 None 事件!
                testRootData
                [testChildPathOne]
                目錄節(jié)點狀態(tài):[5,5,1281804532336,1281804532336,0,1,0,0,12,1,6]
                已經(jīng)觸發(fā)了 NodeChildrenChanged 事件!
                testChildDataTwo
                已經(jīng)觸發(fā)了 NodeDeleted 事件!
                已經(jīng)觸發(fā)了 NodeDeleted 事件!
                

    當對目錄節(jié)點監(jiān)控狀態(tài)打開時,一旦目錄節(jié)點的狀態(tài)發(fā)生變化,Watcher 對象的 process 方法就會被調(diào)用。

    ZooKeeper 典型的應用場景

    Zookeeper 從設計模式角度來看,是一個基于觀察者模式設計的分布式服務管理框架,它負責存儲和管理大家都關(guān)心的數(shù)據(jù),然后接受觀察者的注冊,一旦這些數(shù)據(jù)的狀態(tài)發(fā)生變化,Zookeeper 就將負責通知已經(jīng)在 Zookeeper 上注冊的那些觀察者做出相應的反應,從而實現(xiàn)集群中類似 Master/Slave 管理模式,關(guān)于 Zookeeper 的詳細架構(gòu)等內(nèi)部細節(jié)可以閱讀 Zookeeper 的源碼

    下面詳細介紹這些典型的應用場景,也就是 Zookeeper 到底能幫我們解決那些問題?下面將給出答案。

    統(tǒng)一命名服務(Name Service)

    分布式應用中,通常需要有一套完整的命名規(guī)則,既能夠產(chǎn)生唯一的名稱又便于人識別和記住,通常情況下用樹形的名稱結(jié)構(gòu)是一個理想的選擇,樹形的名稱結(jié)構(gòu)是一個有層次的目錄結(jié)構(gòu),既對人友好又不會重復。說到這里你可能想到了 JNDI,沒錯 Zookeeper 的 Name Service 與 JNDI 能夠完成的功能是差不多的,它們都是將有層次的目錄結(jié)構(gòu)關(guān)聯(lián)到一定資源上,但是 Zookeeper 的 Name Service 更加是廣泛意義上的關(guān)聯(lián),也許你并不需要將名稱關(guān)聯(lián)到特定資源上,你可能只需要一個不會重復名稱,就像數(shù)據(jù)庫中產(chǎn)生一個唯一的數(shù)字主鍵一樣。

    Name Service 已經(jīng)是 Zookeeper 內(nèi)置的功能,你只要調(diào)用 Zookeeper 的 API 就能實現(xiàn)。如調(diào)用 create 接口就可以很容易創(chuàng)建一個目錄節(jié)點。

    配置管理(Configuration Management)

    配置的管理在分布式應用環(huán)境中很常見,例如同一個應用系統(tǒng)需要多臺 PC Server 運行,但是它們運行的應用系統(tǒng)的某些配置項是相同的,如果要修改這些相同的配置項,那么就必須同時修改每臺運行這個應用系統(tǒng)的 PC Server,這樣非常麻煩而且容易出錯。

    像這樣的配置信息完全可以交給 Zookeeper 來管理,將配置信息保存在 Zookeeper 的某個目錄節(jié)點中,然后將所有需要修改的應用機器監(jiān)控配置信息的狀態(tài),一旦配置信息發(fā)生變化,每臺應用機器就會收到 Zookeeper 的通知,然后從 Zookeeper 獲取新的配置信息應用到系統(tǒng)中。


    圖 2. 配置管理結(jié)構(gòu)圖
    圖 2. 配置管理結(jié)構(gòu)圖 

    集群管理(Group Membership)

    Zookeeper 能夠很容易的實現(xiàn)集群管理的功能,如有多臺 Server 組成一個服務集群,那么必須要一個“總管”知道當前集群中每臺機器的服務狀態(tài),一旦有機器不能提供服務,集群中其它集群必須知道,從而做出調(diào)整重新分配服務策略。同樣當增加集群的服務能力時,就會增加一臺或多臺 Server,同樣也必須讓“總管”知道。

    Zookeeper 不僅能夠幫你維護當前的集群中機器的服務狀態(tài),而且能夠幫你選出一個“總管”,讓這個總管來管理集群,這就是 Zookeeper 的另一個功能 Leader Election。

    它們的實現(xiàn)方式都是在 Zookeeper 上創(chuàng)建一個 EPHEMERAL 類型的目錄節(jié)點,然后每個 Server 在它們創(chuàng)建目錄節(jié)點的父目錄節(jié)點上調(diào)用 getChildren(String path, boolean watch) 方法并設置 watch 為 true,由于是 EPHEMERAL 目錄節(jié)點,當創(chuàng)建它的 Server 死去,這個目錄節(jié)點也隨之被刪除,所以 Children 將會變化,這時 getChildren上的 Watch 將會被調(diào)用,所以其它 Server 就知道已經(jīng)有某臺 Server 死去了。新增 Server 也是同樣的原理。

    Zookeeper 如何實現(xiàn) Leader Election,也就是選出一個 Master Server。和前面的一樣每臺 Server 創(chuàng)建一個 EPHEMERAL 目錄節(jié)點,不同的是它還是一個 SEQUENTIAL 目錄節(jié)點,所以它是個 EPHEMERAL_SEQUENTIAL 目錄節(jié)點。之所以它是 EPHEMERAL_SEQUENTIAL 目錄節(jié)點,是因為我們可以給每臺 Server 編號,我們可以選擇當前是最小編號的 Server 為 Master,假如這個最小編號的 Server 死去,由于是 EPHEMERAL 節(jié)點,死去的 Server 對應的節(jié)點也被刪除,所以當前的節(jié)點列表中又出現(xiàn)一個最小編號的節(jié)點,我們就選擇這個節(jié)點為當前 Master。這樣就實現(xiàn)了動態(tài)選擇 Master,避免了傳統(tǒng)意義上單 Master 容易出現(xiàn)單點故障的問題。


    圖 3. 集群管理結(jié)構(gòu)圖
    圖 3. 集群管理結(jié)構(gòu)圖 

    這部分的示例代碼如下,完整的代碼請看附件:


    清單 3. Leader Election 關(guān)鍵代碼
                void findLeader() throws InterruptedException {
                byte[] leader = null;
                try {
                leader = zk.getData(root + "/leader", true, null);
                } catch (Exception e) {
                logger.error(e);
                }
                if (leader != null) {
                following();
                } else {
                String newLeader = null;
                try {
                byte[] localhost = InetAddress.getLocalHost().getAddress();
                newLeader = zk.create(root + "/leader", localhost,
                ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL);
                } catch (Exception e) {
                logger.error(e);
                }
                if (newLeader != null) {
                leading();
                } else {
                mutex.wait();
                }
                }
                }
                

    共享鎖(Locks)

    共享鎖在同一個進程中很容易實現(xiàn),但是在跨進程或者在不同 Server 之間就不好實現(xiàn)了。Zookeeper 卻很容易實現(xiàn)這個功能,實現(xiàn)方式也是需要獲得鎖的 Server 創(chuàng)建一個 EPHEMERAL_SEQUENTIAL 目錄節(jié)點,然后調(diào)用 getChildren方法獲取當前的目錄節(jié)點列表中最小的目錄節(jié)點是不是就是自己創(chuàng)建的目錄節(jié)點,如果正是自己創(chuàng)建的,那么它就獲得了這個鎖,如果不是那么它就調(diào)用exists(String path, boolean watch) 方法并監(jiān)控 Zookeeper 上目錄節(jié)點列表的變化,一直到自己創(chuàng)建的節(jié)點是列表中最小編號的目錄節(jié)點,從而獲得鎖,釋放鎖很簡單,只要刪除前面它自己所創(chuàng)建的目錄節(jié)點就行了。


    圖 4. Zookeeper 實現(xiàn) Locks 的流程圖
    圖 4. Zookeeper 實現(xiàn) Locks 的流程圖 

    同步鎖的實現(xiàn)代碼如下,完整的代碼請看附件:


    清單 4. 同步鎖的關(guān)鍵代碼
                void getLock() throws KeeperException, InterruptedException{
                List<String> list = zk.getChildren(root, false);
                String[] nodes = list.toArray(new String[list.size()]);
                Arrays.sort(nodes);
                if(myZnode.equals(root+"/"+nodes[0])){
                doAction();
                }
                else{
                waitForLock(nodes[0]);
                }
                }
                void waitForLock(String lower) throws InterruptedException, KeeperException {
                Stat stat = zk.exists(root + "/" + lower,true);
                if(stat != null){
                mutex.wait();
                }
                else{
                getLock();
                }
                }
                

    隊列管理

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

    1. 當一個隊列的成員都聚齊時,這個隊列才可用,否則一直等待所有成員到達,這種是同步隊列。
    2. 隊列按照 FIFO 方式進行入隊和出隊操作,例如實現(xiàn)生產(chǎn)者和消費者模型。

    同步隊列用 Zookeeper 實現(xiàn)的實現(xiàn)思路如下:

    創(chuàng)建一個父目錄 /synchronizing,每個成員都監(jiān)控標志(Set Watch)位目錄 /synchronizing/start 是否存在,然后每個成員都加入這個隊列,加入隊列的方式就是創(chuàng)建 /synchronizing/member_i 的臨時目錄節(jié)點,然后每個成員獲取 / synchronizing 目錄的所有目錄節(jié)點,也就是 member_i。判斷 i 的值是否已經(jīng)是成員的個數(shù),如果小于成員個數(shù)等待 /synchronizing/start 的出現(xiàn),如果已經(jīng)相等就創(chuàng)建 /synchronizing/start。

    用下面的流程圖更容易理解:


    圖 5. 同步隊列流程圖
    圖 5. 同步隊列流程圖 

    同步隊列的關(guān)鍵代碼如下,完整的代碼請看附件:


    清單 5. 同步隊列
                void addQueue() throws KeeperException, InterruptedException{
                zk.exists(root + "/start",true);
                zk.create(root + "/" + name, new byte[0], Ids.OPEN_ACL_UNSAFE,
                CreateMode.EPHEMERAL_SEQUENTIAL);
                synchronized (mutex) {
                List<String> list = zk.getChildren(root, false);
                if (list.size() < size) {
                mutex.wait();
                } else {
                zk.create(root + "/start", new byte[0], Ids.OPEN_ACL_UNSAFE,
                CreateMode.PERSISTENT);
                }
                }
                }
                

    當隊列沒滿是進入 wait(),然后會一直等待 Watch 的通知,Watch 的代碼如下:

     public void process(WatchedEvent event) {
                if(event.getPath().equals(root + "/start") &&
                event.getType() == Event.EventType.NodeCreated){
                System.out.println("得到通知");
                super.process(event);
                doAction();
                }
                }
                

    FIFO 隊列用 Zookeeper 實現(xiàn)思路如下:

    實現(xiàn)的思路也非常簡單,就是在特定的目錄下創(chuàng)建 SEQUENTIAL 類型的子目錄 /queue_i,這樣就能保證所有成員加入隊列時都是有編號的,出隊列時通過 getChildren( ) 方法可以返回當前所有的隊列中的元素,然后消費其中最小的一個,這樣就能保證 FIFO。

    下面是生產(chǎn)者和消費者這種隊列形式的示例代碼,完整的代碼請看附件:


    清單 6. 生產(chǎn)者代碼
                boolean produce(int i) throws KeeperException, InterruptedException{
                ByteBuffer b = ByteBuffer.allocate(4);
                byte[] value;
                b.putInt(i);
                value = b.array();
                zk.create(root + "/element", value, ZooDefs.Ids.OPEN_ACL_UNSAFE,
                CreateMode.PERSISTENT_SEQUENTIAL);
                return true;
                }
                


    清單 7. 消費者代碼
                int consume() throws KeeperException, InterruptedException{
                int retvalue = -1;
                Stat stat = null;
                while (true) {
                synchronized (mutex) {
                List<String> list = zk.getChildren(root, true);
                if (list.size() == 0) {
                mutex.wait();
                } else {
                Integer min = new Integer(list.get(0).substring(7));
                for(String s : list){
                Integer tempValue = new Integer(s.substring(7));
                if(tempValue < min) min = tempValue;
                }
                byte[] b = zk.getData(root + "/element" + min,false, stat);
                zk.delete(root + "/element" + min, 0);
                ByteBuffer buffer = ByteBuffer.wrap(b);
                retvalue = buffer.getInt();
                return retvalue;
                }
                }
                }
                }
                

    總結(jié)

    Zookeeper 作為 Hadoop 項目中的一個子項目,是 Hadoop 集群管理的一個必不可少的模塊,它主要用來控制集群中的數(shù)據(jù),如它管理 Hadoop 集群中的 NameNode,還有 Hbase 中 Master Election、Server 之間狀態(tài)同步等。

    本文介紹的 Zookeeper 的基本知識,以及介紹了幾個典型的應用場景。這些都是 Zookeeper 的基本功能,最重要的是 Zoopkeeper 提供了一套很好的分布式集群管理的機制,就是它這種基于層次型的目錄樹的數(shù)據(jù)結(jié)構(gòu),并對樹中的節(jié)點進行有效管理,從而可以設計出多種多樣的分布式的數(shù)據(jù)管理模型,而不僅僅局限于上面提到的幾個常用應用場景。


    posted on 2011-05-04 18:46 ivaneeo 閱讀(1047) 評論(0)  編輯  收藏 所屬分類: java魔力
    主站蜘蛛池模板: 黄床大片免费30分钟国产精品| 国产午夜无码精品免费看| 亚洲综合精品网站| a级毛片在线免费观看| 久久丫精品国产亚洲av不卡| 成全视频在线观看免费高清动漫视频下载| 亚洲AV成人无码久久WWW| 亚洲无av在线中文字幕| 午夜宅男在线永久免费观看网| 色偷偷亚洲男人天堂| 亚洲成A人片在线观看无码不卡 | 亚洲国产精品无码久久一区二区| 丁香花免费完整高清观看| 人人爽人人爽人人片av免费| 亚洲福利一区二区精品秒拍| 国产美女无遮挡免费视频网站| 免费黄网站在线看| 亚洲男同gay片| 亚洲AV无码一区东京热久久| 国产成人免费福利网站| 污视频在线免费观看| 日韩大片免费观看视频播放| 久久久WWW免费人成精品| 亚洲剧情在线观看| 亚洲日韩精品一区二区三区| 精品国产免费观看| 最近最新高清免费中文字幕 | 色婷婷综合缴情综免费观看| 亚洲国产成人久久| 国产精品国产亚洲精品看不卡| 日韩电影免费在线观看视频| 无码国产精品一区二区免费3p | 亚洲无线码一区二区三区| 永久免费无码网站在线观看| 色欲色香天天天综合网站免费 | 男人j进入女人j内部免费网站| 免费观看又污又黄在线观看| 国产成人亚洲综合一区| 亚洲精品美女在线观看| 亚洲性猛交XXXX| 亚洲成AV人在线观看网址|