zookeeper

秋涼星轉百鬥xoxo發表於2020-12-18

Zookeeper 實戰

主要內容
Zookeeper 簡介
Zookeeper 儲存結構
監聽通知
安裝 Zookeeper
Zookeeper 常用命令
使用 Java API 操作 Zookeeper
Zookeeper 實戰
學習目標

在這裡插入圖片描述

一、 Zookeeper 簡介

1 什麼是 Zookeeper

Zookeeper 官網: http://zookeeper.apache.org/
Zookeeper 是 Apache 的一個分散式服務框架,是 Apache Hadoop 的一個子專案。官方文件上這麼解釋 Zookeeper,它主要是用來解決分散式應用中經常遇到的一些資料管理問題,
如:統一命名服務、狀態同步服務、叢集管理、分散式應用配置項的管理等。
簡單來說 zookeeper=檔案系統+監聽通知機制。

二、 Zookeeper 儲存結構

在這裡插入圖片描述

1 Znode

在 Zookeeper 中,znode 是一個跟 Unix 檔案系統路徑相似的節點,可以向節點儲存資料或者獲取資料。
Zookeeper 底層是一套資料結構。這個儲存結構是一個樹形結構,其上的每一個節點,我們稱之為“znode”
Zookeeper 中的資料是按照“樹”結構進行儲存的。而且 znode 節點還分為 4 種不同的型別。

每一個 znode 預設能夠儲存 1MB 的資料(對於記錄狀態性質的資料來說,夠了)可以使用 zkCli 命令,登入到 Zookeeper 上,並通過 ls、create、delete、get、set等命令操作這些 znode 節點。

2 Znode 節點型別

2.1 PERSISTENT-持久化目錄節點

客戶端與 zookeeper 斷開連線後,該節點依舊存在。

2.2 PERSISTENT_SEQUENTIAL-持久化順序編號目錄節點

客戶端與 zookeeper 斷開連線後,該節點依舊存在,只是 Zookeeper 給該節點名稱進行順序編號。

2.3 EPHEMERAL-臨時目錄節點

客戶端與 zookeeper 斷開連線後,該節點被刪除。

2.4 EPHEMERAL_SEQUENTIAL-臨時順序編號目錄節點

客戶端與 zookeeper 斷開連線後,該節點被刪除,只是 Zookeeper 給該節點名稱進行順序編號。

三、 監聽通知機制

Zookeeper 是使用觀察者設計模式來設計的。當客戶端註冊監聽它關心的目錄節點時,
當目錄節點發生變化(資料改變、被刪除、子目錄節點增加刪除)時,Zookeeper 會通知客戶端。

四、 安裝 zookeeper

官方資源包可在 zookeeper.apache.com 站點中下載。最新發布版本為:3.6.0。

安裝很簡單,解壓就可以用,但需要配置

1 安裝單機版

1.1 安裝 Linux

1.2 安裝 JDK

配置環境變數

export JAVA_HOME=/usr/local/jdk
export CLASSPATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar
export PATH=$JAVA_HOME/bin:$PATH

1.3 上傳 Zookeeper

在這裡插入圖片描述

在這裡插入圖片描述

1.4解壓 Zookeeper 壓縮包

在這裡插入圖片描述

拷貝到/usr/local/ 目錄下

在這裡插入圖片描述

在這裡插入圖片描述

1.5Zookeeper 目錄結構

logs目錄是啟動之後才會有的,記錄日誌

在這裡插入圖片描述

1. bin:放置執行指令碼和工具指令碼,
2. conf:zookeeper 預設讀取配置的目錄,裡面會有預設的配置檔案
3. docs:zookeeper 相關的文件
4. lib:zookeeper 核心的 jar
5. logs:zookeeper 日誌

1.6配置 Zookeeper

建立zookeeper執行時資料快取目錄

在這裡插入圖片描述

Zookeeper 在啟動時預設的去 conf 目錄下查詢一個名稱為 zoo.cfg 的配置檔案。
在 zookeeper 應用目錄中有子目錄 conf。其中有配置檔案模板:zoo_sample.cfg
cp zoo_sample.cfg zoo.cfg==(將 zoo_sample.cfg檔案拷貝到當前目錄下改名為zoo.cfg 因為zoo.cfg 是zookeeper預設去找的配置檔案)==。zookeeper 應用中的配置檔案為 conf/zoo.cfg。

在這裡插入圖片描述

修改 zoo.cfg配置檔案

clientPort:監聽客戶端連線的埠號預設為2181

dataDir : 設定zookeeper執行時資料快取目錄位置

在這裡插入圖片描述

