zookeeper基礎教程


一、關於zookeeper

Zookeeper 作為一個分布式的服務框架,主要用來解決分布式集群中應用系統的一致性問題,它能提供基於類似於文件系統的目錄節點樹方式的數據存儲, Zookeeper 作用主要是用來維護和監控存儲的數據的狀態變化,通過監控這些數據狀態的變化,從而達到基於數據的集群管理。

簡單的說,zookeeper=文件系統+通知機制。

1. ZNode節點

ZNode被分為持久(persistent)節點,順序(sequential)節點和臨時(ephemeral)節點。

  • 持久節點:即使在創建該特定znode的客戶端斷開連接后,持久節點仍然存在。默認情況下,除非另有說明,否則所有znode都是持久的。
  • 順序節點:順序節點可以是持久的或臨時的。當一個新的znode被創建為一個順序節點時,ZooKeeper通過將10位的序列號附加到原始名稱來設置znode的路徑。例如,如果將具有路徑 /test 的znode創建為順序節點,則ZooKeeper會將路徑更改為 /test0000000001 ,並將下一個序列號設置為0000000002。如果兩個順序節點是同時創建的,那么ZooKeeper不會對每個znode使用相同的數字。順序節點在鎖定和同步中起重要作用。
  • 臨時節點:客戶端活躍時,臨時節點就是有效的。當客戶端與ZooKeeper集合斷開連接時,臨時節點會自動刪除。因此,只有臨時節點不允許有子節點。如果臨時節點被刪除,則下一個合適的節點將填充其位置。臨時節點在leader選舉中起着重要作用。

2. Session回話

會話對於ZooKeeper的操作非常重要。會話中的請求按FIFO順序執行。一旦客戶端連接到服務器,將建立會話並向客戶端分配會話ID 

客戶端以特定的時間間隔發送心跳以保持會話有效。如果ZooKeeper集合在超過服務器開啟時指定的期間(會話超時)都沒有從客戶端接收到心跳,則它會判定客戶端死機。

會話超時通常以毫秒為單位。當會話由於任何原因結束時,在該會話期間創建的臨時節點也會被刪除。

3.Watcher監視

監視是一種簡單的機制,使客戶端收到關於ZooKeeper集合中的更改的通知。客戶端可以在讀取特定znode時設置Watches。Watches會向注冊的客戶端發送任何znode(客戶端注冊表)更改的通知。

Znode更改是與znode相關的數據的修改或znode的子項中的更改。只觸發一次watches。如果客戶端想要再次通知,則必須通過另一個讀取操作來完成。當連接會話過期時,客戶端將與服務器斷開連接,相關的watches也將被刪除。

二、Zookeeper客戶端命令

執行bin/zkCli.sh連接zookeeper服務器后,我們隨便敲一個aa然后回車,可以看到支持如下命令:

stat path [watch] --- 查看節點狀態
set path data [version]  --- 設置節點數據 ls path [watch]  --- 列出子節點
delquota [-n|-b] path --- 刪除節點配額
ls2 path [watch]  --- 列出子節點,並顯示當前節點信息
setAcl path acl  --- 設置節點權限
setquota -n|-b val path  --- 設置節點配額
history  --- 列出歷史命令
redo cmdno  --- 執行歷史命令 
printwatches on|off
delete path [version] --- 刪除節點,如果待刪除節點包含子節點則刪除失敗 sync path
listquota path  --- 列出節點配額
get path [watch] --- 獲取節點數據
create [-s] [-e] path data acl --- 創建子節點
addauth scheme auth  --- 增加zookeeper用戶認證
quit --- 退出zkCli
getAcl path  --- 查詢節點權限
close --- 關閉當前連接
connect host:port --- 連接zkServer

1.  ls

此命令用於列出和顯示znode的子項。ls命令要求路徑以/開頭,並支持tab鍵自動補全。例如:

  • 執行:ls /                        輸出[dubbo, task, zookeeper, test, disconf]: 說明根節點下包含5個子節點。
  • 執行:ls /test                 輸出[test1-2, test1-1]:說明/test節點下面有2個節點。
  • 執行:ls /test/test1-2     輸出[]:說明/test/test1-2節點下面沒有子節點。

2.  ls2

此命令也用於列出和顯示znode的子項,與ls命令不同的是,該命令同時列出該節點的time、version等信息。例如:

執行 ls /test , 輸出  

[test1-2, test1-1]
cZxid = 0x69043
ctime = Mon Sep 03 06:37:21 CST 2018
mZxid = 0x69043
mtime = Mon Sep 03 06:37:21 CST 2018
pZxid = 0x69045
cversion = 2
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 3
numChildren = 2

3.  create

