zookeeper 入門

概述
ZooKeeper是一種 爲分佈式應用所設計的高可用、高性能 且一致的 開源協調服務,它提供了一項基本服務:分佈式鎖服務。由於ZooKeeper的開源特性,後來的開發者在分佈式鎖的基礎上,摸索了出了其他的使用方法:配置維護、組服務、分佈式消息隊列、分佈式通知/協調等。 在分佈式協調技術方面做得比較好的就是Google的Chubby還有Apache的ZooKeeper都是分佈式鎖的實現者。Chbby是非開源的,Google自家用。後來雅虎模仿Chubby開發出了ZooKeeper,也實現了類似的分佈式鎖的功能,並且將ZooKeeper作爲一種開源的程序捐獻給了Apache。
注意:ZooKeeper性能上的特點決定了它能夠用在大型的、分佈式的系統中。可靠性方面來,它並不會因爲一個節點的錯誤 而崩潰。除此之外,它嚴格的序列訪問控制 意味着複雜的控制原語 可以應用在客戶端上。ZooKeeper在一致性、可用性、容錯性的保證,也是ZooKeeper的成功之處,它獲得的一切成功都與它採用的協議——Zab協議是密不可分的。
ZooKeeper在實現這些服務(分佈式鎖、配置維護、組服務等)時,首先它設計一種新的數據結構——Znode,然後在該數據結構的基礎上定義了一些原語,也就是一些關於該數據結構的一些操作。有了這些數據結構和原語還不夠,因爲ZooKeeper是工作在一個分佈式的環境下,服務是通過消息以網絡的形式發送給分佈式應用程序,所以還需要一個通知機制——Watcher機制。那麼總結一下,ZooKeeper所提供的服務主要是通過:數據結構+原語+watcher機制,三個部分來實現的。

數據模型Znode
ZooKeeper擁有一個層次的命名空間,這個和標準的文件系統非常相似,如下圖
zookeeper 入門

從圖中可以看出ZooKeeper的數據模型,在結構上和標準文件系統的非常相似,都是採用這種樹形層次結構,ZooKeeper樹中的每個節點被稱爲—Znode。和文件系統的目錄樹一樣,ZooKeeper樹中的每個節點可以擁有子節點。但也有不同之處:
(1) 引用方式
Zonde通過路徑引用,如同Unix中的文件路徑。路徑必須是絕對的,因此他們必須由斜槓字符來開頭。除此以外,他們必須是唯一的,也就是說每一個路徑只有一個表示,因此這些路徑不能改變。在ZooKeeper中,路徑由Unicode字符串組成,並且有一些限制。字符串"/zookeeper"用以保存管理信息,比如關鍵配額信息。
(2) Znode結構
ZooKeeper命名空間中的Znode,兼具文件和目錄兩種特點。既像文件一樣維護着數據、元信息、ACL、時間戳等數據結構,又像目錄一樣可以作爲路徑標識的一部分。圖中的每個節點稱爲一個Znode。 每個Znode由3部分組成:
① stat:此爲狀態信息, 描述該Znode的版本, 權限等信息
② data:與該Znode關聯的數據
③ children:該Znode下的子節點
ZooKeeper雖然可以關聯一些數據,但並沒有被設計爲常規的數據庫或者大數據存儲,相反的是,它用來管理調度數據,比如分佈式應用中的配置文件信息、狀態信息、彙集位置等等。這些數據的共同特性就是它們都是很小的數據,通常以KB爲大小單位。ZooKeeper的服務器和客戶端都被設計爲嚴格檢查並限制每個Znode的數據大小至多1M,但常規使用中應該遠小於此值。
(3) 數據訪問
ZooKeeper中的每個節點存儲的數據要被原子性的操作。也就是說讀操作將獲取與節點相關的所有數據,寫操作也將替換掉節點的所有數據。另外,每一個節點都擁有自己的ACL(訪問控制列表),這個列表規定了用戶的權限,即限定了特定用戶對目標節點可以執行的操作。
(4) 節點類型
ZooKeeper中的節點有兩種,分別爲臨時節點和永久節點。節點的類型在創建時即被確定,並且不能改變。
① 臨時節點:該節點的生命週期依賴於創建它們的會話。一旦會話(Session)結束,臨時節點將被自動刪除,當然可以也可以手動刪除。雖然每個臨時的Znode都會綁定到一個客戶端會話,但他們對所有的客戶端還是可見的。另外,ZooKeeper的臨時節點不允許擁有子節點。
② 永久節點:該節點的生命週期不依賴於會話,並且只有在客戶端顯示執行刪除操作的時候,他們才能被刪除。
(5) 順序節點
當創建Znode的時候,用戶可以請求在ZooKeeper的路徑結尾添加一個遞增的計數。這個計數對於此節點的父節點來說是唯一的,它的格式爲"%10d"(10位數字,沒有數值的數位用0補充,例如"0000000001")。當計數值大於232-1時,計數器將溢出。
(6) 觀察
客戶端可以在節點上設置watch,我們稱之爲監視器。當節點狀態發生改變時(Znode的增、刪、改)將會觸發watch所對應的操作。當watch被觸發時,ZooKeeper將會向客戶端發送且僅發送一條通知,因爲watch只能被觸發一次,這樣可以減少網絡流量。

ZooKeeper服務中操作
在ZooKeeper中有9個基本操作,如下圖所示:
圖ZooKeeper類方法描述
zookeeper 入門

更新ZooKeeper操作是有限制的。delete或setData必須明確要更新的Znode的版本號,我們可以調用exists找到。如果版本號不匹配,更新將會失敗。
更新ZooKeeper操作是非阻塞式的。因此客戶端如果失去了一個更新(由於另一個進程在同時更新這個Znode),他可以在不阻塞其他進程執行的情況下,選擇重新嘗試或進行其他操作。
儘管ZooKeeper可以被看做是一個文件系統,但是處於便利,摒棄了一些文件系統地操作原語。因爲文件非常的小並且使整體讀寫的,所以不需要打開、關閉或是尋地的操作。

Watch觸發器
(1) watch概述
ZooKeeper可以爲所有的讀操作設置watch,這些讀操作包括:exists()、getChildren()及getData()。watch事件是一次性的觸發器,當watch的對象狀態發生改變時,將會觸發此對象上watch所對應的事件。watch事件將被異步地發送給客戶端,並且ZooKeeper爲watch機制提供了有序的一致性保證。理論上,客戶端接收watch事件的時間要快於其看到watch對象狀態變化的時間。
(2) watch類型
ZooKeeper所管理的watch可以分爲兩類:
① 數據watch(data  watches):getData和exists負責設置數據watch
② 孩子watch(child watches):getChildren負責設置孩子watch
可以通過操作返回的數據來設置不同的watch:
① getData和exists:返回關於節點的數據信息
② getChildren:返回孩子列表
因此
① 一個成功的setData操作將觸發Znode的數據watch
② 一個成功的create操作將觸發Znode的數據watch以及孩子watch
③ 一個成功的delete操作將觸發Znode的數據watch以及孩子watch
(3) watch註冊與處觸器
圖 watch設置操作及相應的觸發器如圖下圖所示:
zookeeper 入門

① exists操作上的watch,在被監視的Znode創建、刪除或數據更新時被觸發。
② getData操作上的watch,在被監視的Znode刪除或數據更新時被觸發。在被創建時不能被觸發,因爲只有Znode一定存在,getData操作纔會成功。
③ getChildren操作上的watch,在被監視的Znode的子節點創建或刪除,或是這個Znode自身被刪除時被觸發。可以通過查看watch事件類型來區分是Znode,還是他的子節點被刪除:NodeDelete表示Znode被刪除,NodeDeletedChanged表示子節點被刪除。
Watch由客戶端所連接的ZooKeeper服務器在本地維護,因此watch可以非常容易地設置、管理和分派。當客戶端連接到一個新的服務器 時,任何的會話事件都將可能觸發watch。另外,當從服務器斷開連接的時候,watch將不會被接收。但是,當一個客戶端重新建立連接的時候,任何先前 註冊過的watch都會被重新註冊。
(4) 需要注意的幾點
Zookeeper的watch實際上要處理兩類事件:
① 連接狀態事件(type=None, path=null)
這類事件不需要註冊,也不需要我們連續觸發,我們只要處理就行了。
② 節點事件
節點的建立,刪除,數據的修改。它是one time trigger,我們需要不停的註冊觸發,還可能發生事件丟失的情況。
上面2類事件都在Watch中處理,也就是重載的process(Event event)
節點事件的觸發,通過函數exists,getData或getChildren來處理這類函數,有雙重作用:
① 註冊觸發事件
② 函數本身的功能
函數的本身的功能又可以用異步的回調函數來實現,重載proce esult()過程中處理函數本身的的功能。

ZooKeeper中的時間
ZooKeeper有多種記錄時間的形式,其中包含以下幾個主要屬性:
(1) Zxid
致使ZooKeeper節點狀態改變的每一個操作都將使節點接收到一個Zxid格式的時間戳,並且這個時間戳全局有序。也就是說,也就是說,每個對 節點的改變都將產生一個唯一的Zxid。如果Zxid1的值小於Zxid2的值,那麼Zxid1所對應的事件發生在Zxid2所對應的事件之前。實際 上,ZooKeeper的每個節點維護者三個Zxid值,爲別爲:cZxid、mZxid、pZxid。
① cZxid: 是節點的創建時間所對應的Zxid格式時間戳。
② mZxid:是節點的修改時間所對應的Zxid格式時間戳。
實現中Zxid是一個64爲的數字,它高32位是epoch用來標識leader關係是否改變,每次一個leader被選出來,它都會有一個 新的epoch。低32位是個遞增計數。
(2) 版本號
對節點的每一個操作都將致使這個節點的版本號增加。每個節點維護着三個版本號,他們分別爲:
① version:節點數據版本號
② cversion:子節點版本號
③ aversion:節點所擁有的ACL版本號
圖 Znode節點屬性結構

zookeeper 入門

zookeeper 的應用:
(1) Master啓動
在引入了Zookeeper以後啓動兩個主節點,"主節點-A"和"主節點-B" 啓動以後,都向ZooKeeper去註冊一個節點(znode)。假設"主節點-A"鎖註冊znode是"master-00001", "主節點-B"註冊的節點是"master-00002",註冊完以後進行選舉,編號最小的節點將在選舉中獲勝獲得鎖成爲主節點,也就是"主節點-A"將會獲得鎖成爲主節點,然後"主節點-B"將被阻塞成爲一個備用節點。通過這種方式就完成了對兩個Master進程的調度。
圖 ZooKeeper Master選舉
zookeeper 入門
(2) Master故障
如果"主節點-A"掛了,此時它所註冊的節點將被自動刪除,ZooKeeper會自動感知節點的變化,然後再次發出選舉,此時”主節點-B"將在選舉中獲勝,替代"主節點-A"成爲主節點。
圖 ZooKeeper Master選舉
zookeeper 入門

(3) Master 恢復
圖 ZooKeeper Master選舉
zookeeper 入門

如果主節點恢復了,它會再次向ZooKeeper註冊一個節點(znode),此時它註冊的znode將會是"master-00003",ZooKeeper會感知節點的變化再次發動選舉,這時候"主節點-B"在選舉中會再次獲勝繼續擔任"主節點","主節點-A"會擔任備用節點

zookeeper的配置

[root@vm2 ~]# wget http://mirror.bit.edu.cn/apache/zookeeper/zookeeper-3.4.12/zookeeper-3.4.12.tar.gz
[root@vm2 ~]# tar xfz zookeeper-3.4.12.tar.gz -C /usr/local/
[root@vm2 ~]# cd /usr/local/
[root@vm2 local]# ln -s zookeeper-3.4.12 zookeeper
[root@vm2 conf]# cd zookeeper/conf
[root@vm2 conf]# cp zoo_sample.cfg zoo.cfg

zoo.cfg文件如下:

tickTime = 2000
dataDir =  /opt/zookeeper-3.4.9/data
dataLogDir = /opt/zookeeper-3.4.9/logs
tickTime = 2000
clientPort = 2181
initLimit = 5
syncLimit = 2

server.1=node1:2888:3888
server.2=node2:2888:3888
server.3=node3:2888:3888

說明:
tickTime
tickTime則是上述兩個超時配置的基本單位,例如對於initLimit,其配置值爲5,說明其超時時間爲 2000ms * 5 = 10秒。
dataDir
其配置的含義跟單機模式下的含義類似,不同的是集羣模式下還有一個myid文件。myid文件的內容只有一行,且內容只能爲1 - 255之間的數字,這個數字亦即上面介紹server.id中的id,表示zk進程的id。
dataLogDir
如果沒提供的話使用的則是dataDir。zookeeper的持久化都存儲在這兩個目錄裏。dataLogDir裏是放到的順序日誌(WAL)。而dataDir裏放的是內存數據結構的snapshot,便於快速恢復。爲了達到性能最大化,一般建議把dataDir和dataLogDir分到不同的磁盤上,這樣就可以充分利用磁盤順序寫的特性。
initLimit
ZooKeeper集羣模式下包含多個zk進程,其中一個進程爲leader,餘下的進程爲follower。
當follower最初與leader建立連接時,它們之間會傳輸相當多的數據,尤其是follower的數據落後leader很多。initLimit配置follower與leader之間建立連接後進行同步的最長時間。
syncLimit
配置follower和leader之間發送消息,請求和應答的最大時間長度。
server.id=host:port1:port2
server.id 其中id爲一個數字,表示zk進程的id,這個id也是data目錄下myid文件的內容
host 是該zk進程所在的IP地址
port1 表示follower和leader交換消息所使用的端口
port2 表示選舉leader所使用的端口
在data裏會放置一個myid文件,裏面就一個數字,用來唯一標識這個服務。這個id是很重要的,一定要保證整個集羣中唯一
ZooKeeper會根據這個id來取出server.x上的配置。

[root@vm2 zookeeper]# pwd
/usr/local/zookeeper
[root@vm2 zookeeper]# bin/zkServer.sh start

[root@vm2 zookeeper]# bin/zkCli.sh
[zk: localhost:2181(CONNECTED) 4] ls /
[inspiry, zookeeper, firstZone]
[zk: localhost:2181(CONNECTED) 5] rmr /firstZone
[zk: localhost:2181(CONNECTED) 6] ls /
[inspiry, zookeeper]
[zk: localhost:2181(CONNECTED) 7]
[zk: localhost:2181(CONNECTED) 9] create /firstZnode mydata
Created /firstZnode
[zk: localhost:2181(CONNECTED) 10] get /firstZnode
mydata
cZxid = 0x120000000c
ctime = Mon Jun 11 19:19:36 CST 2018
mZxid = 0x120000000c
mtime = Mon Jun 11 19:19:36 CST 2018
pZxid = 0x120000000c
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 6
numChildren = 0
[zk: localhost:2181(CONNECTED) 11] set /firstZnode "welcome to Inspiry"
cZxid = 0x120000000c
ctime = Mon Jun 11 19:19:36 CST 2018
mZxid = 0x120000000d
mtime = Mon Jun 11 19:20:11 CST 2018
pZxid = 0x120000000c
cversion = 0
dataVersion = 1
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 18
numChildren = 0
[zk: localhost:2181(CONNECTED) 12] get /firstZnode
welcome to Inspiry
cZxid = 0x120000000c
ctime = Mon Jun 11 19:19:36 CST 2018
mZxid = 0x120000000d
mtime = Mon Jun 11 19:20:11 CST 2018
pZxid = 0x120000000c
cversion = 0
dataVersion = 1
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 18
numChildren = 0
[zk: localhost:2181(CONNECTED) 13]
[zk: localhost:2181(CONNECTED) 13] ls /
[inspiry, zookeeper, firstZnode]
[zk: localhost:2181(CONNECTED) 14]

常見的zookeeper web管理界面有zkui、exhibitor,以下步驟是zkui及exhibitor的部署過程。
關於管理界面zkui

[root@meteor ~]# git clone https://github.com/DeemOpen/zkui.git
正克隆到 'zkui'...
remote: Counting objects: 527, done.
remote: Total 527 (delta 0), reused 0 (delta 0), pack-reused 526
接收對象中: 100% (527/527), 478.39 KiB | 300.00 KiB/s, done.
處理 delta 中: 100% (217/217), done.
[root@meteor ~]#
[root@meteor ~]# cd zkui/
[root@meteor zkui]# mvn clean package
[root@meteor zkui]# ls target/
archive-tmp  generated-sources  surefire-reports  zkui-2.0-SNAPSHOT.jar
classes      maven-archiver     test-classes      zkui-2.0-SNAPSHOT-jar-with-dependencies.jar
[root@meteor zkui]#
[root@meteor zkui]# vim config.cfg
[root@meteor zkui]# grep -Pv "^(#|$)" config.cfg
serverPort=9090
zkServer=meteor:2181,vm1:2181,vm2:2181
scmRepo=http://myserver.com/@rev1=
scmRepoPath=//appconfig.txt
ldapAuth=false
ldapDomain=mycompany,mydomain
ldapUrl=ldap://<ldap_host>:<ldap_port>/dc=mycom,dc=com
ldapRoleSet={"users": [{ "username":"domain\\user1" , "role": "ADMIN" }]}
userSet = {"users": [{ "username":"admin" , "password":"admin","role": "ADMIN" },{ "username":"appconfig" , "password":"appconfig","role": "USER" }]}
env=prod
jdbcClass=org.h2.Driver
jdbcUrl=jdbc:h2:zkui
jdbcUser=root
jdbcPwd=manager
loginMessage=Please login using admin/manager or appconfig/appconfig.
sessionTimeout=300
zkSessionTimeout=5
blockPwdOverRest=false
https=false
keystoreFile=/home/user/keystore.jks
keystorePwd=password
keystoreManagerPwd=password
defaultAcl=
X-Forwarded-For=false
[root@meteor zkui]#
[root@meteor zkui]# nohup java -jar target/zkui-2.0-SNAPSHOT-jar-with-dependencies.jar &
[1] 17262
[root@meteor zkui]# nohup: 忽略輸入並把輸出追加到"nohup.out"

[root@meteor zkui]# ls
config.cfg  images           Makefile       nohup.out  README.md  src     zkui.h2.db
docker      LICENSE-2.0.txt  nbactions.xml  pom.xml    run.sh     target  zkui-out.log
[root@meteor zkui]# firewall-cmd --add-port=9090/tcp --perm
success
[root@meteor zkui]# firewall-cmd --reload
success
[root@meteor zkui]#

然後訪問http://192.168.20.221:9090/ ,如下圖所示:
zookeeper 入門
可以在pom.xml文件中添加如下內容:

  <distributionManagement>
      <repository>
          <id>releases</id>
          <url>http://192.168.20.221:8081/repository/maven-releases/</url>
      </repository>
      <snapshotRepository>
          <id>snapshots</id>
          <url>http://192.168.20.221:8081/repository/maven-snapshots/</url>
      </snapshotRepository>
  </distributionManagement>

然後執行deploy,把包存儲到nexus上。
[root@meteor zkui]# mvn deploy

關於管理界面exhibitor

[root@meteor ~]# git clone https://github.com/soabase/exhibitor.git
正克隆到 'exhibitor'...
remote: Counting objects: 10329, done.
remote: Total 10329 (delta 0), reused 0 (delta 0), pack-reused 10329
接收對象中: 100% (10329/10329), 2.22 MiB | 526.00 KiB/s, done.
處理 delta 中: 100% (3826/3826), done.
[root@meteor ~]#
[root@meteor ~]# cd exhibitor/
[root@meteor exhibitor]# ls
CHANGELOG.md  exhibitor-core  exhibitor-standalone  HowToRelease.md  LICENSE.txt  NOTICE.txt  OSSMETADATA  pom.xml  README.md
[root@meteor exhibitor]#
[root@meteor exhibitor]# mvn clean install
[root@meteor exhibitor]# cp exhibitor-standalone/target/exhibitor-standalone-1.7.1-SNAPSHOT.jar /usr/local/src/
[root@meteor exhibitor]# cd /usr/local/src/
[root@meteor src]# ls
exhibitor-standalone-1.7.1-SNAPSHOT.jar
[root@meteor src]# vim start.sh
[root@meteor src]# cat start.sh
#!/bin/bash
JARFILE=exhibitor-standalone-1.7.1-SNAPSHOT.jar
nohup java -jar $JARFILE -c file --port 20001 > nohup.log 2>&1 &
[root@meteor src]# sh start.sh
[root@meteor src]# ls
exhibitor-standalone-1.7.1-SNAPSHOT.jar  nohup.log  start.sh exhibitor.properties
[root@meteor src]#

zookeeper 入門
注:需要在cluster中的所有機器上都部署並啓動exhibitor ,exhibitor還可以監控zookeeper進程的狀態,如果發現zookeeper進程down掉,exhibitor會自動拉起zookeeper進程;而且exhibitor還可以在界面上配置、重啓zookeeper,它是一款非常不錯的zookeeper進程管理程序。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章