1.7啟動 Zookeeper

在這裡插入圖片描述

預設載入配置檔案:./zkServer.sh start:預設的會去 conf 目錄下載入 zoo.cfg 配置檔案。

指定載入配置檔案:./zkServer.sh start 配置檔案的路徑。

啟動成功

在這裡插入圖片描述

檢視zookeeper當前狀態 zkServer.sh status

在這裡插入圖片描述

1.8停止 Zookeeper

./zkServer.sh stop

在這裡插入圖片描述

1.9檢視 Zookeeper 狀態

./zkServer.sh status

在這裡插入圖片描述

關於執行BUG:

在這裡插入圖片描述

zookeeper啟動之後會生成logs目錄用於記錄日誌

在這裡插入圖片描述

在這裡插入圖片描述

1.10使用客戶端連線單機版 Zookeeper

1.10.1 連線方式一

bin/zkCli.sh
預設連線地址為本機地址,預設連線埠為 2181

在這裡插入圖片描述

拒絕連線的原因是zookeeper還沒有啟動

在這裡插入圖片描述

啟動zookeeper

在這裡插入圖片描述

連線成功

在這裡插入圖片描述

現在在zookeeper下有一個結點

在這裡插入圖片描述

按Ctrl+C 退出連線

在這裡插入圖片描述

1.10.2 連線方式二

bin/zkCli.sh -server ip:port
連線指定 IP 地址與埠

eg:

在這裡插入圖片描述

連線成功

在這裡插入圖片描述

2 安裝叢集版

2.1Zookeeper 叢集說明

2.1.1 Zookeeper 叢集中的角色

Zookeeper 叢集中的角色主要有以下三類

在建立zookeeper叢集之前所有的結點都是跟隨者,但是一旦通過內部的投票選舉演算法選出一個領導者之後,除了領導者之外每一個跟隨者都是一個zookeeper的結點,當跟隨者結點的資料發生變化時會通知領導者,領導者再去通知其他的跟隨者,從發生資料變化的結點中做資料同步

在這裡插入圖片描述

2.2叢集安裝

使用 3 個 Zookeeper 應用搭建一個偽叢集(在一個Linux系統中搭建叢集)。應用部署位置是:192.168.88.101。

客戶端監聽埠分別為:2181、2182、2183。投票選舉埠分別為 2881/3881、2882/3882、2883/3883。

投票和選舉用的是兩個埠

建立存放zookeeper叢集例項的目錄

在這裡插入圖片描述

tar -zxf zookeeper-3.6.0.tar.gz
將解壓後的 Zookeeper 應用目錄重新命名,便於管理
mv zookeeper-3.6.0 zookeeper01

在這裡插入圖片描述

2.2.1 提供資料快取目錄

在 zookeeper01 應用目錄中,建立 data 目錄,用於快取應用執行資料
cd zookeeper01
mkdir data

在這裡插入圖片描述

2.2.2 複製應用

複製兩份 Zookeeper 應用。用於模擬叢集中的 3 個節點。此時複製的Zookeeper應用帶data目錄
cp -r zookeeper01 zookeeper02
cp -r zookeeper01 zookeeper03

現在相當於安裝好了三個Zookeeper例項了

在這裡插入圖片描述

2.2.3 提供配置檔案

在 zookeeper 應用目錄中有子目錄 conf。其中有配置檔案模板:zoo_sample.cfg
cp zoo_sample.cfg zoo.cfg
zookeeper 應用中的配置檔案為 conf/zoo.cfg。

在這裡插入圖片描述

在這裡插入圖片描述

在這裡插入圖片描述

2.2.4 修改配置檔案 zoo.cfg - 設定資料快取路徑

dataDir 引數值為應用執行快取資料儲存目錄。

Zookeeper01

在這裡插入圖片描述

Zookeeper02

在這裡插入圖片描述

Zookeeper03

在這裡插入圖片描述

2.2.5 提供應用唯一標識

***在 Zookeeper 叢集中,每個節點需要一個唯一標識。***這個唯一標識要求是自然數。且唯一標識儲存位置是:資料快取目錄(dataDir=/usr/local/zookeeper/data)的 myid (data目錄中沒有需要自己建立,且必須叫myid)檔案中。其中“資料快取目錄”為配置檔案 zoo.cfg 中的配置引數

在 data 目錄中建立檔案 myid : touch myid
為應用提供唯一標識。本環境中使用 1、2、3 作為每個節點的唯一標識。
vi myid

簡化方式為: echo [唯一標識] >> myid。

