zookeeper使用詳解(命令、客戶端、源碼)

1. zookeeper使用詳解(命令、客戶端、源碼)

1.1. 前言

  zookeeper咱們經常使用來作分佈式協調中間件,不少時候咱們都接觸不到它的原理和用法,我對他的瞭解也僅限於知道它能夠作分佈式協調、配置管理、分佈式鎖,而且有個watch節點監聽經常能聽到。接下來我要系統的學下zookeeper的功能和原理,一塊兒走進zookeeper的世界html

1.2. 概述

  zookeeper主要目的就是爲了分佈式應用提供協同服務,zookeeper的節點管理機制,當節點發生變化時(建立、刪除、數據變動),能夠通知各個客戶端,利用這種特性,zk的主要場景就如我前面說的:java

1. 統一配置:把配置放在ZooKeeper的節點中維護,當配置變動時,客戶端能夠收到變動的通知,並應用最新的配置。
2. 集羣管理:集羣中的節點,建立ephemeral的節點,一旦斷開鏈接,ephemeral的節點會消失,其它的集羣機器能夠收到消息。
3. 分佈式鎖:多個客戶端發起節點建立操做,只有一個客戶端建立成功,從而得到鎖。

1.3. zk基本操做

  1. ZNode ZNode是ZK樹形結構的一個節點,它能夠包含或者不包含數據。
  2. ZK提供了以下API,用於操做ZNode。
    1. create path data
    2. delete path data
    3. exists path
    4. getdata path
    5. putdata path data
    6. getChildren path
  3. ZK客戶端經過創建一個Session會話,來鏈接ZK服務,經過這些API來操做ZNode。

1.4. zk模式

  1. ZNode模式 目前ZNode包含持久模式和短暫模式ephemeral。
  2. ephemeral模式指的是這個節點在session斷了以後就會消失
  3. 持久模式和ephemeral模式外,ZNode還能夠是有序的(自動分配自增ID到節點上,好比task-1,task-2,task-3)
  4. 所以ZNode一共有四種形態:
    1. 持久
    2. 持久有序
    3. ephemeral
    4. ephemeral有序

1.5. watch機制

  1. Watch和Notifications Watch能夠避免每次主動去請求數據是否變化,而是當ZNode變化時,來通知。
  2. Watch是個一次性操做,每次收到通知後,必須從新watch,若是時間比較久或者ZNode更新頻繁,在此時間之間,可能會有更新沒有被通知到(還沒來得急watch)。
  3. ZNode的建立、刪除和修改均可以被watch到。

1.6. FAQ

1.6.1. 客戶端對服務器列表的輪詢機制

  1. 客戶端創建與zk會話的時候須要咱們填寫zk服務器列表,以後該列表會被隨機打散而後每次請求輪詢該列表,這種打散是一次性的,以後每次都是這個順序
  2. 知道客戶端輪詢原理,能夠知道列表是能夠重複填寫的,這樣也能夠經過重複填寫配置地址來增長權重,但也會有風險,可能使server切換耗時過長,卻是session_expired

參考 https://blog.51cto.com/nileader/932948node

1.6.2. 客戶端常遇到的浴場Connectionloss(鏈接斷開)和Sessionexpired(session過時)處理

  1. 在ZooKeeper中,服務器和客戶端之間維持的是一個長鏈接,在 SESSION_TIMEOUT 時間內,服務器會肯定客戶端是否正常鏈接(客戶端會定時向服務器發送heart_beat),服務器重置下次SESSION_TIMEOUT時間。所以,在正常狀況下,Session一直有效,而且zk集羣全部機器上都保存這個Session信息。在出現問題狀況下,客戶端與服務器之間鏈接斷了(客戶端所鏈接的那臺zk機器掛了,或是其它緣由的網絡閃斷),這個時候客戶端會主動在地址列表(初始化的時候傳入構造方法的那個參數connectString)中選擇新的地址進行鏈接
  2. connectionloss一般發生在鏈接的zk掛了,這個時候只要等待客戶端鏈接上新的zk機器(zk必須集羣),而後確認操做是否執行成功
  3. sessionexpired一般發生在zk客戶端和服務器的鏈接斷了,視圖連上新的zk機器,若是這個過程耗時過長,超過session_timeout時間,那麼服務器認爲這個session已經結束(服務器沒法確認時由於其餘異常緣由仍是客戶端主動結束會話),開始清除和這個會話相關的信息,包括會話建立的臨時節點和註冊的watcher。這時客戶端從新鏈接上服務器,服務器會報sessionexpired。這個時候解決辦法要看業務狀況了,只能從新實例化zk對象,從新操做節點數據