命令格式: create [-s] [-e] path data acl

  • -s:創建順序節點。當一個新的znode被創建為一個順序節點時,ZooKeeper通過將10位的序列號附加到原始名稱來設置znode的路徑。
  • -e:創建臨時節點。客戶端活躍時,臨時節點就是有效的。當客戶端與ZooKeeper集合斷開連接時,臨時節點會自動刪除。
  • path:待創建的節點路徑。以/開頭。
  • data:帶創建的節點存儲的數據內容。
  • acl:權限控制。后面再說。

例如:

  • 創建永久節點:create /test/test1-3 "nodeData"
  • 創建臨時節點:create -e /test/test1-4 "nodeData"
  • 創建循序節點:create -s /test/test1-5 "nodeData"

執行完以上命令以后,再執行 ls /test,可以看到輸出結果:[test1-4, test1-2, test1-3, test1-1, test1-50000000004],可以看到創建的順序節點test1-5被增加了序列號。我們執行quit退出zkCli,再重新執行bin/zkCli.sh重新連接,執行 ls /test,輸出結果:[test1-2, test1-3, test1-1, test1-50000000004],可以看到創建的臨時節點test1-4已經沒有了。

4.  get

命令格式: get path [watch]。它返回znode的關聯數據和指定znode的元數據。你將獲得信息,例如上次修改數據的時間,修改的位置以及數據的相關信息。

  • path:節點路徑。以/開頭。
  • watch:監控。后面再說。

例如:執行 get /test/test1-3 輸出

"nodeData"
cZxid = 0x69046
ctime = Mon Sep 03 07:00:25 CST 2018
mZxid = 0x69046
mtime = Mon Sep 03 07:00:25 CST 2018
pZxid = 0x69046
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 10
numChildren = 0

5.  set

命令格式 set path data [version]。設置指定znode的數據。

  • path:節點路徑。以/開頭。
  • data:節點數據內容。
  • version: 當前數據版本號。如果該參數和當前數據版本號不一致則會拋異常。

例如:執行 set /test/test1-3 "dataNode-New",然后我們再執行 get /test/test1-3 查看數據,輸出:

"dataNode-New"
cZxid = 0x69046
ctime = Mon Sep 03 07:00:25 CST 2018
mZxid = 0x6904b
mtime = Mon Sep 03 07:42:46 CST 2018
pZxid = 0x69046
cversion = 0
dataVersion = 1
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 14
numChildren = 0

可以看到dataVersion已經發生了改變,當前 dataVersion = 1。

6.  delete

命令格式:delete path [version]。刪除指定節點,如果要刪除的節點包含子節點則會刪除失敗。

  • path:節點路徑。以/開頭。
  • version: 當前數據版本號。如果該參數和當前數據版本號不一致則會拋異常。

例如:執行 delete /test/test1-3,再執行 ls /test,輸出:[test1-2, test1-1, test1-50000000004],可以看到test1-3這個節點已經被刪除掉了。

7.  addauth

命令格式:addauth scheme auth。該命令為zookeeper增加一個用戶認證,例如:addauth digest zookeeper:zookeeper-pass。其中zookeeper為用戶名,zookeeper-pass為密碼。

8.  history

命令格式:history。該命令用來查看歷史命令,查詢到的歷史命令前會有編號,提供redo操作。

 

9.  redo

命令格式:redo cmdno。再次執行歷史命令,配合history命令使用。參數cmdno即為history命令執行結果中的編號

三、Zookeeper權限控制

zookeeper支持的命令中,有如下兩條命令是和權限相關的:

  • setAcl path acl
  • getAcl path
  • create [-s] [-e] path data acl

zookeeper共支持5中權限,分別是

  • create:創建節點
  • delete:刪除節點
  • read:讀取節點數據以及子節點
  • write:修改節點數據
  • admin:設置節點權限

zookeeper的ACL,可以從三個維度來理解:一是scheme;二是user;三是permission。通常表示為scheme:id:permissions。