echo 命令為回聲命令,系統會將命令傳送的資料返回。 '>>'為定位,代表系統回聲資料指定傳送到什麼位置。 此命令代表系統回聲資料傳送到 myid 檔案中。 如果沒有檔案則建立檔案。

自動幫助建立myid檔案

在這裡插入圖片描述

繼續標識Zookeeper02和Zookeeper03

在這裡插入圖片描述

2.2.6 修改配置檔案 zoo.cfg - 設定監聽客戶端、投票、選舉埠

vim zoo.cfg

server.1標識定位Zookeeper01這個例項 server.X X的值取決於例項中myid檔案裡面的值

192.168.88.101 是例項所在主機的IP地址 後面跟的是投票和選舉埠

clientPort=2181 #服務埠根據應用做對應修改,zk01-2181,zk02-2182,zk03-2183
server.1=192.168.88.101:2881:3881
server.2=192.168.88.101:2882:3882
server.3=192.168.88.101:2883:3883

修改三個Zookeeper例項的配置檔案

在這裡插入圖片描述

Zookeeper01 客戶端監聽埠為 2181

在這裡插入圖片描述

Zookeeper02 客戶端監聽埠改為 2182

在這裡插入圖片描述

Zookeeper03 客戶端監聽埠改為 2183

在這裡插入圖片描述

在這裡插入圖片描述

測試啟動三個Zookeeper例項 Zookeeper會自動建立叢集

在這裡插入圖片描述

叢集建立成功 檢視各個結點的狀態,領導者和跟隨者 觀察者會在兩個跟隨者之間選一個,看不見

在這裡插入圖片描述

2.3編寫啟動、關閉叢集指令碼

在 Linux 中可以使用 chmod 命令為檔案授權。
chmod 777 檔名
777 表示為檔案分配可讀,可寫,可執行許可權。

2.3.1 啟動 Zookeeper 叢集指令碼
zookeeper01/bin/zkServer.sh start
zookeeper02/bin/zkServer.sh start
zookeeper03/bin/zkServer.sh start

在這裡插入圖片描述

在這裡插入圖片描述

給startall.sh檔案分配可執行許可權

可讀可寫可執行

在這裡插入圖片描述

執行這個集中啟動的檔案 三個Zookeeper例項都執行了

在這裡插入圖片描述

2.3.2 關閉 Zookeeper 叢集指令碼
zookeeper01/bin/zkServer.sh stop
zookeeper02/bin/zkServer.sh stop
zookeeper03/bin/zkServer.sh stop

建立集中關閉的檔案 shutdownall.sh

在這裡插入圖片描述

在這裡插入圖片描述

分配可讀可寫可執行許可權

在這裡插入圖片描述

執行集中關閉的可執行檔案 三個Zookeeper例項都被關閉了

在這裡插入圖片描述

2.4連線叢集

可以使用任何節點中的客戶端工具連線叢集中的任何節點。
./zkCli.sh -server 192.168.88.101:2183

客戶端工具是誰無所謂,不管在不在叢集裡面都可以連線叢集

先啟動再連線,用Zookeeper01連線Zookeeper03

啟動

在這裡插入圖片描述

連線

在這裡插入圖片描述

連線成功

在這裡插入圖片描述

用單機Zookeeper連線叢集Zookeeper中的結點

在這裡插入圖片描述

在這裡插入圖片描述

五、Zookeeper 常用命令

1 ls 命令

ls /path
使用 ls 命令檢視 zookeeper 中的內容。在 ZooKeeper 控制檯客戶端中,沒有預設列表功能,

必須指定要列表資源的位置。 如: ls / 或者 ls /path

必須給個路徑

在這裡插入圖片描述

檢視根下 根下有個Zookeeper的結點

在這裡插入圖片描述

檢視Zookeeper下有哪些結點

在這裡插入圖片描述

檢視config下有哪些結點 config下沒有任何結點了

在這裡插入圖片描述

2 create 命令

create [-e] [-s] /path [data]

path:指定在哪個位置建立結點 data:建立結點時新增的資料

使用 create 命令建立一個新的 Znode。create [-e] [-s] path data - 建立節點,

如: create /test 123 建立一個/test 節點(不加任何引數預設為持久型別的結點在客戶端斷開連線時不會被刪除),節點攜帶資料資訊 123。

create -e /test 123 建立一個臨時節點/test,攜帶資料為 123,

臨時節點只在當前會話生命週期中有效,會話結束節點自動刪除。

create -s /test 123 建立一個順序節點/test,攜帶資料 123,

建立的順序節點由 ZooKeeper 自動為節點增加字尾資訊,如-/test00000001 等。-e 和-s 引數可以聯合使用。

在根下建立結點bjsxt

在這裡插入圖片描述

先退出連結再連結,bjsxt這個結點還在 不給任何引數預設為持久型別的

在這裡插入圖片描述

-s 表示建立一個帶序號的持久化結點 結點的值為123 0000000001為結點的序號

在這裡插入圖片描述

-e 表示建立一個臨時結點 不給值

在這裡插入圖片描述

臨時結點退出客戶端連線之後就會被刪除,不是立即刪除需要等待幾秒鐘 temp結點已被刪除

在這裡插入圖片描述

建立一個臨時的帶序號的結點

在這裡插入圖片描述

退出重新連線客戶端之後臨時結點被刪除

在這裡插入圖片描述

3 get 命令

get [-s] /path

get 命令獲取 Znode 中的資料。

在這裡插入圖片描述

get -s /path
-s 檢視 Znode 詳細資訊

在這裡插入圖片描述

lzc:存放的資料
cZxid:建立時 zxid(znode 每次改變時遞增的事務 id)
ctime:建立時間戳
mZxid:最近一次更近的 zxid
mtime:最近一次更新的時間戳
pZxid:子節點的 zxid
cversion:子節點更新次數
dataversion:節點資料更新次數
aclVersion:節點 ACL(授權資訊)的更新次數
ephemeralOwner:如果該節點為 ephemeral 節點(臨時,生命週期與 session 一樣),
ephemeralOwner 值表示與該節點繫結的 session id. 如果該節點不是
ephemeral 節點, ephemeralOwner 值為 0.

dataLength:節點資料位元組數
numChildren:子節點數量

4 set 命令

set /path [data]
新增或修改 Znode 中的值

修改結點bjsxt的值為AAA

在這裡插入圖片描述

建立一個空結點,然後通過set命令給結點賦值

在這裡插入圖片描述

5 delete 命令

delete /path
刪除 Znode。

test結點已被刪除

在這裡插入圖片描述

在這裡插入圖片描述

六、 使用 Java API 操作 Zookeeper

1 建立 Znode

1.1建立專案

在這裡插入圖片描述

在這裡插入圖片描述

在這裡插入圖片描述

1.2修改 POM 檔案新增依賴

該依賴為基於 Java 語言連線 Zookeeper 的客戶端工具

依賴版本應和按照的Zookeeper版本保持一致

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

1.3建立 Znode 並新增資料

前四個常量含義依次為:

建立許可權

對當前操作開放所有許可權

讀取許可權

只對當前客戶端開放所有許可權

在這裡插入圖片描述

package com.bjsxt;

import org.apache.zookeeper.*;

import java.io.IOException;

/**
 * 操作Zookeeper的Znode
 */
public class ZnodeDemo implements Watcher {

    public static void main(String[] args) throws KeeperException, InterruptedException, IOException {
        //建立連線Zookeeper物件   這個物件是有新增的依賴jar包提供的
        /**
         * param1:給定當前去連結Zookeeper的IP地址和埠,連線Zookeeper叢集則用逗號分隔
         * param2:客戶端工具在連結Zookeeper的超時時間
         * param3:Zookeeper提供的做事件通知的處理器,對Zookeeper做事件操作Zookeeper會把事件傳遞到程式碼中,需要實現Watcher介面
         * 在Watcher介面下有process()方法,採用回撥的方式將事件行為通知給程式碼
         */
        ZooKeeper zooKeeper = new ZooKeeper("192.168.88.101:2181,192.168.88.101:2182,192.168.88.101:2183", 150000, new ZnodeDemo());
        //建立一個Znode     create方法的返回值是建立Znode的路徑
        /**
         * param1:建立Znode的位置
         * param2:為Znode新增的值,必須是位元組陣列型別
         * param3:許可權設定
         * param4:建立Znode的型別(4種型別)
         */
        String path = zooKeeper.create("/bjsxt/test", "lzc".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT_SEQUENTIAL);
        System.out.println(path);
    }

    /**
     * 事件通知回撥方法
     * @param watchedEvent
     * 一旦有時間產生Zookeeper會呼叫process方法通知
     */
    @Override
    public void process(WatchedEvent watchedEvent) {
        //獲取連結事件
        if (watchedEvent.getState() == Event.KeeperState.SyncConnected) {
            System.out.println("連線成功!");
        }
    }
}

建立成功 在bjsxt結點下建立有序子節點test

在這裡插入圖片描述

在這裡插入圖片描述

2 獲取 Znode 中的資料

2.1獲取指定節點中的資料

在這裡插入圖片描述

package com.bjsxt;

import org.apache.zookeeper.*;
import org.apache.zookeeper.data.Stat;

import java.io.IOException;

/**
 * 操作Zookeeper的Znode
 */
public class ZnodeDemo implements Watcher {

    public static void main(String[] args) throws KeeperException, InterruptedException, IOException {
        //建立連線Zookeeper物件   這個物件是有新增的依賴jar包提供的
        /**
         * param1:給定當前去連結Zookeeper的IP地址和埠,連線Zookeeper叢集則用逗號分隔
         * param2:客戶端工具在連結Zookeeper的超時時間
         * param3:Zookeeper提供的做事件通知的處理器,對Zookeeper做事件操作Zookeeper會把事件傳遞到程式碼中,需要實現Watcher介面
         * 在Watcher介面下有process()方法,採用回撥的方式將事件行為通知給程式碼
         */
        ZooKeeper zooKeeper = new ZooKeeper("192.168.88.101:2181,192.168.88.101:2182,192.168.88.101:2183", 150000, new ZnodeDemo());
        //建立一個Znode     create方法的返回值是建立Znode的路徑
        /**
         * param1:建立Znode的位置
         * param2:為Znode新增的值,必須是位元組陣列型別
         * param3:許可權設定
         * param4:建立Znode的型別(4種型別)
         */
        /*String path = zooKeeper.create("/bjsxt/test", "lzc".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT_SEQUENTIAL);
        System.out.println(path);*/

        //獲取指定結點的資料     返回一個byte[]型別的位元組陣列
        /**
         * param1:獲取資料的結點路徑
         * param2:回撥處理器
         * param3:能夠返回節點的餓統計資訊
         */
        byte[] data = zooKeeper.getData("/bjsxt/test0000000000", new ZnodeDemo(), new Stat());
        //得先將位元組陣列轉換成String型別做字串轉換才能列印
        System.out.println(new String(data));
    }

    /**
     * 事件通知回撥方法
     * @param watchedEvent
     * 一旦有時間產生Zookeeper會呼叫process方法通知
     */
    @Override
    public void process(WatchedEvent watchedEvent) {
        //獲取連結事件
        if (watchedEvent.getState() == Event.KeeperState.SyncConnected) {
            System.out.println("連線成功!");
        }
    }
}

在這裡插入圖片描述

2.2獲取所有子節點中的資料

//獲取指定結點中的所有子節點中的資料     返回結果為list   list中存放的是根據當前結點下所有子節點的路徑
    /**
     * param1:取哪個結點的子節點的路徑
     * param2:回撥處理器
     */
    List<String> list = zooKeeper.getChildren("/bjsxt", new ZnodeDemo());
    //遍歷子節點路徑
    for (String path : list) {
        //取某個結點的值得給完整的路徑所以得拼接上跟路徑   stat也可以不給,給個null就行
        byte[] data = zooKeeper.getData("/bjsxt/" + path, new ZnodeDemo(), null);
        System.out.println(new String(data));
    }
}

bjsxt下只有一個子節點

在這裡插入圖片描述

在這裡插入圖片描述

3 設定 Znode 中的值

/**
 * param1:給定需要設定值的結點的路徑
 * param2:需要設定的值    需要一個byte[]型別陣列
 * param3:版本匹配      給 -1 表示忽略版本匹配任何版本都可以
 */
Stat stat = zooKeeper.setData("/bjsxt/test0000000000", "bjsxt".getBytes(), -1);
System.out.println(stat);

在這裡插入圖片描述

資料發生改變

在這裡插入圖片描述

4 刪除 Znode

//刪除Znode中的值
/**
 * param1:需要刪除結點的路徑
 * param2:版本匹配      給 -1 表示忽略版本匹配任何版本都可以
 */
zooKeeper.delete("/bjsxt/test0000000000",-1);

在這裡插入圖片描述

刪除成功

在這裡插入圖片描述

package com.bjsxt;

import org.apache.zookeeper.*;
import org.apache.zookeeper.data.Stat;

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

/**
 * 操作Zookeeper的Znode
 */
public class ZnodeDemo implements Watcher {

