博客 / 詳情

返回

ZooKeeper 入門教程

0. 前言

文章已經收錄到 GitHub 個人博客項目,歡迎 Star:

https://github.com/chenyl8848/chenyl8848.github.io

或者訪問網站,進行在線瀏覽:

https://chenyl8848.github.io/

1. ZooKeeper 簡介

ZooKeeper(動物園管理者)簡稱 ZK一個分佈式的,開放源碼的分佈式應用程序協調服務,是 Google 的 Chubby 一個開源的實現,是 Hadoop 和 Hbase 的重要組件。ZooKeeper 使用 Java 所編寫,但是支持 Java 和 C 兩種編程語言。

應用場景

  • 分佈式微服務註冊中心:Dubbo 框架、Spring Cloud 框架
  • 集羣管理:Hadoop Hbase 組件
  • 分佈式鎖
關注微信公眾號:【Java陳序員】,獲取開源項目分享、AI副業分享、超200本經典計算機電子書籍等。

2. ZooKeeper 內存數據模型

2.1 模型結構

2.2 模型的特點

  • 每個子目錄如 /node1 都被稱作一個 znode(節點),這個 znode 是被它所在的路徑唯一標識
  • znode 可以有子節點目錄,並且每個 znode 可以存儲數據
  • znode 是有版本的,每個 znode 中存儲的數據可以有多個版本,也就是一個訪問路徑中可以存儲多份數據
  • znode 可以被監控,包括這個目錄節點中存儲的數據的修改,子節點目錄的變化等,一旦變化可以通知設置監控的客户端

3. 節點的分類

3.1 持久節點(PERSISTENT)

指在節點創建後,就一直存在,直到有刪除操作來主動刪除這個節點 —— 不會因為創建該節點的客户端會話失效而消失

3.2 持久順序節點(PERSISTENT_SEQUENTIAL)

這類節點的基本特性和上面的節點類型是一致的。額外的特性是,在 ZooKeeper 中,每個父節點會為他的第一級子節點維護一份時序,會記錄每個子節點創建的先後順序。基於這個特性,在創建子節點的時候,可以設置這個屬性,那麼在創建節點過程中,ZooKeeper 會自動為給定節點名加上一個數字後綴,作為新的節點名。這個數字後綴的範圍是整型的最大值。

3.3 臨時節點(EPHEMERAL)

和持久節點不同的是,臨時節點的生命週期和客户端會話綁定。也就是説,如果客户端會話失效,那麼這個節點就會自動被清除掉。注意,這裏提到的是會話失效,而非連接斷開。另外,在臨時節點下面不能創建子節點。 

3.4 臨時順序節點(EPHEMERAL_SEQUENTIAL)

具有臨時節點特點,額外的特性是,每個父節點會為他的第一級子節點維護一份時序,這點和持久順序節點類似。


4. 安裝

4.1 Linux 系統安裝

# 1.安裝 JDK 並配置環境變量&下載 ZooKeeper 安裝包
-    https://mirrors.bfsu.edu.cn/apache/zookeeper/zookeeper-3.6.2/apache-zookeeper-3.6.2-bin.tar.gz

# 2.下載安裝包上傳到 Linux 服務器中,並解壓縮
-    tar -zxvf zookeeper-3.4.12.tar.gz

# 3.重命名安裝目錄
-    mv zookeeper-3.4.12 zk

# 4.配置 zoo.cfg 配置文件
-    1.修改 ZooKeeper 的 conf 目錄下的 zoo_simple.cfg,修改完後,重命名為zoo.cfg
  tickTime=2000
  initLimit=10
  syncLimit=5
  dataDir=/usr/zookeeper/zkdata
  clientPort=2181

# 5.啓動 ZooKeeper
-    在 ZooKeeper 的 bin 目錄下,運行 zkServer.sh
    ./bin/zkServer.sh start /usr/zookeeper/conf/zoo.cfg
    
# 6.使用 jps 查看啓動是否成功

# 7.啓動客户端連接到 ZooKeeper
- ./bin/zkCli.sh -server 192.168.0.220:2181
    注意:可以通過  ./bin/zkCli.sh help 查看客户端所有可以執行的指令

4.2 Docker 安裝 ZooKeeper

# 1.獲取 ZooKeeper 的鏡像
- docker pull zookeeper:3.4.14

# 2.啓動 ZooKeeper 服務
- docker run --name zk -p 2181:2181 -d zookeeper:3.4.14

5. 客户端基本指令

# 1.ls path                                查看特定節點下面的子節點

# 2.create path data               創建一個節點。並給節點綁定數據(默認是持久性節點)
  - create path data                                  創建持久節點(默認是持久節點)
  - create -s path data                           創建持久性順序節點
  -    create -e path data                           創建臨時性節點(注意:臨時節點不能含有任何子節點)
  -    create -e -s path data            創建臨時順序節點(注意:臨時節點不能含有任何子節點)
 
# 3.stat path                查看節點狀態
# 4.set path data            修改節點數據
# 5.ls2 path                 查看節點下孩子和當前節點的狀態
# 6.history                  查看操作歷史
# 7.get path                 獲得節點上綁定的數據信息
# 8.delete path              刪除節點(注意:刪除節點不能含有子節點)
# 9.rmr  path                遞歸刪除節點(注意:會將當前節點下所有節點刪除)
# 10.quit                    退出當前會話(會話失效)

6. 節點監聽機制 watch

客户端可以監測 znode 節點的變化。znode 節點的變化會觸發相應的事件,然後清除對該節點的監測。

當監測一個 znode 節點時候,Zookeeper 會發送通知給監測節點。一個 Watch 事件是一個一次性的觸發器,當被設置了 Watch 的數據和目錄發生了改變的時候,則服務器將這個改變發送給設置了 Watch 的客户端以便通知它們

# 1.ls /path true             監聽節點目錄的變化
# 2.get /path true                        監聽節點數據的變化 

7.Java 操作 ZooKeeper

7.1 創建項目引入依賴

<dependency>
    <groupId>com.101tec</groupId>
    <artifactId>zkclient</artifactId>
    <version>0.10</version>
</dependency>

7.2 獲取 ZooKeeper 客户端對象

private ZkClient zkClient;
/**
 * 獲取zk客户端連接
 */
@Before
public void Before() {

    // 參數1:服務器的ip和端口
    // 參數2:會話的超時時間
    // 參數3:回話的連接時間
    // 參數4:序列化方式
    zkClient = new ZkClient("192.168.28.132:2181", 30000, 60000, new SerializableSerializer());
}
/**
 * 關閉資源
 */
@After
public void after(){
    zkClient.close();
}

7.3 常用 API

  • 創建節點
/**
 * 創建節點
 */
@Test
public void testCreateNode() {
    //第一中創建方式  返回創建節點的名稱
    String nodeName = zkClient.create("/node5", "lisi", CreateMode.PERSISTENT);
    zkClient.create("/node6", "zhangsan", CreateMode.PERSISTENT_SEQUENTIAL);
    zkClient.create("/node7", "王五", CreateMode.EPHEMERAL);
    zkClient.create("/node8", "xiaozhang", CreateMode.EPHEMERAL_SEQUENTIAL);
    //第二種創建方式 不會返回創建節點的名稱
    zkClient.createPersistent("/node1", "持久數據");
    zkClient.createPersistentSequential("/node1/aa", "持久數據順序節點");
    zkClient.createEphemeral("/node2", "臨時節點");
    zkClient.createEphemeralSequential("/node1/bb", "臨時順序節點");
}
  • 刪除節點
/**
 * 刪除節點
 */
@Test
public void testDeleteNode() {
    // 刪除沒有子節點的節點  返回值:是否刪除成功
    boolean delete = zkClient.delete("/node1");
    // 遞歸刪除節點信息 返回值:是否刪除成功
    boolean recursive = zkClient.deleteRecursive("/node1");
}
  • 查看節點的子節點
/**
 * 查詢節點
 */
@Test
public void testFindNodes() {
    // 獲取指定路徑的節點信息  
    // 返回值:為當前節點的子節點信息
    List<String> children = zkClient.getChildren("/");
    for (String child : children) {
        System.out.println(child);
    }
}
  • 查看當前節點的數據
注意:如果出現:org.I0Itec.zkclient.exception.ZkMarshallingError: java.io.StreamCorruptedException: invalid stream header: 61616161. 異常的原因是:在 Shell 中的數據序列化方式和 Java 代碼中使用的序列化方式不一致,因此要解決這個問題只需要保證序列化一致即可。
/**
 * 獲取節點的數據
 *
 */
@Test
public void testFindNodeData() {
    Object readData = zkClient.readData("/node3");
    System.out.println(readData);
}
  • 查看當前節點的數據並獲取狀態信息
/**
 * 獲取數據以及當前節點的狀態信息
 */
@Test
public void testFindNodeDataAndStat() {
    Stat stat = new Stat();
    Object readData = zkClient.readData("/node60000000024", stat);
    System.out.println(readData);
    System.out.println(stat);
}
  • 修改節點數據
/**
 * 修改節點數據
 */
@Test
public void testUpdateNodeData() {
    zkClient.writeData("/node60000000024", new User("121", "name", "xxx"));
}
  • 監聽節點數據的變化
/**
 * 監聽節點數據的變化
 */
@Test
public void testOnNodeDataChange() throws IOException {
    zkClient.subscribeDataChanges("/node60000000024", new IZkDataListener() {
        // 當節點的值在修改時,會自動調用這個方法  將當前修改節點的名字,和節點變化之後的數據傳遞給方法
        public void handleDataChange(String nodeName, Object result) throws Exception {
            System.out.println(nodeName);
            System.out.println(result);
        }

        // 當節點的值被刪除的時候,會自動調用這個方法,會將節點的名字已參數形式傳遞給方法
        public void handleDataDeleted(String nodename) throws Exception {
            System.out.println("節點的名字:" + nodename);
        }
    });
    //阻塞客户端
    System.in.read();
}
  • 監聽節點目錄的變化
/**
 * 監聽節點的變化
 */
@Test
public void testOnNodesChange() throws IOException {
    zkClient.subscribeChildChanges("/node60000000024", new IZkChildListener() {
        // 當節點的發生變化時,會自動調用這個方法
        // 參數1:父節點名稱
        // 參數2:父節點中的所有子節點名稱
        public void handleChildChange(String nodeName, List<String> list) throws Exception {
            System.out.println("父節點名稱:" + nodeName);
            System.out.println("發生變更後字節孩子節點名稱:");
            for (String name : list) {
                System.out.println(name);
            }
        }
    });
    // 阻塞客户端
    System.in.read();
}

8. ZooKeeper 的集羣

8.1 集羣(Cluster)

# 1.集羣(Cluster)
- 集合同一種軟件服務的多個節點同時提供服務

# 2.集羣解決問題
-  單節點的併發訪問的壓力問題
-  單節點故障問題(如硬件老化、自然災害等)

8.2 集羣架構

8.3 集羣搭建

# 1.創建三個 dataDir
- mkdir zkdata1 zkdata2 zkdata3

# 2.分別在三個dataDir目錄下面myid文件
- touch ./zkdata1/myid
    myid 的內容是 服務器的  表示  1|2|3

# 3.在 /conf 目錄下創建三個 ZooKeeper 配置文件,分別為 zoo1.cfg、zoo2.cfg、zoo3.cfg    
-    zoo1.cfg
    tickTime=2000
    initLimit=10
    syncLimit=5
    dataDir=/root/zkdata1
    clientPort=3001
    server.1=10.15.0.5:3002:3003
    server.2=10.15.0.5:4002:4003
    server.3=10.15.0.5:5002:5003
- zoo2.cfg
    tickTime=2000
    initLimit=10
    syncLimit=5
    dataDir=/root/zkdata2
    clientPort=4001
    server.1=10.15.0.5:3002:3003
    server.2=10.15.0.5:4002:4003
    server.3=10.15.0.5:5002:5003
- zoo3.cfg
    tickTime=2000
    initLimit=10
    syncLimit=5
    dataDir=/root/zkdata3
    clientPort=5001
    server.1=10.15.0.5:3002:3003
    server.2=10.15.0.5:4002:4003
    server.3=10.15.0.5:5002:5003

解釋:
    1.server.X: x為服務器的唯一標識。
    2.192.168.0.220: 服務器所在的ip地址
    3.3002: 數據同步使用的端口號
    4.3003: 選舉使用的端口號

# 4.分別啓動各個 ZooKeeper 服務器
- ./bin/zkServer.sh start /usr/zookeeper/conf/zoo1.cfg 
- ./bin/zkServer.sh start /usr/zookeeper/conf/zoo2.cfg 
- ./bin/zkServer.sh start /usr/zookeeper/conf/zoo3.cfg

# 5.查看各個 ZooKeeper 服務器的角色信息
- ./bin/zkServer.sh status /usr/zookeeper/conf/zoo1.cfg

# 6.客户端連接任意 ZooKeeper 服務器進行節點操作
- ./bin/zkCli.sh -server 192.168.0.220:3001

# 7.停止特定 ZooKeeper 服務器
- ./bin/zkServer.sh stop /usr/zookeeper/conf/zoo1.cfg 

user avatar tekin_cn 頭像 wenjinhua 頭像 daimaxianyumeng 頭像 tangtaixian_5fc4b5d1c3eff 頭像 zeran 頭像 tnfe 頭像 abai_681266b7f0de8 頭像 codecraft 頭像 ryan_5fd1bc9a06259 頭像 guyu_5d2e806c57ac8 頭像
10 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.