授權模式

  • world: 表示所有,創建節點時默認此權限范圍。有唯一的id是anyone。例如:setAcl /test/test1-1 world:anyone:cdrwa。表示/test/test1-1節點所有人都可以執行cdrwa操作。
  • auth:它不需要id,只要是通過認證的user都有權限。例如:setAcl /test/test1-2 auth:zookeeper:zookeeper-pass:cdrwa表示通過密碼驗證的用戶名為zookeeper的用戶可執行cdrwa操作。當然,執行此命令的前提是已經通過addauth命令添加了該用戶名密碼的用戶。執行完setAcl命令后,再執行getAcl /test/test1-2,返回輸出結果:
    'digest,'zookeeper:hdtnoeyerDQpPGLJ41lW1u1lCSA=
    : cdrwa
    請注意上面輸出結果的hdtnoeyerDQpPGLJ41lW1u1lCSA=,該值緊接着我們就會用到。
  • digest:類似於auth授權。與auth授權的區別在於,輸入命令的時候,用戶密碼需要先進行SHA-1加密再進行Base64編碼。首先,先生成加密后的密碼,執行:echo -n zookeeper:zookeeper-pass | openssl dgst -binary -sha1 | openssl base64,輸出:hdtnoeyerDQpPGLJ41lW1u1lCSA=。可以看到該輸出值即為auth授權后重新getAcl得到的值。然后,執行digest授權:setAcl /test/test1-3 digest:zookeeper:hdtnoeyerDQpPGLJ41lW1u1lCSA=:cdrwa。通過 getAcl /test/test1-3 可以看到授權結果和 getAcl /test/test1-2完全一致
  • ip:它對應的id為客戶機的IP地址,設置的時候可以設置一個ip段,比如執行:setAcl /test/test1-4 ip:192.168.1.0/16:cdrwa, 表示匹配前16個bit的IP段。

 四、quota配額

zookeeper可以在znode上設置配額限制。如果超出了配置限制,zookeeper將會在log日志中打印WARN日志。如果超出配額限制,並不會停止行為操作。

1.  setquota

命令格式:setquota -n|-b val path。為節點設定配額。例如命令:setquota -n 10 /test/test1-1

  • -n :限制子節點個數
  • -b :限制節點數據長度
  • val :  限制的具體的值
  • path : 要做限制的節點路徑

2.  delquota

命令格式:delquota [-n|-b] path。刪除節點配額。例如命令:delquota  -n /test/test1-1

  • -n|-b : 要刪除的配額類型
  • path : 要做限制的節點路徑

3.  listquota

命令格式:listquota path。列出節點設置的配額。例如命令:listquota  /test/test1-1

五、zookeeper in java

@Test
public void testClient() throws IOException, InterruptedException, KeeperException {
    final String zookeeperHost = "10.5.31.155";
    final String zookeeperPort = "2181";
    // 鏈接zk服務器
    final CountDownLatch countDownLatchConnect = new CountDownLatch(1);
    final ZooKeeper zooKeeper = new ZooKeeper(zookeeperHost + ":" + zookeeperPort, 60000, event -> {
        if (event.getState().equals(Watcher.Event.KeeperState.SyncConnected)) {
            countDownLatchConnect.countDown();
        }
    });
    countDownLatchConnect.await();

    // watch節點 並執行創建
    zooKeeper.exists("/clientTestNode", System.out::println);
    zooKeeper.create("/clientTestNode", "clientTestNodeData".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);

    // watch節點 並執行刪除
    zooKeeper.exists("/clientTestNode", System.out::println);
    zooKeeper.delete("/clientTestNode", -1);

    // 1. 創建節點 2. watch節點數據 3. 修改節點數據 4. 刪除節點
    zooKeeper.create("/clientTestNode", "clientTestNodeData".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
    byte[] dataOld = zooKeeper.getData("/clientTestNode", System.out::println, null);
    System.out.println(new String(dataOld));
    zooKeeper.setData("/clientTestNode", "clientTestNodeDataNew".getBytes(), -1);
    byte[] dataNew = zooKeeper.getData("/clientTestNode", System.out::println, null);
    System.out.println(new String(dataNew));
    zooKeeper.delete("/clientTestNode", -1);

    // 1. 創建節點 2. 監控節點的子節點 3. 創建子節點 4. 監控節點子節點 5. 刪除子節點
    zooKeeper.create("/clientTestNode", "clientTestNodeData".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
    zooKeeper.getChildren("/clientTestNode", System.out::println, null);
    zooKeeper.create("/clientTestNode/child1", "child1Data".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
    zooKeeper.getChildren("/clientTestNode", System.out::println, null);
    zooKeeper.create("/clientTestNode/child2", "child2Data".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
    zooKeeper.getChildren("/clientTestNode", System.out::println, null);
    zooKeeper.delete("/clientTestNode/child1", -1);
    zooKeeper.getChildren("/clientTestNode", System.out::println, null);
    zooKeeper.delete("/clientTestNode/child2", -1);
    zooKeeper.delete("/clientTestNode", -1);

}

需要注意的點:

  1. 由於zookeeper的連接是異步過程,所以執行new Zookeeper之后不要緊接着執行zookeeper命令。否則可能會出現丟失連接異常。
  2. 由於zookeeper的watcher是一次性有效(即zookeeper回調watcher后該watcher即被刪除),所以會出現重復注冊watcher的現象


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM