1. 程式人生 > >ZooKeeper學習第三期---Zookeeper命令操作

ZooKeeper學習第三期---Zookeeper命令操作

一、Zookeeper的四字命令

Zookeeper支援某些特定的四字命令字母與其的互動。他們大多數是查詢命令,用來獲取Zookeeper服務的當前狀態及相關資訊。使用者在客戶端可以通過telnet或nc向Zookeeper提交相應的命令。Zookeeper常用的四字命令見下圖所示。

下圖,是Zookeeper四字命令的一個簡單用例。

[root@hadoop ~]# echo ruok|nc localhost 2181
[root@hadoop ~]# zkServer.sh start zoo1.cfg
JMX enabled by default
Using config: /usr/local/zk/bin/../conf/zoo1.cfg
Starting zookeeper ... STARTED
[root@hadoop ~]#  zkServer.sh start zoo2.cfg
JMX enabled by default
Using config: /usr/local/zk/bin/../conf/zoo2.cfg
Starting zookeeper ... STARTED
[root@hadoop ~]#  zkServer.sh start zoo3.cfg
JMX enabled by default
Using config: /usr/local/zk/bin/../conf/zoo3.cfg
Starting zookeeper ... STARTED
[root@hadoop ~]# echo ruok|nc localhost 2181
imok[root@hadoop ~]# echo ruok|nc localhost 2182
imok[root@hadoop ~]# echo ruok|nc localhost 2183
imok[root@hadoop ~]# echo conf|nc localhost 2181
clientPort=2181
dataDir=/usr/local/zk/data_1/version-2
dataLogDir=/usr/local/zk/logs_1/version-2
tickTime=2000
maxClientCnxns=60
minSessionTimeout=4000
maxSessionTimeout=40000
serverId=0
initLimit=10
syncLimit=5
electionAlg=3
electionPort=3387
quorumPort=2287
peerType=0
[root@hadoop ~]#

二、Zookeeper的簡單操作

2.1  Zookeeper的shell操作

2.1.1 Zookeeper命令工具

再啟動Zookeeper服務之後,輸入以下命令,連線到Zookeeper服務:

    zkCli.sh -server localhost:2181

執行結果如下所示:

[root@hadoop ~]# zkCli.sh -server localhost:2181
Connecting to localhost:2181
2014-10-17 03:35:51,051 [myid:] - INFO  [main:Environment@100] - Client environment:zookeeper.version=3.4.5-1392090, built on 09/30/2012 17:52 GMT
2014-10-17 03:35:51,055 [myid:] - INFO  [main:Environment@100] - Client environment:host.name=hadoop
2014-10-17 03:35:51,057 [myid:] - INFO  [main:Environment@100] - Client environment:java.version=1.6.0_24
2014-10-17 03:35:51,057 [myid:] - INFO  [main:Environment@100] - Client environment:java.vendor=Sun Microsystems Inc.
2014-10-17 03:35:51,066 [myid:] - INFO  [main:Environment@100] - Client environment:java.home=/usr/local/jdk/jre
2014-10-17 03:35:51,079 [myid:] - INFO  [main:Environment@100] - Client environment:java.class.path=/usr/local/zk/bin/../build/classes:/usr/local/zk/bin/../build/lib/*.jar:/usr/local/zk/bin/../lib/slf4j-log4j12-1.6.1.jar:/usr/local/zk/bin/../lib/slf4j-api-1.6.1.jar:/usr/local/zk/bin/../lib/netty-3.2.2.Final.jar:/usr/local/zk/bin/../lib/log4j-1.2.15.jar:/usr/local/zk/bin/../lib/jline-0.9.94.jar:/usr/local/zk/bin/../zookeeper-3.4.5.jar:/usr/local/zk/bin/../src/java/lib/*.jar:/usr/local/zk/bin/../conf:
2014-10-17 03:35:51,083 [myid:] - INFO  [main:Environment@100] - Client environment:java.library.path=/usr/local/jdk/jre/lib/i386/client:/usr/local/jdk/jre/lib/i386:/usr/local/jdk/jre/../lib/i386:/usr/java/packages/lib/i386:/lib:/usr/lib
2014-10-17 03:35:51,084 [myid:] - INFO  [main:Environment@100] - Client environment:java.io.tmpdir=/tmp
2014-10-17 03:35:51,086 [myid:] - INFO  [main:Environment@100] - Client environment:java.compiler=<NA>
2014-10-17 03:35:51,099 [myid:] - INFO  [main:Environment@100] - Client environment:os.name=Linux
2014-10-17 03:35:51,100 [myid:] - INFO  [main:Environment@100] - Client environment:os.arch=i386
2014-10-17 03:35:51,101 [myid:] - INFO  [main:Environment@100] - Client environment:os.version=2.6.32-358.el6.i686
2014-10-17 03:35:51,101 [myid:] - INFO  [main:Environment@100] - Client environment:user.name=root
2014-10-17 03:35:51,102 [myid:] - INFO  [main:Environment@100] - Client environment:user.home=/root
2014-10-17 03:35:51,106 [myid:] - INFO  [main:Environment@100] - Client environment:user.dir=/root
2014-10-17 03:35:51,120 [myid:] - INFO  [main:ZooKeeper@438] - Initiating client connection, connectString=localhost:2181 sessionTimeout=30000 watcher=org.apache.zookeeper.ZooKeeperMain$MyWatcher@b02e7a
Welcome to ZooKeeper!
JLine support is enabled
2014-10-17 03:35:51,233 [myid:] - INFO  [main-SendThread(localhost:2181):ClientCnxn$SendThread@966] - Opening socket connection to server localhost/127.0.0.1:2181. Will not attempt to authenticate using SASL (Unable to locate a login configuration)
2014-10-17 03:35:51,247 [myid:] - INFO  [main-SendThread(localhost:2181):ClientCnxn$SendThread@849] - Socket connection established to localhost/127.0.0.1:2181, initiating session
[zk: localhost:2181(CONNECTING) 0] 2014-10-17 03:35:51,290 [myid:] - INFO  [main-SendThread(localhost:2181):ClientCnxn$SendThread@1207] - Session establishment complete on server localhost/127.0.0.1:2181, sessionid = 0x491da0e20b0000, negotiated timeout = 30000

WATCHER::

WatchedEvent state:SyncConnected type:None path:null

[zk: localhost:2181(CONNECTED) 0]

連線成功之後,系統會輸出Zookeeper的相關環境及配置資訊,並在螢幕輸出“welcome to Zookeeper!”等資訊。輸入help之後,螢幕會輸出可用的Zookeeper命令,如下圖所示

2.1.2 使用Zookeeper命令的簡單操作步驟

(1) 使用ls命令檢視當前Zookeeper中所包含的內容:ls /

[zk: localhost:2181(CONNECTED) 1] ls /
[zookeeper]
[zk: localhost:2181(CONNECTED) 2]

(2) 建立一個新的Znode節點"zk",以及和它相關字元,執行命令:create /zk myData

[zk: localhost:2181(CONNECTED) 2] create /zk myData
Created /zk

(3) 再次使用ls命令來檢視現在Zookeeper的中所包含的內容:ls /

[zk: localhost:2181(CONNECTED) 3] ls /
[zk, zookeeper]

此時看到,zk節點已經被建立。  

(4) 使用get命令來確認第二步中所建立的Znode是否包含我們建立的字串,執行命令:get /zk

[zk: localhost:2181(CONNECTED) 4] get /zk
myData
cZxid = 0x500000006
ctime = Fri Oct 17 03:54:20 PDT 2014
mZxid = 0x500000006
mtime = Fri Oct 17 03:54:20 PDT 2014
pZxid = 0x500000006
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 6
numChildren = 0

(5) 接下來通過set命令來對zk所關聯的字串進行設定,執行命令:set /zk jiang1234

[zk: localhost:2181(CONNECTED) 5] set /zk jiang2014
cZxid = 0x500000006
ctime = Fri Oct 17 03:54:20 PDT 2014
mZxid = 0x500000007
mtime = Fri Oct 17 03:55:50 PDT 2014
pZxid = 0x500000006
cversion = 0
dataVersion = 1
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 9
numChildren = 0

(6) 再次使用get命令來檢視,上次修改的內容,執行命令:get /zk

[zk: localhost:2181(CONNECTED) 6] get /zk
jiang2014
cZxid = 0x500000006
ctime = Fri Oct 17 03:54:20 PDT 2014
mZxid = 0x500000007
mtime = Fri Oct 17 03:55:50 PDT 2014
pZxid = 0x500000006
cversion = 0
dataVersion = 1
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 9
numChildren = 0

(7) 下面我們將剛才建立的Znode刪除,執行命令:delete /zk

[zk: localhost:2181(CONNECTED) 7] delete /zk

(8) 最後再次使用ls命令檢視Zookeeper中的內容,執行命令:ls /

[zk: localhost:2181(CONNECTED) 8] ls /
[zookeeper]

經過驗證,zk節點已經刪除。

2.2 Zookeeper的api的簡單使用

2.2.1 ZookeeperAPI簡介

Zookeeper API共包含五個包,分別為:

  (1)org.apache.zookeeper
  (2)org.apache.zookeeper.data
  (3)org.apache.zookeeper.server
  (4)org.apache.zookeeper.server.quorum
  (5)org.apache.zookeeper.server.upgrade

其中org.apache.zookeeper,包含Zookeeper類,他是我們程式設計時 最常用的類檔案。這個類是Zookeeper客戶端的主要類檔案。如果要使用Zookeeper服務,應用程式首先必須建立一個Zookeeper例項, 這時就需要使用此類。一旦客戶端和Zookeeper服務建立起了連線,Zookeeper系統將會給次連線會話分配一個ID值,並且客戶端將會週期性的 向伺服器端傳送心跳來維持會話連線。只要連線有效,客戶端就可以使用Zookeeper API來做相應處理了。

Zookeeper類提供瞭如下圖所示的幾類主要方法

2.2.2 Zookeeper API的使用

這裡通過一個例子來簡單介紹如何使用Zookeeper API 編寫自己的應用程式,程式碼如下:

package org.zk;

import java.io.IOException;
import java.util.List;

import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.ZooKeeper;

public class ListGroup extends ConnectionWatcher {
    public void list(String groupNmae) throws KeeperException, InterruptedException{
        String path ="/"+groupNmae;
        try {
            List<String> children = zk.getChildren(path, false);
            if(children.isEmpty()){
                System.out.printf("No memebers in group %s\n",groupNmae);
                System.exit(1);
            }
            for(String child:children){
                System.out.println(child);
            }
        } catch (KeeperException.NoNodeException e) {
            System.out.printf("Group %s does not exist \n", groupNmae);
            System.exit(1);
        } 
    }
    public static void main(String[] args) throws IOException, InterruptedException, KeeperException {
        ListGroup listGroup = new ListGroup();
        listGroup.connect(args[0]);
        listGroup.list(args[1]);
        listGroup.close();
    }
}

此類包含兩個主要的 ZooKeeper 函式,分別為 createZKInstance ()和 ZKOperations()。其中:

(1) createZKInstance()函式負責對 ZooKeeper 例項 zk 進行初始化。

ZooKeeper 類有兩個建構函式,我們這裡使用“ ZooKeeper (String connectString, int sessionTimeout ,Watcher watcher )”對其進行初始化。因此,我們需要提供初始化所需的,連線字串資訊,會話超時時間,以及一個 watcher 例項。 19行到 25行程式碼,是程式所構造的一個watcher 例項,它能夠輸出所發生的事件。

(2) ZKOperations ()函式是我們所定義的對節點的一系列操作。

它包括:建立 ZooKeeper 節點( 35行到 36行程式碼)、檢視節點( 38 行到 39 行程式碼)、修改節點資料( 41 行到 42 行程式碼)、檢視修改後節點資料( 44 行到 45行程式碼)、刪除節點( 47行到 48行程式碼)、檢視節點是否存在( 50 行到 51 行程式碼)。

程式碼的執行結果如下:

1. 建立ZooKeeper節點(Znode:/znode;資料:myData2;許可權:OPEN_ACL_UNSAFE;節點型別:Persistent)
 None
2. 檢視節點是否建立成功:
 /znode myData2
3. 修改節點資料:
4. 檢視是否修改成功:
 jiang1234
5. 刪除節點:
6. 檢視/znode節點狀態:
 節點間狀態:[null]

三、ZooKeeper示例

假設一組伺服器,用於為客戶端提供一些服務。我們希望每個客戶端都能夠能夠找到其中一臺伺服器,使其能夠使用這些服務,挑戰之一就是維護這組伺服器 列表。這組伺服器的成員列表明顯不能存在網路中的單個節點上,因為如果那個節點發生故障,就意味著是整個系統的故障(我們希望這個列表有很高的可用性)。 假設我們有了一個可靠的方法解決了這個成員列表的儲存問題。如果其中一臺伺服器出現故障,我們仍然需要解決如何從伺服器成員列表中將它刪除的問題。某個進 程需要負責刪除故障伺服器,但注意不能由故障伺服器自己來完成,因為故障伺服器已經不再執行。

我們所描述的不是一個被動的分散式資料結構,而是一個主動的、能夠在某個外部事件發生時修改資料項狀態的資料結構。ZooKeeper提供這種服務,所以讓我們看看如何使用它來實現這種眾所周知的組成員管理應用。

ZooKeeper中的組成員關係

理解ZooKeeper的一種方法就是將其看作一個具有高可用性的檔案系統。但這個檔案系統中沒有檔案和目錄,而是統一使用“節點”(node)的概念,稱為znode。znode既可以作為儲存資料的容器(如同檔案),也可以作為儲存其他znode的容器(如同目錄)。所有的znode構成一個層次化的名稱空間。一種自然的建立組成員列表的方式就是利用這種層次結構,建立一個以組名為節點名的znode作為父節點,然後以組成員名(伺服器名)為節點名來建立作為子節點的znode。如下圖給出了一組具有層次結構的znode。

 

在這個示例中,我們沒有在任何znode中儲存資料,但在一個真實的應用中,你可以將“關於成員的資料”儲存在它們的znode中,例如主機名。

3.1 建立組

3.1.1 程式碼示例

讓我們通過編寫一段程式的方式來再次詳細介紹ZooKeeper的Java API,這段示例程式用於為組名為/zoo的組建立一個znode。程式碼參見如下

程式碼 該程式在ZooKeeper中新建表示組的Znode

package org.zk;

import java.io.IOException;
import java.util.concurrent.CountDownLatch;

import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.Watcher.Event.KeeperState;
import org.apache.zookeeper.ZooDefs.Ids;
import org.apache.zookeeper.ZooKeeper;

public class CreateGroup implements Watcher{
    private static final int SESSION_TIMEOUT=5000;
    
    private ZooKeeper zk;
    private CountDownLatch connectedSignal=new CountDownLatch(1);
    @Override
    public void process(WatchedEvent event) {
        if(event.getState()==KeeperState.SyncConnected){
            connectedSignal.countDown();
        }
    }
    
    public static void main(String[] args) throws IOException, InterruptedException, KeeperException {
        CreateGroup createGroup = new CreateGroup();
        createGroup.connect(args[0]);
        createGroup.create(args[1]);
        createGroup.close();
    }

    private void close() throws InterruptedException {
        zk.close();
    }

    private void create(String groupName) throws KeeperException, InterruptedException {
        String path="/"+groupName;
        if(zk.exists(path, false)== null){
            zk.create(path, null/*data*/, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
        }
        System.out.println("Created:"+path);
    } 

    private void connect(String hosts) throws IOException, InterruptedException {
        zk = new ZooKeeper(hosts, SESSION_TIMEOUT, this);
        connectedSignal.await();
    }
}