    public static void main(String[] args) throws KeeperException, InterruptedException, IOException {
        //建立連線Zookeeper物件   這個物件是有新增的依賴jar包提供的
        /**
         * param1:給定當前去連結Zookeeper的IP地址和埠,連線Zookeeper叢集則用逗號分隔
         * param2:客戶端工具在連結Zookeeper的超時時間
         * param3:Zookeeper提供的做事件通知的處理器,對Zookeeper做事件操作Zookeeper會把事件傳遞到程式碼中,需要實現Watcher介面
         * 在Watcher介面下有process()方法,採用回撥的方式將事件行為通知給程式碼
         */
        ZooKeeper zooKeeper = new ZooKeeper("192.168.88.101:2181,192.168.88.101:2182,192.168.88.101:2183", 150000, new ZnodeDemo());
        //建立一個Znode     create方法的返回值是建立Znode的路徑
        /**
         * param1:建立Znode的位置
         * param2:為Znode新增的值,必須是位元組陣列型別
         * param3:許可權設定
         * param4:建立Znode的型別(4種型別)
         */
        /*String path = zooKeeper.create("/bjsxt/test", "lzc".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT_SEQUENTIAL);
        System.out.println(path);*/

        //獲取指定結點的資料     返回一個byte[]型別的位元組陣列
        /**
         * param1:獲取資料的結點路徑
         * param2:回撥處理器
         * param3:能夠返回節點的餓統計資訊
         */
        /*byte[] data = zooKeeper.getData("/bjsxt/test0000000000", new ZnodeDemo(), new Stat());
        //得先將位元組陣列轉換成String型別做字串轉換才能列印
        System.out.println(new String(data));*/

        //獲取指定結點中的所有子節點中的資料     返回結果為list   list中存放的是根據當前結點下所有子節點的路徑
        /**
         * param1:取哪個結點的子節點的路徑
         * param2:回撥處理器
         */
        /*List<String> list = zooKeeper.getChildren("/bjsxt", new ZnodeDemo());
        //遍歷子節點路徑
        for (String path : list) {
            //取某個結點的值得給完整的路徑所以得拼接上跟路徑   stat也可以不給,給個null就行
            byte[] data = zooKeeper.getData("/bjsxt/" + path, new ZnodeDemo(), null);
            System.out.println(new String(data));
        }*/

        //設定Znode中的值    返回stat型別是當前結點詳細資訊的物件
        /**
         * param1:給定需要設定值的結點的路徑
         * param2:需要設定的值    需要一個byte[]型別陣列
         * param3:版本匹配      給 -1 表示忽略版本匹配任何版本都可以
         */
        /*Stat stat = zooKeeper.setData("/bjsxt/test0000000000", "bjsxt".getBytes(), -1);
        System.out.println(stat);*/

        //刪除Znode中的值
        /**
         * param1:需要刪除結點的路徑
         * param2:版本匹配      給 -1 表示忽略版本匹配任何版本都可以
         */
        zooKeeper.delete("/bjsxt/test0000000000",-1);

    }

    /**
     * 事件通知回撥方法
     * @param watchedEvent
     * 一旦有時間產生Zookeeper會呼叫process方法通知
     */
    @Override
    public void process(WatchedEvent watchedEvent) {
        //獲取連結事件
        if (watchedEvent.getState() == Event.KeeperState.SyncConnected) {
            System.out.println("連線成功!");
        }
    }
}

七、 Zookeeper 實戰

實戰案例介紹:使用 Zookeeper 與 RMI 技術實現一個 RPC 框架。
RPC:RPC(Remote Procedure Call)遠端過程呼叫。

1 基於 RMI 實現遠端方法呼叫

1.1RMI 簡 介

RMI(Remote Method Invocation) 遠端方法呼叫。
RMI 是從 JDK1.2 推出的功能,它可以實現在一個 Java 應用中可以像呼叫本地方法一樣 呼叫另一個伺服器中 Java 應用(JVM)中的內容。
RMI 是 Java 語言的遠端呼叫,無法實現跨語言。

1.2執行流程

Server:被呼叫者

Client:呼叫者

Registry:登錄檔

Server端允許外界應用通過Server端物件呼叫Server端的方法(物件的暴露),如果不暴露這個物件只能在當前Server端使用,暴露需要使用bind()或者rebind()方法把這個物件繫結到一個登錄檔中,登錄檔有Server端提供,登錄檔中放的是所有暴露的物件,Client如果想通過Server中的物件呼叫某一個方法Client需要拿到Server端的登錄檔,再通過lookup()方法從登錄檔中找到需要的物件,然後Client拿著找到的物件呼叫Server下的方法

在這裡插入圖片描述

Registry(登錄檔)是放置所有伺服器物件的名稱空間。 每次服務端建立一個物件時,它都會使用 bind()或 rebind()方法註冊該物件。 這些是使用稱為繫結名稱的唯一名稱註冊的。
**要呼叫遠端物件,客戶端需要該物件的引用。**即通過服務端繫結的名稱從登錄檔中獲取物件(lookup()方法)。

1.3RMI 的 API 介紹

1.3.1 Remote 介面

