Zookeeper集群搭建及原理


1 概述

1.1 簡介

ZooKeeper 是 Apache 的一個頂級項目,為分布式應用提供高效、高可用的分布式協調服務,提供了諸如數據發布/訂閱、負載均衡、命名服務、分布式協調/通知和分布式鎖等分布式基礎服務。由於 ZooKeeper 便捷的使用方式、卓越的性能(基於內存)和良好的穩定性,被廣泛地應用於諸如 Hadoop、HBase、Kafka 和 Dubbo 等大型分布式系統中。
官方地址:https://zookeeper.apache.org

1.2 角色

領導者(leader):負責進行投票的發起和決議,更新系統狀態。
跟隨者(follower):用於接收客戶端請求並給客戶端返回結果,在選主過程中進行投票。
觀察者(observer):可以接受客戶端連接,將寫請求轉發給 leader,但是observer 不參加投票的過程,只是為了擴展系統,提高讀取的速度。

1.3 節點特性

ZooKeeper 節點的生命周期取決於節點的類型。在 ZooKeeper 中,節點根據持續時間可以分為持久節點(PERSISTENT)臨時節點(EPHEMERAL),根據是否有序可以分為順序節點(SEQUENTIAL)、和無序節點(默認是無序的)。每個客戶端連接zookeeper會產生一個session,客戶端連接關閉時session也會消失
持久節點一旦被創建,除非主動移除,不然一直會保存在 Zookeeper 中(不會因為創建該節點的客戶端的會話失效而消失)。

1.4 數據模型

層次化的目錄結構,命名符合常規文件系統規范,類似於文件目錄。
每個節點在 Zookeeper 中叫做 Znode,並且其有一個唯一的路徑標識。
每個節點中數據存儲不能超過1M(不要把Zookeeper當數據庫用)。

1.5 特性

順序一致性。客戶端的更新將按發送順序應用。
原子性。要么成功要失敗。沒有中間狀態(最終一致性)。
單個系統映像。由於zookeeper使用復制集群,無論客戶端連接哪個節點都能看到相同的數據。
可靠性。即所有節點支持持久化。
及時性。存儲在節點中的數據會在較短時間內及時同步。

1.6 CAP

任何分布式架構都不能同時滿足C(一致性)、A(可用性)、P(分區容錯性)。zookeeper集群在保證一致性的同時,在A和P之間做了取舍,最終選擇了P,因此可用性差一點。

2 zookeeper集群搭建

2.1 網絡拓撲

2.2 環境准備

CentOS7 * 4
JDK8
Zookeeper3.6.3

2.3 安裝

在q101服務器中配置好后發送到其他服務器。q101操作如下:

wget https://mirrors.tuna.tsinghua.edu.cn/apache/zookeeper/zookeeper-3.6.3/apache-zookeeper-3.6.3-bin.tar.gz --no-check-certificate

tar xf apache-zookeeper-3.6.3-bin.tar.gz 
mv apache-zookeeper-3.6.3-bin /usr/local/zookeepe
cd /usr/local/zookeeper/conf
mv zoo_sample.cfg zoo.cfg
vi zoo.cfg 
# 1. 修改dataDir路徑為“/var/zookeeper”
# 2. 增加集群節點信息,信息如下
server.4=192.168.88.101:2888:3888
server.3=192.168.88.102:2888:3888
server.2=192.168.88.103:2888:3888
server.1=192.168.88.104:2888:3888
# 3. 保存退出

# 創建配置文件中設置的持久化目錄
mkdir /var/zookeeper

# 將節點信息中的節點ID寫如myid中
echo 4 > /var/zookeeper/myid

# 修改環境變量 增加zookeeper信息
# export ZOOKEEPER_HOME=/usr/local/zookeeper
# 在export PATH末尾追加 :$ZOOKEEPER_HOME/bin
 vi /etc/profile
 source /etc/profile

詳細配置說明

# leader和follower心跳時間,用於維護節點是否存活。單位毫秒
tickTime=2000

# 初始延遲。當follower追隨leader時,leader允許初始延遲時間。
# 用tickTime乘以initLimit,此處為2000*10即20秒
initLimit=10

# 數據同步時間,及超過時間數據同步失敗。
# 用tickTime乘以syncLimit,此處為2000*5即10秒
syncLimit=5

# 持久化目錄,不建議存在tmp下
dataDir=/var/zookeeper

# 客戶端連接zookeeper端口號
clientPort=2181

# 允許客戶端連接最大連接數
#maxClientCnxns=60

# dataDir中保留快照數量
#autopurge.snapRetainCount=3

# 清除任務時間間隔,單位小時。0表示禁用自動清楚功能
#autopurge.purgeInterval=1

# 自定義監控
#metricsProvider.className=org.apache.zookeeper.metrics.prometheus.PrometheusMetricsProvider
#metricsProvider.httpPort=7000
#metricsProvider.exportJvmInfo=true

# 此處為自己添加的配置。集群信息,投票過半數量=(集群節點行數 除以 2)+1
# 數字1,2,3,4為節點ID,用於謙讓出leader,投票過半后數字最大的為leader
# 端口說明:當leader掛掉后(或第一次啟動沒有leader時候)其他節點通過3888建立連接進行投票選出leader,
#                選中leader后,leader會通過2888端口來與follower進行工作
server.4=192.168.88.101:2888:3888
server.3=192.168.88.102:2888:3888
server.2=192.168.88.103:2888:3888
server.1=192.168.88.104:2888:3888

將q101相關文件傳到q102並修改myid

scp -r /usr/local/zookeeper 192.168.88.102:/usr/local/
scp -r /var/zookeeper 192.168.88.102:/var/
scp /etc/profile 192.168.88.102:/etc

# 切換到q102服務器執行,數字與配置文件中的節點ID數據一致
echo 3 > /var/zookeeper/myid
# q102執行 使環境變量生效
source /etc/profile

將q101相關文件傳到q103並修改myid

scp -r /usr/local/zookeeper 192.168.88.103:/usr/local/
scp -r /var/zookeeper 192.168.88.102:/var/
scp /etc/profile 192.168.88.103:/etc

# 切換到q103服務器執行
echo 2 > /var/zookeeper/myid
source /etc/profile

將q101相關文件傳到q104並修改myid

scp -r /usr/local/zookeeper 192.168.88.103:/usr/local/
scp -r /var/zookeeper 192.168.88.102:/var/
scp /etc/profile 192.168.88.104:/etc

# 切換到q104服務器執行
echo 1 > /var/zookeeper/myid
source /etc/profile

2.4 啟動

為了測試我們采用前台啟動,4台節點分別執行

zkServer.sh start-foreground

啟動前3台的時候會發現報錯,連接其他節點失敗,屬於正常情況,當啟動最后一台的時候發現沒有發錯了。原因是啟動前2台的時候沒有leader。當第3台啟動完成的時候,此時節點數量已經超過半數,所以開始根據zoo.cfg配置文件中的服務ID進行選舉,因為q101服務器ID 設置的4,按節點謙讓規則q101為leader。
啟動錯誤信息如下:

節點角色驗證
在q101執行

zkServer.sh status

3 zookeeper常用功能

3.1 常用命令

啟動關閉
后台啟動:zkServer.sh start
前台啟動:zkServer.sh start-foreground
停止:zkServer.sh stop
重啟:zkServer.sh restart
查看版本:zkServer.sh version
查看狀態:zkServer.sh status

連接與退出
連接zookeeper客戶端:zkCli.sh -server 127.0.0.1:2181 連接本機可以直接輸入zkCli.sh
退出zookeeper客戶端:quit

操作命令
查看zookeeper所有命令:連接到zookeeper客戶端后輸入help
查看根目錄下包含的節點:ls /
查看節點狀態信息:ls2 / 或者使用 ls -s /

創建一個非順序的持久化節點:create [-s] [-e] path data acl 比如 create /test test-1
創建一個臨時節點:create -e /test/tmp tem-data
創建一個順序節點:create -s /test/aaa aaa-data
刪除一個節點:delete /test

[zk: localhost:2181(CONNECTED) 0] ls /
[zookeeper]
[zk: localhost:2181(CONNECTED) 1] create /abc
Created /abc
[zk: localhost:2181(CONNECTED) 3] ls /
[abc, zookeeper]
# 創建節點后默認數據為null
[zk: localhost:2181(CONNECTED) 9] get /abc
null
[zk: localhost:2181(CONNECTED) 4] create /abc/abcd
Created /abc/abcd
[zk: localhost:2181(CONNECTED) 15] set /abc/abcd hello
[zk: localhost:2181(CONNECTED) 16] get /abc/abcd 
hello

3.2 stat結構

Zookeeper每個znode都有一個與之關聯的stat結構,類似於Unix/Linux文件系統中文件的stat結構。通過stat /xxx/xxx查看

[zk: localhost:2181(CONNECTED) 17] stat /abc/abcd
cZxid = 0x100000003
ctime = Wed Mar 02 23:41:34 CST 2022
mZxid = 0x100000006
mtime = Wed Mar 02 23:43:32 CST 2022
pZxid = 0x100000003
cversion = 0
dataVersion = 2
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 5
numChildren = 0

字段含義
cZxid:創建節點znode時的事務ID由leader維護的遞增計數器,共64位,其中前32為表示leader紀元,即leader革新換代次數,比如上面的0x1表示目前leader為做變更,如當前leader關閉后重新選舉的leader此處會變成0x2,並且后32位的事務ID重新開始;后32為表示所有的增刪改操作次數。
mZxid:修改節點znode更改的事務ID。
pZxid:添加或刪除子節點znode更改的事務ID。
ctime:表示從1970-01-01T00:00:00Z開始以毫秒為單位的znode創建時間。
mtime:表示從1970-01-01T00:00:00Z開始以毫秒為單位的znode最近修改時間。
dataVersion:表示對該znode的數據所做的更改次數。
cversion:表示對此znode的子節點進行的更改次數。
aclVersion:版本號,即znode的ACL進行更改的次數。
ephemeralOwner:如果znode是ephemeral類型節點,則這是znode所有者的 session ID。 如果znode不是ephemeral節點,則該字段設置為零。
dataLength:這是znode數據字段的長度。
numChildren:這表示znode的子節點的數量。

3.3 臨時節點

臨時節點伴隨着session會話,當會話結束后臨時節點銷毀

  1. 通過q101連接zookeeper客戶端查看日志

通過日志發現sessionID為:0x400000595cc0001
2. 創建臨時節點 create -e /xxxx

create -e /aaa
  1. 通過stat查看發現剛創建的臨時節點“bbbb”的ephemeralOwner正是連接時候的sessionID

    當使用另一台服務器q102連接zkCli后查看aaa節點stat sessionID依舊是q101連接的sessionID。

3.4 持久序列

通過create -s path data可以創建持久序列,znode被創建后,znode名稱會自動添加一個編號,編號會自動遞增。編號的遞增和節點名稱無關.

編號的遞增不會因為斷開而重置,也不會因為zookeeper重啟而重置

4 zookeeper原理

4.1 Paxos算法

它是一個基於消息傳遞的一致性算法,Leslie Lamport在1990年提出,近幾年被廣泛應用於分布式計算中,Google的Chubby,Apache的Zookeeper都是基於它的理論來實現的,Paxos還被認為是到目前為止唯一的分布式一致性算法,其它的算法都是Paxos的改進或簡化。有個問題要提一下,Paxos有一個前提:Paxos只有在一個可信的計算環境中才能成立,這個環境是不會被入侵所破壞的。

關於Paxos的具體描述可以參考:https://baike.baidu.com/item/Paxos算法

4.2 ZAB協議

ZAB(ZooKeeper Atomic broadcast)即ZooKeeper原子消息廣播協議,類似於一個二階段提交過程(2PC)屬於最終一致性。是zookeeper基於Paxos算法的簡化實現。

所有事務請求必須由一個全局唯一的服務器來協調處理,這樣的服務器被稱為 Leader服務器,而余下的其他服務器則成為 Follower服務器。 Leader服務器負責將一個客戶端事務請求轉換成一個事務 Proposal(提議),並將該 Proposal分發給集群中所有的Follower服務器。之后 Leader服務器需要等待所有 Follower服務器的反饋,一旦超過半數的 Follower服務器進行了正確的反饋后,那么 Leader就會再次向所有的 Follower服務器分發 Commit消息,要求其將前一個 Proposal進行提交。

上圖說明:
1:Client向Follower發出寫操作;
2:Follower將收到的寫請求轉發給Leader處理;
3:Leader收到請求后分配一個全局單調遞增的唯一的事務ID(即ZXID,按其先后順序來進行排序與處理);
4.1:Leader服務器會為每一個 Follower服務器都各自分配一個單獨的隊列,然后將需要廣播的事務Proposal依次放入這些隊列中去,並且根據FIFO策略進行消息發送;每一個 Follower服務器在接收到這個事務 Proposal之后,都會首先將其以事務日志的形式寫入到本地磁盤中去,並且在成功寫入后反饋給 Leader服務器個Ack響應。Leader自己也會將事務日志寫入磁盤;
4.2:當 Leader服務器接收到超過半數Follower的Ack響應后,就會廣播一個Commit消息給所有的 Follower服務器以通知其進行事務提交(寫入內存),同時 Leader自身也會完成對事務的提交。
5:由Leader將結果返回給Client1

上圖中4.1和4.2步驟,由於Leader自身也計一票,如果Follower2 由於網絡等原因沒有給Leader Ack響應,但Leader和Follower 1 兩票已超過半數,所以結果依然會成功。此時Client2請求Follower2獲取“/aaa”數據時,可以通過sync 讓Follower 2向Leader同步后再返回數據。

4.3 選舉機制

4.3.1 節點網絡

基於上面的第2章集群搭建,查看每台節點網絡連接情況

yum install net-tools
netstat -natp | egrep '(2888|3888)'

查看結果如下:

# q101信息
[root@q101 ~]# netstat -natp | egrep '(2888|3888)'
tcp6       0      0 192.168.88.101:3888     :::*                    LISTEN      1924/java           
tcp6       0      0 192.168.88.101:58052    192.168.88.102:3888     ESTABLISHED 1924/java           
tcp6       0      0 192.168.88.101:39738    192.168.88.103:3888     ESTABLISHED 1924/java           
tcp6       0      0 192.168.88.101:54174    192.168.88.102:2888     ESTABLISHED 1924/java           
tcp6       0      0 192.168.88.101:47726    192.168.88.104:3888     ESTABLISHED 1924/java 

# q102信息
[root@q102 ~]#  netstat -natp | egrep '(2888|3888)'
tcp6       0      0 192.168.88.102:2888     :::*                    LISTEN      1661/java           
tcp6       0      0 192.168.88.102:3888     :::*                    LISTEN      1661/java           
tcp6       0      0 192.168.88.102:3888     192.168.88.101:58052    ESTABLISHED 1661/java           
tcp6       0      0 192.168.88.102:2888     192.168.88.103:38244    ESTABLISHED 1661/java           
tcp6       0      0 192.168.88.102:50104    192.168.88.104:3888     ESTABLISHED 1661/java           
tcp6       0      0 192.168.88.102:2888     192.168.88.104:48034    ESTABLISHED 1661/java           
tcp6       0      0 192.168.88.102:2888     192.168.88.101:54174    ESTABLISHED 1661/java           
tcp6       0      0 192.168.88.102:37466    192.168.88.103:3888     ESTABLISHED 1661/java  

# q103信息
[root@q103 ~]#  netstat -natp | egrep '(2888|3888)'
tcp6       0      0 192.168.88.103:3888     :::*                    LISTEN      1415/java           
tcp6       0      0 192.168.88.103:38244    192.168.88.102:2888     ESTABLISHED 1415/java           
tcp6       0      0 192.168.88.103:3888     192.168.88.101:39738    ESTABLISHED 1415/java           
tcp6       0      0 192.168.88.103:3888     192.168.88.102:37466    ESTABLISHED 1415/java           
tcp6       0      0 192.168.88.103:58344    192.168.88.104:3888     ESTABLISHED 1415/java  

# q104信息
[root@q104 ~]#  netstat -natp | egrep '(2888|3888)'
tcp6       0      0 192.168.88.104:3888     :::*                    LISTEN      1333/java           
tcp6       0      0 192.168.88.104:3888     192.168.88.101:47726    ESTABLISHED 1333/java           
tcp6       0      0 192.168.88.104:3888     192.168.88.102:50104    ESTABLISHED 1333/java           
tcp6       0      0 192.168.88.104:3888     192.168.88.103:58344    ESTABLISHED 1333/java           
tcp6       0      0 192.168.88.104:48034    192.168.88.102:2888     ESTABLISHED 1333/java 

通過上面的網絡信息整體網絡連接圖如下

上圖說明:
zookeeper Leader和Follower連接主要通過2888和3888,2888主要用於leader與follower進行工作,3888用於Leader選舉。每個zookeeper節點都會與其他節點建立連接。

4.3.2 選舉流程

情況一:第一次啟動
以上面我們搭好的4台節點為例,myid和zxid情況如下:

節點名稱 myid zxid
q101 4 0
q102 3 0
q103 2 0
q104 1 0

選舉前提當投票數量超過半數時才有效,此處4台節點,當第一次啟動時候zxid都為0。只要有3台節點啟動就可以選舉出Leader。雖然說q101的myid=4但是如果最后啟動q101,那么第一次啟動時一定是q102成為Leader。

情況二:zxid不為0時,當集群重啟,或Leader掛了的時候
當Leader掛了后,zookeeper集群會進行推選制選舉,會優先選舉數據最全的節點作為Leader(通過比較zxid,zxid越大代表該節點數據最全),如果zxid相同再比較myid。

節點名稱 myid zxid
q101 4 0x100000009
q103 2 0x100000010
q104 1 0x100000010

原Leader q102掛掉后無論哪個節點先發現Leader掛掉,都會推選zxid最大的節點,q103和q104 zxid 都為xxx10再比較myid。所以此處q103最終會被選為新的Leader。

5 watch

  1. Watch是輕量級的,其實就是本地JVM的Callback,服務器端只是存了是否有設置了Watcher的布爾類型。
  2. 在服務端,在FinalRequestProcessor處理對應的Znode操作時,會根據客戶端傳遞的watcher變量,添加到對應的ZKDatabase(org.apache.zookeeper.server.ZKDatabase)中進行持久化存儲,同時將自己NIOServerCnxn做為一個Watcher callback,監聽服務端事件變化
  3. Leader通過投票通過了某次Znode變化的請求后,然后通知對應的Follower,Follower根據自己內存中的zkDataBase信息,發送notification信息給zookeeper客戶端。
  4. Zookeeper客戶端接收到notification信息后,找到對應變化path的watcher列表,挨個進行觸發回調。

6 zookeeper實現分布式鎖

6.1 實現原理

多個節點同時在一個指定的節點下面創建臨時會話順序節點,誰創建的節點序號最小,誰就獲得了鎖,並且其他節點就會監聽序號比自己小的節點,一旦序號比自己小的節點被刪除了,其他節點就會得到相應的事件,然后查看自己是否為序號最小的節點,如果是,則獲取鎖。


免責聲明!

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



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