1.6.3. 建立的臨時節點何時會被刪除,是鏈接一斷就刪除嗎?

  1. 鏈接斷了以後,ZK不會立刻移除臨時數據,只有當SESSIONEXPIRED以後,纔會把這個會話創建的臨時數據移除。所以,用戶須要謹慎設置Session_TimeOut

1.6.4. zk日誌清理

  1. zk不會自動清理日誌,參考:https://blog.51cto.com/nileader/932156

1.7. zkClient命令行(包含了所有命令)

1.7.1. 建立節點

  1. 語法create [-s] [-e] path data acl
  2. -s 建立有序節點 -e建立臨時節點
  3. acl專門一節講git

    [zk: localhost:2181(CONNECTED) 4] create -s -e /mynode/subnode hellp
    Node does not exist: /mynode/subnode
    [zk: localhost:2181(CONNECTED) 5] create -s -e /mynode/ hellp       
    Node does not exist: /mynode/
    [zk: localhost:2181(CONNECTED) 6] create -s -e /mynode hellp 
    Created /mynode0000000001
    [zk: localhost:2181(CONNECTED) 7] create -s -e /mynode/subnode hello
    Node does not exist: /mynode/subnode
    [zk: localhost:2181(CONNECTED) 8] ls /mynode
    Node does not exist: /mynode
    [zk: localhost:2181(CONNECTED) 9] ls /
    [mycat, mynode0000000001, zookeeper]
    [zk: localhost:2181(CONNECTED) 10] create -s -e /mynode0000000001/subnode hello
    Ephemerals cannot have children: /mynode0000000001/subnode
  4. 上面的命令能夠看出
    1. 第一不能直接建立多級幾點
    2. 第二建立臨時節點不能有子節點
    3. 第三有序節點節點名後會加上序號

1.7.2. 列出節點 ls

  1. ls path [watch]
  2. ls2 path [watch]
[zk: localhost:2181(CONNECTED) 17] ls2 /persistence
[]
cZxid = 0x12
ctime = Tue Mar 26 06:52:28 GMT 2019
mZxid = 0x12
mtime = Tue Mar 26 06:52:28 GMT 2019
pZxid = 0x12
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 3
numChildren = 0
[zk: localhost:2181(CONNECTED) 18] ls /persistence 
[]
  1. 能夠看出,ls2能給出更詳細的路徑信息
[zk: localhost:2181(CONNECTED) 2] ls / 1
[persistence, temporary, mycat, zookeeper]
[zk: localhost:2181(CONNECTED) 3] create -e /temp 123

WATCHER::

WatchedEvent state:SyncConnected type:NodeChildrenChanged path:/
Created /temp
[zk: localhost:2181(CONNECTED) 4] create -e /temp2 123
Created /temp2
  1. 根節點建立監聽器1,以後根節點的改變會觸發監聽器,但只有一次

1.7.3. 獲取節點信息 get

  1. get path [watch]
[zk: localhost:2181(CONNECTED) 5] get /temp2
123
cZxid = 0x17
ctime = Tue Mar 26 06:59:20 GMT 2019
mZxid = 0x17
mtime = Tue Mar 26 06:59:20 GMT 2019
pZxid = 0x17
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x103c831d4dc0003
dataLength = 3
numChildren = 0
  1. 能夠看到,get到路徑有詳細信息,和ls2得到的信息同樣
  2. 每個對znode樹的更新操做,都會被賦予一個全局惟一的ID,咱們稱之爲zxid(ZooKeeper Transaction ID)
  3. 更新操做的ID按照發生的時間順序升序排序。例如,z1大於z2,那麼z1的操做就早於z2操做。
  4. 每一個 znode 的狀態信息包含如下內容:
    • czxid,建立(create)該 znode 的 zxid
    • mzxid,最後一次修改(modify)該 znode 的 zxid
    • pzxid,最後一次修改該 znode 子節點的 zxid
    • ctime,建立該 znode 的時間
    • mtime,最後一次修改該 znode 的時間
    • dataVersion,該節點內容的版本,每次修改內容,版本都會增長
    • cversion,該節點子節點的版本
    • aclVersion,該節點的 ACL 版本
    • ephemeralOwner,若是該節點是臨時節點(ephemeral node),會列出該節點所在客戶端的 session id;若是不是臨時節點,該值爲 0
    • dataLength,該節點存儲的數據長度
    • numChildren,該節點子節點的個數

1.7.4. 檢查狀態 stat

  1. stat path [watch]
  2. 與 get 的區別是,不會列出 znode 的值。

1.7.5. 修改節點 set

  1. set path data [version]
  2. 修改已經存在的節點的值
[zk: localhost:2181(CONNECTED) 10] set /temp2 456
cZxid = 0x17
ctime = Tue Mar 26 06:59:20 GMT 2019
mZxid = 0x18
mtime = Tue Mar 26 07:12:27 GMT 2019
pZxid = 0x17
cversion = 0
dataVersion = 1
aclVersion = 0
ephemeralOwner = 0x103c831d4dc0003
dataLength = 3
numChildren = 0
  1. 能夠看到,在修改節點值以後,mZxid、mtime、dataVersion 都發生了變化。

1.7.6. 刪除節點 rmr

  1. rmr path
[zk: localhost:2181(CONNECTED) 12] rmr /temp2
[zk: localhost:2181(CONNECTED) 13] get /temp2
Node does not exist: /temp2
  1. 刪除 /mynode,不會返回任何內容。若是有子節點的時候,連帶子節點也一塊兒刪除

1.7.7. 刪除節點 delete

  1. delete path [version]
  2. 調用delete和set操做時,若是指定znode版本號,須要與當前的版本號匹配。若是版本號不匹配,操做將會失敗。失敗的緣由多是在咱們提交以前,該znode已經被修改過了,版本號發生了增量變化。若是不指定版本號,就是直接操做最新版本的 znode。
  3. 若是要刪除的節點有子節點,不能刪除
[zk: localhost:2181(CONNECTED) 17] create /per 1
Created /per
[zk: localhost:2181(CONNECTED) 18] create /per/subper 2
Created /per/subper
[zk: localhost:2181(CONNECTED) 19] delete /per
Node not empty: /per
[zk: localhost:2181(CONNECTED) 20] delete /per/subper
[zk: localhost:2181(CONNECTED) 21] delete /per
[zk: localhost:2181(CONNECTED) 22] ls /per
Node does not exist: /per

1.7.8. 歷史記錄 history

  1. history 列出最近的10條歷史記錄
[zk: localhost:2181(CONNECTED) 23] history
13 - get /temp2
14 - ls /
15 - ls /temp
16 - get /temp
17 - create /per 1
18 - create /per/subper 2
19 - delete /per
20 - delete /per/subper
21 - delete /per
22 - ls /per
23 - history

1.7.9. 重複以前的命令 redo

  1. redo cmdno 根據 cmdno 重複以前的命令,cmdno 就是方括號裏面最後的數字,每次執行命令都會自增。
[zk: localhost:2181(CONNECTED) 25] redo 22
Node does not exist: /per
[zk: localhost:2181(CONNECTED) 26] redo 17
Created /per

1.7.10. 是否輸出 watch 事件(printwatches)

  1. printwatches on|off
[zk: localhost:2181(CONNECTED) 28] printwatches
printwatches is on
[zk: localhost:2181(CONNECTED) 29] ls /mynode
Node does not exist: /mynode
[zk: localhost:2181(CONNECTED) 30] create /mynode 123
Created /mynode
[zk: localhost:2181(CONNECTED) 31] ls /mynode watch
[]
[zk: localhost:2181(CONNECTED) 34] create /mynode/subnode 234

WATCHER::

WatchedEvent state:SyncConnected type:NodeChildrenChanged path:/mynode
Created /mynode/subnode
[zk: localhost:2181(CONNECTED) 35] printwatches off
[zk: localhost:2181(CONNECTED) 36] ls /mynode 2
[subnode]
[zk: localhost:2181(CONNECTED) 37] create /mynode/subnode2 567
Created /mynode/subnode2
  1. 能夠看到設置off後,監聽打印輸出就沒有了

1.7.11. 關閉鏈接 close

  1. close