java.rmi.Remote 定義了此介面為遠端呼叫介面。如果介面被外部呼叫,需要繼承此介面。

在這裡插入圖片描述

1.3.2 RemoteException 類

java.rmi.RemoteException
繼承了 Remote 介面的介面,如果方法是允許被遠端呼叫的,需要丟擲此異常。

1.3.3 UnicastRemoteObject 類

java.rmi.server.UnicastRemoteObject
此類實現了 Remote 介面和 Serializable 介面。
自定義介面實現類除了實現自定義介面還需要繼承此類。

1.3.4 LocateRegistry 類

java.rmi.registry.LocateRegistry
可以通過 LocateRegistry 在本機上建立 Registry,通過特定的埠就可以訪問這個Registry。

1.3.5 Naming 類

java.rmi.Naming
Naming 定義了釋出內容可訪問 RMI 名稱。也是通過 Naming 獲取到指定的遠端方法。

1.4建立 Server 端

1.4.1 建立專案

建立空專案

在這裡插入圖片描述

在這裡插入圖片描述

新增模組

在這裡插入圖片描述

建立maven型別工程

在這裡插入圖片描述

在這裡插入圖片描述

在這裡插入圖片描述

1.4.2 建立介面
package com.bjsxt.service;

import java.rmi.Remote;
import java.rmi.RemoteException;

/**
 * 允許遠端呼叫介面,該介面必須要實現Remote介面
 * 允許被遠端呼叫的方法必須要丟擲RemoteException異常
 */
public interface DemoService extends Remote {

    String demo(String str) throws RemoteException;

}

在這裡插入圖片描述

1.4.3 建立介面實現類

需要將構造方法的修飾型別由 protected改成public

在這裡插入圖片描述

在這裡插入圖片描述

package com.bjsxt.service.impl;

import com.bjsxt.service.DemoService;

import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;

/**
 * 介面實現類必須要繼承UnicastRemoteObject
 * 會自動新增構造方法,需要修改為public
 */
public class DemoServiceImpl extends UnicastRemoteObject implements DemoService {
    //UnicastRemoteObject下有三個構造方法保留一個即可
    public DemoServiceImpl() throws RemoteException {
    }

    @Override
    public String demo(String str) throws RemoteException {
        return "Hello RMI"+str;
    }
}
1.4.4 編寫主方法
package com.bjsxt;

import com.bjsxt.service.DemoService;
import com.bjsxt.service.impl.DemoServiceImpl;

import java.net.MalformedURLException;
import java.rmi.AlreadyBoundException;
import java.rmi.Naming;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;

public class DemoServer {
    public static void main(String[] args) throws RemoteException, AlreadyBoundException, MalformedURLException {
        //將物件例項化
        DemoService demoService = new DemoServiceImpl();
        //建立本地登錄檔       指定訪問該登錄檔的埠號為8888
        LocateRegistry.createRegistry(8888);
        //將物件繫結到登錄檔上    rmi:表示未來查詢物件是通過rmi協議查詢  localhost:當前server所在主機IP
        //第一個demoService是作為被繫結物件的唯一標識,一般用被繫結物件名稱
        Naming.bind("rmi://localhost:8888/demoService",demoService);
    }
}

啟動之後程式處於阻塞狀態,說明createRegistry在監聽8888埠,等著外界呼叫demoServer物件

在這裡插入圖片描述

1.5建立 Client 端

1.5.1 建立專案

在這裡插入圖片描述

在這裡插入圖片描述

在這裡插入圖片描述

1.5.2 複製服務端介面
package com.bjsxt.service;

import java.rmi.Remote;
import java.rmi.RemoteException;

/**
 * 允許遠端呼叫介面,該介面必須要實現Remote介面
 * 允許被遠端呼叫的方法必須要丟擲RemoteException異常
 */
public interface DemoService extends Remote {

    String demo(String str) throws RemoteException;

}
1.5.3 建立主方法
package com.bjsxt;

import com.bjsxt.service.DemoService;

import java.net.MalformedURLException;
import java.rmi.Naming;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;

public class ClientDemo {
    public static void main(String[] args) throws RemoteException, NotBoundException, MalformedURLException {
        DemoService demoService = (DemoService) Naming.lookup("rmi://localhost:8888/demoService");
        String result = demoService.demo("北京尚學堂");
        System.out.println(result);
    }
}

在這裡插入圖片描述

2 使用 Zookeeper 作為註冊中心實現 RPC

以Zookeeper作為註冊中心,rmiServer在啟動時除了要把地址註冊到本地登錄檔中還要把地址放到Zookeeper的一個Znode裡面儲存,rmiClient在啟動時去連結Zookeeper找到存放地址的結點通過getDate()方法獲取地址的值,然後將獲取的地址交給lookup()方法,lookup()再去連結本地登錄檔拿到物件然後通過物件呼叫rmiServer裡面的方法

2.1建立服務端

2.1.1 建立專案

在這裡插入圖片描述

在這裡插入圖片描述

2.1.2 修改 POM 檔案新增依賴
<dependency>
    <groupId>org.apache.zookeeper</groupId>
    <artifactId>zookeeper</artifactId>
    <version>3.6.0</version>
</dependency>
2.1.3 建立介面
package com.bjsxt.service;

import java.rmi.Remote;
import java.rmi.RemoteException;

public interface UsersService extends Remote {

    String findUsers(String str) throws RemoteException;

}
2.1.4 建立介面實現類
package com.bjsxt.service.impl;

import com.bjsxt.service.UsersService;

import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;

public class UsersServiceImpl extends UnicastRemoteObject implements UsersService {

    //只保留一個無引數的構造方法即可    將protect改為public
    public UsersServiceImpl() throws RemoteException {

    }

    @Override
    public String findUsers(String str) throws RemoteException {
        return "Hello Zookeeper " + str;
    }
}
2.1.5 編寫主方法
package com.bjsxt;

import com.bjsxt.service.UsersService;
import com.bjsxt.service.impl.UsersServiceImpl;
import org.apache.zookeeper.*;

import java.io.IOException;
import java.net.MalformedURLException;
import java.rmi.AlreadyBoundException;
import java.rmi.Naming;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;

public class serverDemo implements Watcher {
    public static void main(String[] args) throws IOException, AlreadyBoundException, KeeperException, InterruptedException {
        //例項化對外暴露的物件
        UsersService usersService = new UsersServiceImpl();
        //將物件繫結到本地的登錄檔中     建立一個本地的登錄檔監聽8888埠
        LocateRegistry.createRegistry(8888);
        String url = "rmi://localhost:8888/user";
        //url為繫結標識  完成繫結
        Naming.bind(url, usersService);
        //將url放到Zookeeper的結點中
        ZooKeeper zooKeeper = new ZooKeeper("192.168.88.101:2181,192.168.88.101:2182,192.168.88.101:2183",150000,new serverDemo());
        //建立結點      並且將url放到Znode中
        zooKeeper.create("/bjsxt/service", url.getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
        System.out.println("服務釋出成功!!!");
    }

    @Override
    public void process(WatchedEvent watchedEvent) {
        if (watchedEvent.getState() == Event.KeeperState.SyncConnected) {
            System.out.println("連結成功!!!");
        }
    }
}

在這裡插入圖片描述

在這裡插入圖片描述

2.2建立客戶端

2.2.1 建立專案

在這裡插入圖片描述

在這裡插入圖片描述

2.2.2 修改 POM 檔案新增依賴
<dependency>
    <groupId>org.apache.zookeeper</groupId>
    <artifactId>zookeeper</artifactId>
    <version>3.6.0</version>
</dependency>
2.2.3 建立介面
package com.bjsxt.service;

import java.rmi.Remote;
import java.rmi.RemoteException;

public interface UsersService extends Remote {
    String findUsers(String str) throws RemoteException;
}
2.2.4 編寫主方法
package com.bjsxt;

import com.bjsxt.service.UsersService;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooKeeper;

import java.io.IOException;
import java.rmi.Naming;
import java.rmi.NotBoundException;

public class ClientDemo implements Watcher {
    public static void main(String[] args) throws IOException, KeeperException, InterruptedException, NotBoundException {
        //連結Zookeeper
        ZooKeeper zooKeeper = new ZooKeeper("192.168.88.101:2181,192.168.88.101:2182,192.168.88.101:2183", 150000, new ClientDemo());
        //將Zookeeper當成註冊中心從來裡面取出來的
        byte[] bytes = zooKeeper.getData("/bjsxt/service", new ClientDemo(), null);
        //將位元組陣列轉換成String型別
        String url = new String(bytes);
        System.out.println(url);
        //找物件
        UsersService usersService = (UsersService) Naming.lookup(url);
        //通過拿到的物件呼叫方法
        String result = usersService.findUsers("百戰程式設計師   Zookeeper作為註冊中心");
        System.out.println(result);
    }

    @Override
    public void process(WatchedEvent watchedEvent) {
        if (watchedEvent.getState() == Event.KeeperState.SyncConnected) {
            System.out.println("連結成功!!!");
        }
    }
}

在這裡插入圖片描述

相關文章