第8章 ZooKeeper操做

8.1 集羣環境搭建

【操做目的】
因爲在ZooKeeper集羣中,會有一個Leader服務器負責管理和協調其餘集羣服務器,所以服務器的數量一般都是單數,例如3,5,7...等,這樣數量爲2n+1的服務器就能夠容許最多n臺服務器的失效。
【操做步驟】
本例中,咱們仍然使用三個節點搭建部署ZooKeeper集羣,搭建步驟以下:node

1.上傳ZooKeeper安裝文件

在centos01節點中,上傳ZooKeeper安裝文件zookeeper-3.4.9.tar.gz到目錄/opt/softwares/中,並將其解壓到目錄/opt/modules/,解壓命令以下:shell

tar -zxvf zookeeper-3.4.9.tar.gz -C /opt/modules/

2.編寫配置文件

(1)在ZooKeeper安裝目錄下新建文件夾dataDir,用於存放ZooKeeper數據。
(2)在ZooKeeper安裝目錄下的conf文件夾中新建配置文件zoo.cfg,加入如下內容:apache

tickTime=2000
initLimit=10
syncLimit=5
dataDir=/opt/modules/zookeeper-3.4.9/dataDir
clientPort=2181

server.1=centos01:2888:3888
server.2=centos02:2888:3888
server.3=centos03:2888:3888

上述參數說明以下:
initLimit:集羣中的Follower服務器初始化鏈接Leader服務器時能等待的最大心跳數(鏈接超時時長)。默認爲10,即若是通過10個心跳以後Follower服務器仍然沒有收到Leader服務器的返回信息,則鏈接失敗。本例中該參數值爲5,參數tickTime爲2000,則鏈接超時時長爲52000=10秒(即tickTimeinitLimit=10秒)。
syncLimit:集羣中的Follower服務器與Leader服務器之間發送消息以及請求/應答時所能等待的最多心跳數。本例中,tickTime的值爲2,時長爲2*2000=4秒。
server.id:標識不一樣的ZooKeeper服務器。ZooKeeper能夠從「server.id=host:port1:port2」中讀取相關信息。其中,id值必須在整個集羣中是惟一的,且大小在1到255之間;host是服務器的名稱或IP地址;第一個端口(port1)是Leader端口,即該服務器做爲Leader時供Follower鏈接的端口;第二個端口(port2)是選舉端口,即選舉Leader服務器時供其它Follower鏈接的端口。
dataDir:ZooKeeper保存數據的目錄。
clientPort:客戶端鏈接ZooKeeper服務器的端口,ZooKeeper會監聽這個端口,接收客戶端的請求。
(3)在配置文件zoo.cfg中的參數dataDir指定的目錄下(此處爲ZooKeeper安裝目錄下的dataDir文件夾)新建一個名爲myid的文件,這個文件僅包含一行內容,即當前服務器的id值,與參數server.id中的id值相同。本例中,當前服務器(centos01)的id值爲1,則應該在myid文件中寫入數字1。ZooKeeper啓動時會讀取該文件,將其中的數據與zoo.cfg裏寫入的配置信息進行對比,從而獲取當前服務器的身份信息。centos

3.拷貝ZooKeeper安裝信息到其它節點

centos01節點安裝完成後,須要拷貝整個ZooKeeper安裝目錄到centos02和centos03節點,命令以下:api

scp -r /opt/modules/zookeeper-3.4.9/ hadoop@centos02:/opt/modules/
scp -r /opt/modules/zookeeper-3.4.9/ hadoop@centos03:/opt/modules/

4.修改其它節點配置

拷貝完成後,須要將centos02和centos03節點中的myid文件的值修改成對應的數字,即做出如下操做:
修改centos02節點中的opt/modules/zookeeper-3.4.9/dataDir/myid文件中的值爲2。
修改centos03節點中的opt/modules/zookeeper-3.4.9/dataDir/myid文件中的值爲3。數組

5.啓動ZooKeeper

分別進入每一個節點的ZooKeeper安裝目錄,執行以下命令:服務器

bin/zkServer.sh start

輸出如下信息表明啓動成功:
ZooKeeper JMX enabled by default
Using config: /usr/local/zookeeper-3.4.9/bin/../conf/zoo.cfg
Starting zookeeper ... STARTEDeclipse

須要注意的是,每臺服務器都要執行一遍啓動命令,這樣才能使得整個集羣啓動起來。ide

6.查看啓動狀態

分別在各個節點上執行以下命令,查看ZooKeeper服務的狀態:

bin/zkServer.sh status