[zk: localhost:2181(CONNECTED) 38] close 
2019-03-26 07:26:59,240 [myid:] - INFO  [main:ZooKeeper@693] - Session: 0x103c831d4dc0003 closed
[zk: localhost:2181(CLOSED) 39] 2019-03-26 07:26:59,241 [myid:] - INFO  [main-EventThread:ClientCnxn$EventThread@522] - EventThread shut down for session: 0x103c831d4dc0003
ls
Not connected
[zk: localhost:2181(CLOSED) 40] ls /
Not connected

1.7.12. 打開鏈接 connect

  1. connect host:port
[zk: localhost:2181(CLOSED) 42] connect
2019-03-26 07:28:18,093 [myid:] - INFO  [main:ZooKeeper@442] - Initiating client connection, connectString=localhost:2181 sessionTimeout=30000 watcher=org.apache.zookeeper.ZooKeeperMain$MyWatcher@782830e
[zk: localhost:2181(CONNECTING) 43] 2019-03-26 07:28:18,096 [myid:] - INFO  [main-SendThread(localhost:2181):ClientCnxn$SendThread@1029] - Opening socket connection to server localhost/127.0.0.1:2181. Will not attempt to authenticate using SASL (unknown error)
2019-03-26 07:28:18,097 [myid:] - INFO  [main-SendThread(localhost:2181):ClientCnxn$SendThread@879] - Socket connection established to localhost/127.0.0.1:2181, initiating session
2019-03-26 07:28:18,100 [myid:] - INFO  [main-SendThread(localhost:2181):ClientCnxn$SendThread@1303] - Session establishment complete on server localhost/127.0.0.1:2181, sessionid = 0x103c831d4dc0004, negotiated timeout = 30000

[zk: localhost:2181(CONNECTED) 43] ls /
[mycat, mynode, zookeeper, persistence, per]
  1. 能夠看到默認鏈接的是本地的2181

1.7.13. 退出鏈接 quit

  1. quit 直接退出當前的 zkCli 命令行。

1.7.14. 強制同步 sync

  1. sync path
  2. sync方法會強制客戶端所鏈接的服務器狀態與leader的狀態同步,這樣在讀取 path 的值就是最新的值了

1.7.15. ACL 操做

  1. 一個znode中不只包含了存儲的數據,還有 ACL(Access Control List)。znode的建立時,能夠給它設置一個ACL(Access Control List),來決定誰能夠對znode作哪些操做
  2. ACL 具備如下特色:
    • ZooKeeper的權限控制是基於每一個znode節點的,須要對每一個節點設置權限
    • 每一個znode支持設置多種權限控制方案和多個權限
    • 子節點不會繼承父節點的權限,客戶端無權訪問某節點,但可能能夠訪問它的子節點
    • 因此任何一個客戶端均可以經過exists 操做來得到任何znode的狀態,從而得知znode是否真的存在。

1.7.16. ACL Permissions

ACL 權限 ACL 簡寫 容許的操做
CREATE c 建立子節點
READ r 獲取節點的數據和它的子節點
WRITE w 設置節點的數據
DELETE d 刪除子節點 (僅下一級節點)
ADMIN a 設置 ACL 權限

1.7.17. 權限相關命令

命令 語法 描述
getAcl getAcl path 讀取ACL權限
setAcl setAcl path acl 設置ACL權限
addauth addauth scheme auth 添加認證用戶
create create [-s] [-e] path data acl 建立節點時指明 ACL 權限

1.7.18. ACL Schemes

  1. ZooKeeper內置了一些權限控制方案,能夠用如下方案爲每一個節點設置權限:
方案 描述
world 只有一個用戶:anyone,表明全部人(默認)
ip 使用IP地址認證
auth 使用已添加認證的用戶認證
digest 使用「用戶名:密碼」方式認證
  1. ACL是由鑑權方式、鑑權方式的ID和一個許可(permession)的集合組成。例如,咱們想經過一個ip地址爲10.0.0.1的客戶端訪問一個znode。那麼,咱們須要爲znode設置一個ACL,鑑權方式使用IP鑑權方式,鑑權方式的ID爲10.0.0.1,只容許讀權限。那麼 ACL 的格式就是:ip:10.0.0.1:w

1.7.18.1. world 方案

  1. setAcl <path> world:anyone:<acl>
[zk: localhost:2181(CONNECTED) 7] getAcl /world
'world,'anyone
: cdrwa
[zk: localhost:2181(CONNECTED) 8] setAcl /world world:anyone:cdr
cZxid = 0x27
ctime = Tue Mar 26 07:41:43 GMT 2019
mZxid = 0x27
mtime = Tue Mar 26 07:41:43 GMT 2019
pZxid = 0x27
cversion = 0
dataVersion = 0
aclVersion = 1
ephemeralOwner = 0x0
dataLength = 1
numChildren = 0
[zk: localhost:2181(CONNECTED) 9] set /world 234
Authentication is not valid : /world
  1. 能夠看出,在修改權限爲 cdr 以後,不能再設置節點數據了。注意 aclVersion 也發生了變化

1.7.18.2. IP 方案

  1. setAcl <path> ip:<ip>:<acl>
[zk: localhost:2181(CONNECTED) 10] create /ip hello
Created /ip
[zk: localhost:2181(CONNECTED) 11] getAcl /ip    
'world,'anyone
: cdrwa
[zk: localhost:2181(CONNECTED) 12] setAcl /ip ip:52.231.163.100:cdrwa
cZxid = 0x2a
ctime = Tue Mar 26 07:56:21 GMT 2019
mZxid = 0x2a
mtime = Tue Mar 26 07:56:21 GMT 2019
pZxid = 0x2a
cversion = 0
dataVersion = 0
aclVersion = 1
ephemeralOwner = 0x0
dataLength = 5
numChildren = 0
[zk: localhost:2181(CONNECTED) 13] getAcl /ip                        
'ip,'52.231.163.100
: cdrwa
[zk: localhost:2181(CONNECTED) 14] get /ip
Authentication is not valid : /ip

1.7.18.3. auth 方案

addauth digest <user>:<password> #添加認證用戶
setAcl <path> auth:<user>:<acl>
[zk: localhost:2181(CONNECTED) 15] create /auth hello
Created /auth
[zk: localhost:2181(CONNECTED) 16] addauth digest admin:tom
[zk: localhost:2181(CONNECTED) 17] setAcl /auth auth:tom:cdrwa
cZxid = 0x2c
ctime = Tue Mar 26 08:04:23 GMT 2019
mZxid = 0x2c
mtime = Tue Mar 26 08:04:23 GMT 2019
pZxid = 0x2c
cversion = 0
dataVersion = 0
aclVersion = 1
ephemeralOwner = 0x0
dataLength = 5
numChildren = 0
[zk: localhost:2181(CONNECTED) 18] getAcl /auth
'digest,'admin:cFk4QI8k/ZVgHVEnb06Vtoc651o=
: cdrwa

斷開之後再連上,須要從新認證github

WatchedEvent state:SyncConnected type:None path:null
[zk: localhost:2181(CONNECTED) 0] get /auth
Authentication is not valid : /auth

1.7.18.4. digest 方案

  1. setAcl <path> digest:<user>:<password>:<acl>
  2. 這裏的密碼是通過SHA1及BASE64處理的密文,在SHELL中能夠經過如下命令計算:算法

    echo -n : | openssl dgst -binary -sha1 | openssl base64 apache

[root@izbp1itlw36onyj4m9b4hiz ~]# echo -n admin:123456 | openssl dgst -binary -sha1 | openssl base64
0uek/hZ/V9fgiM35b0Z2226acMQ=
[zk: localhost:2181(CONNECTED) 21] create /digest hello
Created /digest
[zk: localhost:2181(CONNECTED) 22] setAcl /digest digest:admin:0uek/hZ/V9fgiM35b0Z2226acMQ=:cdrw 
cZxid = 0x39
ctime = Tue Mar 26 08:22:04 GMT 2019
mZxid = 0x39
mtime = Tue Mar 26 08:22:04 GMT 2019
pZxid = 0x39
cversion = 0
dataVersion = 0
aclVersion = 1
ephemeralOwner = 0x0
dataLength = 5
numChildren = 0
[zk: localhost:2181(CONNECTED) 23] getAcl /digest
'digest,'admin:0uek/hZ/V9fgiM35b0Z2226acMQ=
: cdrw
[zk: localhost:2181(CONNECTED) 24] get /digest
Authentication is not valid : /digest
[zk: localhost:2181(CONNECTED) 25] addauth digest admin:123456
[zk: localhost:2181(CONNECTED) 26] get /digest
hello
cZxid = 0x39
ctime = Tue Mar 26 08:22:04 GMT 2019
mZxid = 0x39
mtime = Tue Mar 26 08:22:04 GMT 2019
pZxid = 0x39
cversion = 0
dataVersion = 0
aclVersion = 1
ephemeralOwner = 0x0
dataLength = 5
numChildren = 0

1.7.18.5. 建立節點時指定 ACL

[zk: localhost:2181(CONNECTED) 27] addauth digest admin:tim
[zk: localhost:2181(CONNECTED) 28] create /createnode hello auth:tim:cdrwa  
Created /createnode
[zk: localhost:2181(CONNECTED) 29] getAcl
[zk: localhost:2181(CONNECTED) 30] getAcl /createnode
'digest,'admin:0uek/hZ/V9fgiM35b0Z2226acMQ=
: cdrwa
'digest,'admin:H4JbicQawMpoqvA2LI0LFNFSMNE=
: cdrwa
[zk: localhost:2181(CONNECTED) 31] get /createnode
hello
cZxid = 0x3b
ctime = Tue Mar 26 08:27:27 GMT 2019
mZxid = 0x3b
mtime = Tue Mar 26 08:27:27 GMT 2019
pZxid = 0x3b
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 5
numChildren = 0
[zk: localhost:2181(CONNECTED) 32] close

[zk: localhost:2181(CLOSED) 33] connect
[zk: localhost:2181(CONNECTED) 34] get /createnode
Authentication is not valid : /createnode
[zk: localhost:2181(CONNECTED) 35] getAcl /createnode
'digest,'admin:0uek/hZ/V9fgiM35b0Z2226acMQ=
: cdrwa
'digest,'admin:H4JbicQawMpoqvA2LI0LFNFSMNE=
: cdrwa
[zk: localhost:2181(CONNECTED) 36] addauth digest admin:tim
[zk: localhost:2181(CONNECTED) 37] get /createnode
hello
cZxid = 0x3b
ctime = Tue Mar 26 08:27:27 GMT 2019
mZxid = 0x3b
mtime = Tue Mar 26 08:27:27 GMT 2019
pZxid = 0x3b
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 5
numChildren = 0
  1. 關閉會話後須要從新受權

1.7.19. zookeeper quota

  1. zookeeper quota 機制支持節點個數(namespace)和空間大小(bytes)的設置。
  2. zookeeper quota 保存在 /zookeeper/quota 節點下,能夠設置該節點的 ACL 權限,以防其餘人修改。
  3. 語法listquota pathsetquota -n|-b val pathdelquota [-n|-b] path
  4. -n表示設置znode count限制,包含自身節點
[zk: localhost:2181(CONNECTED) 10] ls /zookeeper/quota
[]
[zk: localhost:2181(CONNECTED) 11] get /zookeeper/quota

cZxid = 0x0
ctime = Thu Jan 01 00:00:00 GMT 1970
mZxid = 0x0
mtime = Thu Jan 01 00:00:00 GMT 1970
pZxid = 0x0
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 0
numChildren = 0
[zk: localhost:2181(CONNECTED) 12] listquota /persistence
absolute path is /zookeeper/quota/persistence/zookeeper_limits
quota for /persistence does not exist.
[zk: localhost:2181(CONNECTED) 13] setquota -n 3 /persistence
Comment: the parts are option -n val 3 path /persistence
[zk: localhost:2181(CONNECTED) 14] create /persistence/node1 123
Created /persistence/node1
[zk: localhost:2181(CONNECTED) 15] create /persistence/node2 124
Created /persistence/node2
[zk: localhost:2181(CONNECTED) 16] create /persistence/node3 125
Created /persistence/node3
[zk: localhost:2181(CONNECTED) 17] listquota /persistence
absolute path is /zookeeper/quota/persistence/zookeeper_limits
Output quota for /persistence count=3,bytes=-1
Output stat for /persistence count=4,bytes=12
  1. 能夠看到,超出節點數zk也不會報錯,只會在listquato裏打下日誌
[zk: localhost:2181(CONNECTED) 18] delquota -n /persistence
[zk: localhost:2181(CONNECTED) 19] listquota /persistence  
absolute path is /zookeeper/quota/persistence/zookeeper_limits
Output quota for /persistence count=-1,bytes=-1
Output stat for /persistence count=4,bytes=12
  1. 刪除節點quota,count變成-1

1.8. java操做

1.8.1. 異步操做

  1. 主流異步操做api

    參考 https://github.com/llohellohe/zookeeper/blob/master/src/main/java/yangqi/zookeeper/example/masterworker/AsynMaster.java服務器

1.8.2. 狀態變動

  1. Watcher優點 經過watcher,能夠避免主動輪詢致使的額外負擔,更加實時和有效率。
  2. Watcher接口,僅有一個實現接口public void process(WatchedEvent event)
  3. WatchedEvent表明watcher到的事件,它包含發生了什麼事件,ZooKeeper的當前鏈接狀態以及產生事件的ZNode路徑
    1. KeeperState
    2. EventType
    3. path
  4. KeeperState包含Disconnected\SyncConnected\AuthFailed\ConnectedReadOnly\SaslAuthenticated\Expired等6種狀態。
  5. EventType包含五種狀態:
    1. None
    2. NodeCreated
    3. NodeDeleted
    4. NodeDataChanged
    5. NodeChildrenChanged
    • 其中後四種用於表示ZNode的狀態或者數據變動,而None則用於會話的狀態變動。
  6. EventType爲None的Watch SessionWatch實例描述了,初始化一個ZooKeeper實例時註冊的Watcher接口
    1. 將在鏈接時收到EventType爲None,KeeperState爲SyncConnected,path爲null的Event
    2. 將在失去鏈接時收到EventType爲None,KeeperState爲:Disconnected,path爲null的Event
  7. ChildrenCallback 經過getChildren方法,能夠設置ChildrenCallback,以便得到得到當子節點發生變化時的相關信息。
    • ChildrenCallback 的惟一接口:
    public void processResult(int rc, String path, Object ctx, List<String> children)
    • getChildren能夠設置對應的Watcher,一旦發現節點的事件類型爲NodeChildrenChanged後,能夠繼續設置watch
  8. 例子:https://github.com/llohellohe/llohellohe.github.com/blob/master/readers/ZooKeeper/04-%E7%8A%B6%E6%80%81%E5%8F%98%E6%9B%B4.md
  9. 上述這篇例子我用zookeeper3.4.13運行會報NullPointException,我dubug後發現,該版本zookeeper在進入連接剛創建狀態(None)時,會添加默認defaultWatcher,而示例代碼初始化時,watch填了null,致使了後續,從set中獲得的watcher爲null,我認爲這是個bug,初始化鏈接不放watch應該也是容許的。