執行該程式需要配置classpath環境變數或者在執行java命令時新增-classpath選項,具體執行方式參見:http://www.cnblogs.com/sunddenly/p/4050812.html

執行後的結果為:

[root@hadoop code]# ls
build  classes  CreateGroup.java  HelloWorld.java  jar.jar  PackageTest.java  zookeeper.out
[root@hadoop code]# javac -d ./classes CreateGroup.java
[root@hadoop code]# java org.zk.CreateGroup localhost:2181 zoo
2014-10-28 18:00:26,154 [myid:] - INFO  [main:Environment@100] - Client environment:zookeeper.version=3.4.5-1392090, built on
2014-10-28 18:00:26,157 [myid:] - INFO  [main:Environment@100] - Client environment:host.name=hadoop
2014-10-28 18:00:26,157 [myid:] - INFO  [main:Environment@100] - Client environment:java.version=1.6.0_24
2014-10-28 18:00:26,157 [myid:] - INFO  [main:Environment@100] - Client environment:java.vendor=Sun Microsystems Inc.
2014-10-28 18:00:26,158 [myid:] - INFO  [main:Environment@100] - Client environment:java.home=/usr/local/jdk/jre
2014-10-28 18:00:26,158 [myid:] - INFO  [main:Environment@100] - Client environment:java.class.path=……
……
Created:/zoo
2014-10-28 18:00:26,236 [myid:] - INFO  [main:ZooKeeper@684] - Session: 0x4956f7f1d70005 closed
2014-10-28 18:00:26,237 [myid:] - INFO  [main-EventThread:ClientCnxn$EventThread@509] - EventThread shut down
[root@hadoop code]#

 3.1.2 程式碼分析

在上面程式碼中,main()方法執行時,建立了一個CreateGroup例項並且呼叫了這個例項的connect()方法。connect方法例項化了一個新的ZooKeeper類的物件,這個類是客戶端API中的主要類,並且負責維護客戶端和ZooKeeper服務之間的連線。ZooKeeper類的建構函式有三個引數:
  第一個是:ZooKeeper服務的主機地址,可指定埠,預設埠是2181。
  第二個是:以毫秒為單位的會話超時引數,這裡我們設成5秒。
  第三個是:引數是一個Watcher物件的例項。
Watcher物件接收來自於ZooKeeper的回撥,以獲得各種事件的通知。在這個例子中,CreateGroup是一個Watcher物件,因此我們將它傳遞給ZooKeeper的建構函式。
當一個ZooKeeper的例項被建立時,會啟動一個執行緒連線到ZooKeeper服務。由於對建構函式的呼叫是立即返回的,因此在使用新建的ZooKeeper物件之前一定要等待其與ZooKeeper服務之間的連線建立成功。我們使用Java的CountDownLatch類來阻止使用新建的ZooKeeper物件,直到這個ZooKeeper物件已經準備就緒。這就是Watcher類的
用途,在它的介面中只有一個方法:
    public void process(WatcherEvent event);
客 戶端已經與ZooKeeper建立連線後,Watcher的process()方法會被呼叫,引數是一個表示該連線的事件。在接收到一個連線事件(由 Watcher.Event.KeeperState的列舉型值SyncConnected來表示)時,我們通過呼叫CountDownLatch的countDown()方法來遞減它的計數器。鎖存器(latch)被建立時帶有一個值為1的計數器,用於表示在它釋放所有等待執行緒之前需要發生的事件數。在呼叫一歡countDown()方法之後,計數器的值變為0,則await()方法返回。
現在connect()方法已經返回,下一個執行的是CreateGroup的create()方法。在這個方法中,我們使用ZooKeeper例項中的create()方法來建立一個新的ZooKeeper的znode。所需的引數包括:
    路徑:用字串表示。
    znode的內容:位元組陣列,本例中使用空值。
    訪問控制列表:簡稱ACL,本例中使用了完全開放的ACL,允許任何客戶端對znode進行讀寫。
    建立znode的型別:有兩種型別的znode:短暫的和持久的。

建立znode的客戶端斷開連線時,無論客戶端是明確斷開還是因為任何原因而終止,短暫znode都會被ZooKeeper服務刪除。與之相反,當 客戶端斷開連線時,持久znode不會被刪除。我們希望代表一個組的znode存活的時間應當比建立程式的生命週期要長,因此在本例中我們建立了一個持久 的znode。

create()方法的返回值是ZooKeeper所建立的路徑,我們用這個返回值來列印一條表示路徑成功建立的訊息。當我們檢視“順序znode”(sequential znode)時.會發現create()方法返回的路徑與傳遞給該方法的路徑不同。

3.2 加入組

下面的這一段程式用於註冊組的成員。每個組成員將作為一個程式執行,並且加入到組中。當程式退出時,這個組成員應當從組中被刪除。為了實現這一點,我們在ZooKeeper的名稱空間中使用短暫znode來代表一個組成員。

在基類ConnectionWatcher中,對建立和連線ZooKeeper例項的程式邏輯進行了重構,參見程式碼如下

程式碼 用於將成員加入組的程式

package org.zk;

import java.io.IOException;

import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.ZooDefs.Ids;

public class JoinGroup extends ConnectionWatcher{
    public void join(String groupName,String memberName) throws KeeperException, InterruptedException{
        String path="/"+groupName+"/"+memberName;
        String createdPath=zk.create(path, null, Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL);
        System.out.println("Created:"+createdPath);
    }
    public static void main(String[] args) throws InterruptedException, IOException, KeeperException {
        JoinGroup joinGroup = new JoinGroup();
        joinGroup.connect(args[0]);
        joinGroup.join(args[1], args[2]);
        
        //stay alive until process is killed or thread is interrupted
        Thread.sleep(Long.MAX_VALUE);
    }
}

程式碼 3.3 用於等待建立與ZooKeeper連線的輔助類

package org.zk;

import java.io.IOException;
import java.util.concurrent.CountDownLatch;

import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.Watcher.Event.KeeperState;
import org.apache.zookeeper.ZooKeeper;

public class ConnectionWatcher implements Watcher{
    private static final int SESSION_TIMEOUT=5000;
    
    protected ZooKeeper zk;
    CountDownLatch connectedSignal=new CountDownLatch(1);
    public void connect(String host) throws IOException, InterruptedException{
        zk=new ZooKeeper(host, SESSION_TIMEOUT, this);
        connectedSignal.await();
    }
    @Override
    public void process(WatchedEvent event) {
        if(event.getState()==KeeperState.SyncConnected){
            connectedSignal.countDown();
        }
    }
    public void close() throws InterruptedException{
        zk.close();
    }

}

JoinGroup的程式碼與CreateGroup非常相似,在它的join()方法中,建立短暫znode,作為組znode的子節點,然後通過 休眠來模擬正在做某種工作,直到該程序被強行終止。接著,你會看到隨著程序終止,這個短暫znode被ZooKeeper刪除。

3.3 列出組成員

現在,我們需要一段程式來檢視組成員,參見程式碼如下:

程式碼 用於列出組成員的程式

package org.zk;

import java.io.IOException;
import java.util.List;

import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.ZooKeeper;

public class ListGroup extends ConnectionWatcher {
    public void list(String groupNmae) throws KeeperException, InterruptedException{
        String path ="/"+groupNmae;
        try {
            List<String> children = zk.getChildren(path, false);
            if(children.isEmpty()){
                System.out.printf("No memebers in group %s\n",groupNmae);
                System.exit(1);
            }
            for(String child:children){
                System.out.println(child);
            }
        } catch (KeeperException.NoNodeException e) {
            System.out.printf("Group %s does not exist \n", groupNmae);
            System.exit(1);
        } 
    }
    public static void main(String[] args) throws IOException, InterruptedException, KeeperException {
        ListGroup listGroup = new ListGroup();
        listGroup.connect(args[0]);
        listGroup.list(args[1]);
        listGroup.close();
    }
}

在list()方法中,我們呼叫了getChildren()方法來檢索並列印輸出一個znode的子節點列表,呼叫引數為:該znode的路徑和 設為false的觀察標誌。如果在一znode上設定了觀察標誌,那麼一旦該znode的狀態改變,關聯的觀察(Watcher)會被觸發。雖然在這裡我 們可以不使用觀察,但在檢視一個znode的子節點時,也可以設定觀察,讓應用程式接收到組成員加入、退出和組被刪除的有關通知。

在這段程式中,我們捕捉了KeeperException.NoNodeException異常,代表組的znode不存在時,這個異常就會被拋 出。下面看一下ListGroup程式的工作過程:雖然搭建了分散式的ZooKeeper,但分散式ZooKeeper啟動執行比較耗時,我在這採用前面 提到的複製模式下的ZooKeeper來進行測試。

首先我們得啟動ZooKeeper,啟動以後將上面的源程式放到Linux目錄中並進行編譯,我將其放到了"/usr/code"目錄下,並在該目錄下建立一個classes資料夾,用於存放生成位元組碼檔案:

[root@hadoop ~]# cd /usr/code
[root@hadoop code]# ls
ConnectionWatcher.java  DeleteGroup.java  ListGroup.java
classes  CreateGroup.java  JoinGroup.java  PackageTest.java
[root@hadoop code]# javac -d ./classes ConnectionWatcher.java
[root@hadoop code]# javac -d ./classes *.java

由於目前我們還沒有在組中新增任何成員,因此zoo是空的:

[root@hadoop code]# java org.zk.ListGroup  localhost zoo
2014-10-30 01:52:19,703 [myid:] - INFO  [main:Environment@100] - Client environment:……
……
No memebers in group zoo

我們可以使用JoinGroup來向組中新增成員。在sleep語句的作用下,這些作為組成員的znode不會自己終止,所以我們可以,以後臺程序的方式來啟動他們:

[root@hadoop code]# java org.zk.JoinGroup localhost zoo duck &
2014-10-30 02:06:05,018 [myid:] - INFO  [main:Environment@100] - Client environment:……
……
Created:/zoo/duck
[root@hadoop code]# java org.zk.JoinGroup localhost zoo cow &
2014-10-30 02:06:05,018 [myid:] - INFO  [main:Environment@100] - Client environment:……
……
Created:/zoo/cow
[root@hadoop code]# java org.zk.JoinGroup localhost zoo goat &
2014-10-30 02:06:05,018 [myid:] - INFO  [main:Environment@100] - Client environment:……
……
Created:/zoo/goat

最後一行命令儲存了將goat新增到組中的java程序的ID。我們需要儲存這個程序的ID,以便能夠在檢視組成員之後殺死程序。

[root@hadoop code]#
2014-10-30 03:15:30,619 [myid:] - INFO  [main:Environment@100] - Client environment:……
……
duck
cow
goat

為了從組中刪除一個成員,我們殺死了goat所對應的程序:

[root@hadoop code]# kill $goat_pid
幾秒鐘之後,由於程序的ZooKeeper會話已經結束(超時為5秒),所以goat會從組成員列表消失,並且對應的短暫znode也已經被刪除。
[root@hadoop code]# java org.zk.ListGroup localhost zoo
2014-10-30 03:23:41,120 [myid:] - INFO  [main:Environment@100] - Client environment:……
……
duck
cow

對於參與到一個分散式系統中的節點,這樣就有了一個建立節點列表的方法。這些節點也許彼此並不瞭解。例如,一個想使用列表中節點來完成某些工作的客 戶端,能夠在這些節點不知道客戶端的情況下發現它們。最後,注意,組成員關係管理並不能解決與節點通訊過程中出現的網路問題。即使一個節點是一個組中的成 員,在與其通訊的過程中仍然會出現故障,這種故障必須以一種合適的方式解決(重試、使用組中另外一個成員等)。☆☆☆

3.4 ZooKeeper命令列工具

ZooKeeper提供了一個命令列工具用於在其名稱空間內進行互動。我們可以使用這個命令工具列出/zoo節點之下的znode列表,如下所示

[root@hadoop code]# zkCli.sh -server localhost ls /zoo
Connecting to localhost
……
WATCHER::
WatchedEvent state:SyncConnected type:None path:null
[duck, cow]
[root@hadoop code]#

3.5 刪除組

下面來看如何刪除一個組。ZooKeeper類提供了一個delete()方法,該方法有兩個引數:

1. 路徑

2. 版本號

如果所提供的版本號與znode的版本號一致,ZooKeeper會刪除這個znode。這是一種樂觀的加鎖機制,使客戶端能夠檢測出對znode的修改衝突。通過將版本號設定為-1,可以繞過這個版本檢測機制,不管znode的版本號是什麼而直接將其刪除。ZooKeeper不支援遞迴的刪除操作,因此在刪除父節點之前必須先刪除子節點。

在程式碼3.5中,DeleteGroup類用於刪除一個組及其所有成員。
程式碼3.5用於刪除一個組及其所有成員的程式

package org.zk;

import java.io.IOException;
import java.util.List;

import org.apache.zookeeper.KeeperException;

public class DeleteGroup extends ConnectionWatcher{
    public void delete(String groupName) throws InterruptedException, KeeperException{
        String path="/"+groupName;
        List<String> children;
        try {
            children = zk.getChildren(path, false);
            for(String child:children){
                zk.delete(path+"/"+child, -1);            
            }
            zk.delete(path, -1);
        } catch (KeeperException.NoNodeException e) {
            System.out.printf("Group %s does not exist\n", groupName);
            System.exit(1);
        }    
    }
    public static void main(String[] args) throws InterruptedException, IOException, KeeperException {
        DeleteGroup deleteGroup = new DeleteGroup();
        deleteGroup.connect(args[0]);
        deleteGroup.delete(args[1]);
        deleteGroup.close();
    }
}

最後,我們可以刪除之前所建立的zoo組:

[root@hadoop code]# java org.zk.DeleteGroup localhost zoo
……
[root@hadoop code]# java org.zk.ListGroup localhost zoo
2014-10-30 05:39:41,974 [myid:] - INFO  [main:Environment@100] - Client environment:……
Group zoo does not exist
[root@hadoop code]#