在centos01節點上查看服務狀態,輸出瞭如下信息:
ZooKeeper JMX enabled by default
Using config: /usr/local/zookeeper-3.4.9/bin/../conf/zoo.cfg
Mode: follower

在centos02服務器上查看服務狀態,輸出瞭如下信息:
ZooKeeper JMX enabled by default
Using config: /usr/local/zookeeper-3.4.9/bin/../conf/zoo.cfg
Mode: follower

在centos03服務器上查看服務狀態,輸出瞭如下信息:
ZooKeeper JMX enabled by default
Using config: /usr/local/zookeeper-3.4.9/bin/../conf/zoo.cfg
Mode: leader

因而可知,本例中centos03服務器上的ZooKeeper服務爲Leader,其他兩個ZooKeeper服務爲Follower。
若是在查看啓動狀態時輸出如下信息,說明ZooKeeper集羣啓動不成功,出現錯誤。
Error contacting service. It is probably not running.
此時須要修改bin/zkEvn.sh文件中的如下內容,將錯誤信息輸出到日誌文件。

if [ "x${ZOO_LOG4J_PROP}" = "x" ]  
then  
    ZOO_LOG4J_PROP="INFO,CONSOLE"  
fi

將上述內容中的CONSOLE改成ROLLINGFILE,修改後的內容以下:

if [ "x${ZOO_LOG4J_PROP}" = "x" ]  
then  
    ZOO_LOG4J_PROP="INFO,ROLLINGFILE"  
fi

修改後,從新啓動ZooKeeper集羣,查看在ZooKeeper安裝目錄下生成的日誌文件zookeeper.log,發現報如下錯誤:
java.net.NoRouteToHostException: 沒有到主機的路由。
產生上述錯誤的緣由是,系統沒有關閉防火牆,致使ZooKeeper集羣間鏈接不成功。所以須要關閉系統防火牆(爲了防止出錯,在最初的集羣環境配置的時候能夠直接將防火牆關閉),CentOS7關閉防火牆的命令以下:

systemctl stop firewalld.service
systemctl disable firewalld.service

關閉各節點的防火牆後,從新啓動ZooKeeper,再一次查看啓動狀態,發現一切正常了。

7.測試客戶端鏈接

在centos01節點上(其它節點也能夠),進入ZooKeeper安裝目錄,執行如下命令,能夠鏈接ZooKeeper服務器,鏈接成功後能夠輸入ZooKeeper的Shell命令進行操做與測試了。

[hadoop@centos01]$ bin/zkCli.sh -server centos01:2181

8.2 命令行操做

【操做目的】
ZooKeeper的命令行工具相似於Shell。當ZooKeeper服務啓動之後,能夠在其中一臺運行ZooKeeper服務的服務器中輸入如下命令(須要進入ZooKeeper安裝目錄),啓動一個客戶端,鏈接到ZooKeeper集羣:
[hadoop@centos01]$ bin/zkCli.sh -server centos01:2181
鏈接成功後,系統會輸出ZooKeeper的運行環境及配置信息,並在屏幕輸出「Welcome to ZooKeeper」等歡迎信息。以後就可使用ZooKeeper命令行工具了。
【操做步驟】
如下是ZooKeeper命令行工具的一些簡單操做示例:
(1)使用ls命令,能夠查看當前ZooKeeper中所包含的內容:

[zk: centos01:2181(CONNECTED) 4] ls /
[zookeeper]

能夠看到,當前有一個名稱爲zookeeper的Znode節點。
(2)使用create命令,能夠建立一個新的Znode節點。例如,建立一個名爲「zk」的Znode以及在它上面存放的元數據字符串「myData」,命令及輸出信息以下:

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

(3)使用get命令,能夠查看某個Znode的詳細信息及其包含的元數據字符串。例如,查看Znode節點/zk的詳細信息,命令及輸出信息以下:

[zk: centos01:2181(CONNECTED) 6] get /zk
myData
cZxid = 0x800000002
ctime = Thu Mar 22 10:12:11 CST 2018
mZxid = 0x800000002
mtime = Thu Mar 22 10:12:11 CST 2018
pZxid = 0x800000002
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 6
numChildren = 0

(4)使用set命令,能夠修改Znode節點的元數據字符串。例如,將Znode節點/zk所關聯的字符串修改成「myDataUpdate」,命令及輸出信息以下:

[zk: centos01:2181(CONNECTED) 10] set /zk "myDataUpdate"
cZxid = 0x800000002
ctime = Thu Mar 22 10:12:11 CST 2018
mZxid = 0x800000005
mtime = Thu Mar 22 10:18:19 CST 2018
pZxid = 0x800000002
cversion = 0
dataVersion = 3
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 12
numChildren = 0

(5)使用delete命令,能夠將某個Znode節點刪除。例如,刪除上面建立的Znode節點/zk,命令以下:

[zk: centos01:2181(CONNECTED) 11] delete /zk

使用ZooKeeper命令行工具也能夠建立有層次的目錄。例如,在/zk節點目錄下建立新的目錄node1,並關聯其元數據爲「nodeData」,命令及輸出信息以下:

[zk: centos01:2181(CONNECTED) 18] create /zk/node1 "nodeData"
Created /zk/node1

8.3 Java API操做

除了可使用命令行方式對ZooKeeper進行操做外,ZooKeeper還提供了Java API操做接口。下面對ZooKeeper的經常使用Java API接口進行介紹。

8.3.1 建立Java工程

在編寫Java API以前,首先須要新建一個ZooKeeper項目。ZooKeeper項目的結構與普通的Java項目同樣,只是依賴的jar包不一樣。
在eclipse中新建一個Maven項目zk_demo(Maven項目的搭建此處不作過多講解),而後在該項目的pom.xml文件中添加如下代碼,以引入ZooKeeper的Java API依賴包:

<dependency>
     <groupId>org.apache.zookeeper</groupId>
     <artifactId>zookeeper</artifactId>
     <version>3.4.9</version>
   </dependency>

配置好pom.xml後,便可進行ZooKeeper Java API的編寫。

8.3.2 建立節點

Zookeeper建立節點不支持遞歸調用,即沒法在父節點不存在的狀況下建立一個子節點,如在/zk0l節點不存在的狀況下建立/zk01/ch01節點;而且若是一個節點已經存在,那麼建立同名節點時,會拋出NodeExistsException異常。
下面咱們建立一個節點/ zk001,節點的元數據爲「zk001_data」,步驟以下:

1.編寫代碼

在新建的zk_demo項目中新建Java類CreatePath.java,完整代碼以下所示:

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

public class CreatePath {

    public static void main(String[] args) throws Exception {
        String connectStr="192.168.170.128:2181,192.168.170.129:2181,192.168.170.130:2181";
        //參數1:服務器鏈接字符串
        //參數2:鏈接超時時間
        //參數3:觀察者對象(回調函數)
        ZooKeeper zk = new ZooKeeper(connectStr, 3000, null);       
        /* 1.CreateMode 取值   
         *  PERSISTENT:持久化節點
         *  PERSISTENT_SEQUENTIAL:順序自動編號的目錄節點
         *  EPHEMERAL:臨時目錄節點,客戶端斷開鏈接時,這種節點會被自動刪除 
         *  EPHEMERAL_SEQUENTIAL:臨時自動編號節點 
         *  */
        String path=zk.create("/zk001", "zk001_data".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
        System.out.println(path);
    }
}

2.程序解讀

ZooKeeper zk  = new ZooKeeper(connectStr, 3000, null);

新建一個ZooKeeper對象,傳入三個參數,第一個參數爲以逗號分隔的服務器鏈接字符串,格式:「host:端口」,這裏須要把全部的ZooKeeper服務器的地址都寫上,而不是隻寫其中一臺。ZooKeeper客戶端對象將從鏈接串中挑選任意一個服務器進行鏈接,若是鏈接失敗,將嘗試鏈接另一個服務器,直到創建鏈接。這樣的好處是能保證ZooKeeper服務的高可靠性,防止由於其中一臺機器宕機而致使鏈接失敗。第二個參數爲鏈接超時時間,這裏是3秒。第三個參數爲觀察者對象,鏈接成功後會調用觀察者對象中的回調函數,這裏傳入null便可。

String path=zk.create("/zk001", "zk001_data".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);

調用ZooKeeper對象的建立節點函數,返回建立的節點路徑,並須要傳入四個參數。第一個參數爲節點名稱。第二個參數爲節點數據,須要轉成字節數組。第三個參數爲權限控制,這裏使用ZooKeeper自帶的徹底開放權限Ids.OPEN_ACL_UNSAFE。第四個參數爲建立模式,它是一個枚舉類型,共有四個取值,PERSISTENT(持久化,這個目錄節點存儲的數據不會丟失 ,即客戶端失去鏈接以後不會被自動刪除)、PERSISTENT_SEQUENTIAL(順序自動編號的目錄節點,這種目錄節點在命名上會根據當前已經存在的節點數自動加 1,而後將已經成功建立的目錄節點名返回給客戶端)、EPHEMERAL(臨時目錄節點,客戶端斷開鏈接時,這種節點會被自動刪除)、EPHEMERAL_SEQUENTIAL(臨時自動編號目錄節點,客戶端斷開鏈接時,這種節點也會被自動刪除)。

3.運行程序

直接在eclipse中右擊運行該程序便可。

8.3.3 添加數據

咱們能夠經過調用ZooKeeper對象的setData()函數給節點添加數據,示例代碼以下:

@Test  
    public void setNodeData() throws Exception {  
        String connectStr="192.168.170.128:2181,192.168.170.129:2181,192.168.170.130:2181";
        ZooKeeper zk  = new ZooKeeper(connectStr, 3000, null);  
        Stat stat = zk.setData("/zk002", "zk002_data2".getBytes(), -1);  
        System.out.println(stat.getVersion());
    }

代碼解析:

Stat stat = zk.setData("/zk002", "zk002_data2".getBytes(), -1);

setData函數的第一個參數爲節點路徑。第二個參數爲須要添加的數據,並轉成字節數組。第三個參數爲版本號,-1表明全部版本。

8.3.4 獲取數據

咱們能夠調用ZooKeeper對象的getData()函數,得到指定節點的數據,示例代碼以下:

@Test  
    public void getNodeData() throws Exception {  
        String connectStr="192.168.170.128:2181,192.168.170.129:2181,192.168.170.130:2181";
        ZooKeeper zk  = new ZooKeeper(connectStr, 3000, null);  
        Stat stat=new Stat();
    //返回指定路徑上的節點數據和節點狀態,節點的狀態會放入stat對象中
    byte[] bytes=zk.getData("/zk002", null, stat);
    System.out.println(new String(bytes));
    }

上述代碼獲取了節點/zk002的數據,並且轉成了字符串進行了輸出,並將節點/zk002的狀態放入了對象stat中。如需查看狀態信息,能夠從對象stat中進行輸出查看。
能夠看到,在getData函數的第二個參數,傳入的是null,也能夠指定一個觀察者對象watcher,對節點數據的變化進行監聽,一旦有數據改變,就會觸發watcher指定的回調函數。咱們對上方代碼加入觀察者後,示例代碼以下:

@Test  
    public void getNodeDataWatch() throws Exception {  
        String connectStr="192.168.170.128:2181,192.168.170.129:2181,192.168.170.130:2181";
        ZooKeeper zk  = new ZooKeeper(connectStr, 3000, null);  
        Stat stat=new Stat();
        //返回指定路徑上的節點數據和節點狀態,節點的狀態會放入stat對象中
        byte[] bytes=zk.getData("/zk002", new Watcher(){
            @Override
            public void process(WatchedEvent event) {
                System.out.println(event.getType());
            }
            
        }, stat);
        System.out.println(new String(bytes));
        
        //改變節點數據,觸發watch
        zk.setData("/zk002", "zk002_data_testwatch".getBytes(), -1); 
        
        //爲了驗證是否觸發了watch,不讓程序結束
        while(true){
            Thread.sleep(3000);
        }
        
    }

代碼分析:

public void process(WatchedEvent event) {
    System.out.println(event.getType());
}

process 方法是 Watcher 接口中的一個回調方法,當 ZooKeeper 向客戶端發送一個 Watcher 事件通知時,客戶端就會對相應的 process 方法進行回調,從而實現對事件的處理。
process 方法包含 WatcherEvent 類型的參數,WatchedEvent 包含了每個事件的三個基本屬性:通知狀態(KeeperState)、事件類型(EventType)和節點路徑(Path),ZooKeeper 使用 WatchedEvent 對象來封裝服務端事件並傳遞給 Watcher,從而方便回調方法 process 對服務端事件進行處理。
上述代碼經過System.out.println(event.getType());輸出服務端的事件類型,輸出結果爲NodeDataChanged。從結果單詞的含義可知,節點數據被改變了。

while(true){
    Thread.sleep(3000);
}

爲了可以更好的驗證是否觸發了watch,不讓程序一次執行到底,從而加入了上方代碼,讓程序一直停留在此處。

8.3.5 刪除節點

咱們能夠經過調用ZooKeeper對象的delete()函數,對指定路徑節點進行刪除。示例代碼以下:

@Test
    public void deletePath() throws Exception{
        String connectStr="192.168.170.128:2181,192.168.170.129:2181,192.168.170.130:2181";
        ZooKeeper zk  = new ZooKeeper(connectStr, 3000, null);  
        //刪除節點 
        zk.delete("/zk001", -1);
    }

上述代碼中,delete函數須要傳入兩個參數,第一個參數爲須要刪除的節點路徑。第二個參數爲節點版本,若是是-1則表明刪除全部版本。

原創文章,轉載請註明出處!!

相關文章
相關標籤/搜索