@Override
        public Set<Watcher> materialize(Watcher.Event.KeeperState state,
                                        Watcher.Event.EventType type,
                                        String clientPath)
        {
            Set<Watcher> result = new HashSet<Watcher>();

            switch (type) {
            case None://初始化鏈接時進入
                result.add(defaultWatcher);//defaultWatcher若是填空,後續會報錯
private void processEvent(Object event) {
          try {
              if (event instanceof WatcherSetEventPair) {
                  // each watcher will process the event
                  WatcherSetEventPair pair = (WatcherSetEventPair) event;
                  for (Watcher watcher : pair.watchers) {
                      try {
                          watcher.process(pair.event);//watcher爲null
  1. processEvent方法就是EvenetThread線程處理watcher監聽的地方

1.8.3. Curator

  1. Curator是構建在ZooKeeper上的API,它屏蔽一些複雜的ZooKeeper操做,並提供了一些擴展。

1.8.3.1. 流式API

  1. 通常的ZooKeeper建立節點的代碼以下:
zk.create("/mypath", new byte[0],
              ZooDefs.Ids.OPEN_ACL_UNSAFE,
              CreateMode.PERSISTENT);

1.8.3.2. 使用Curator後

zkc.create().withMode(CreateMode.PERSISTENT).forPath("/mypath", new byte[0]);

1.8.3.3. leader latch 和leader selection

  • 經過Curator能夠方便的進行leader的選舉和控制

1.8.3.4. Leader Elections

  在ZK集羣中,Leader的做用是保證變動操做(create\setData\delete)的順序性
它將接收到的請求轉換成事務,而後提議followers按照順序應用這些事務。在初始階段,全部的ZK服務端都處於LOOKING狀態,要麼找到已經存在Leader結點,要麼本身選舉出Leader。成爲Leader的節點將進入LEADING狀態,其它則將進入FOLLOWING狀態網絡

1.8.3.5. 選舉過程

  1. 進入LOOKING狀態的server將廣播消息,稱爲vote。
  2. vote包含serverId和ZXID,好比(1,5)表示server id爲1的服務端,它的ZXID爲5。
  3. 每一個server將比較本身的vote和收到的vote,若是:
    1. 收到vote的zxid大於本身的,則使用這個vote
    2. 若是zxid相等,則sid大的獲勝
    3. 當推選超過半數以上,則肯定leader

1.9. 源碼分析

1.9.1. 客戶端組成

  • Zookeeper客戶端主要由以下核心部件構成。
    1. Zookeeper實例,客戶端入口
    2. ClientWatchManager, 客戶端Watcher管理器
    3. HostProvider,客戶端地址列表管理器。
    4. ClientCnxn,客戶端核心線程,內部包含了SendThread和EventThread兩個線程,SendThread爲I/O線程,主要負責Zookeeper客戶端和服務器之間的網絡I/O通訊;EventThread爲事件線程,主要負責對服務端事件進行處理。
      1

1.9.2. 初始化

  • 客戶端在初始化和啓動過程當中大致能夠分爲以下3個步驟
    1. 設置默認Watcher
    2. 設置Zookeeper服務器地址列表
    3. 建立ClientCnxn。
  • 若在Zookeeper構造方法中傳入Watcher對象時,那麼Zookeeper就會將該Watcher對象保存在ZKWatcherManager的defaultWatcher中,並做爲整個客戶端會話期間的默認Watcher。

1

1.9.3. 會話的建立

  1. 客戶端與服務端會話創建的整個過程,包括初始化階段(第一階段)、會話建立階段(第二階段)、響應處理階段(第三階段)三個階段。
    2
    細節:
    1

1.9.4. 服務器地址列表

  1. 在實例化Zookeeper時,用戶傳入Zookeeper服務器地址列表,如192.168.0.1:2181,192.168.0.2:2181,192.168.0.3:2181
    • Chroot:客戶端能夠設置本身的命名空間,若客戶端設置了Chroot,此時,該客戶端對服務器的任何操做都將被限制在本身的命名空間下,如設置Choot爲/app/X,那麼該客戶端的全部節點路徑都是以/app/X爲根節點。具體設置可使輸入地址列表的時候加上192.168.0.1:2181/app/X
    • 地址列表管理:Zookeeper使用StaticHostProvider打散服務器地址(shuffle),並將服務器地址造成一個環形循環隊列,而後再依次取出服務器地址。

1.10. 總結

  1. zookeeper比較重要的概念就是選主算法,這裏儘量簡單的簡述一下,分兩類:
    • 全新集羣選主:按照服務器啓動順序,判斷server_id大小,根據過半選舉的規則選主;好比服務器1-5對應server_id1-5,按順序啓動,每啓動一臺會有個選主過程,服務器會交換選主信息,id大的勝出,啓動2臺時,因爲沒有超過半數以上的機器,因此繼續保持LOOKING,當第三臺機器啓動,id最大,且選票結果超過半數,則肯定leader爲server_id=3的機器,後續四、5啓動,因爲已經存在leader,只能當following
    • 非全新集羣選主:這種狀況說明zookeeper集羣leader機器宕機,須要從新選舉,須要根據數據的票選輪數epoch、zxid和server_id判斷,先進行選舉信息的交換,票選輪數小的忽略,zxid大的勝出,zxid相同狀況看server_id,server_id大的勝出
    • zookeeper3.4.13查看源碼能夠發現,protected int electionAlg = 3;
    case 3:
            qcm = createCnxnManager();
            QuorumCnxManager.Listener listener = qcm.listener;
            if(listener != null){
                listener.start();
                le = new FastLeaderElection(this, qcm);
            } else {
                LOG.error("Null listener when initializing cnx manager");
            }
            break;

參考

https://blog.csdn.net/feixiang2039/article/details/79810102#zookeeper-%E5%91%BD%E4%BB%A4 zookeeper 客戶端 zkCli 命令詳解

https://github.com/llohellohe/llohellohe.github.com/blob/master/readers/ZooKeeper llohellohe/llohellohe.github.com

http://www.cnblogs.com/leesf456/p/6098255.html Zookeeper客戶端

https://blog.csdn.net/cxhzqhzq/article/details/6568040 Zookeeper全解析——Paxos做爲靈魂

http://blog.chinaunix.net/uid-26726125-id-4038581.html zookeeper跟經典paxos的對比(附源碼)

https://blog.csdn.net/panxj856856/article/details/80403487 FastLeaderElection選舉算法

相關文章
相關標籤/搜索