Zookeeper客戶端使用與叢集原理

~一角錢發表於2020-10-05

點贊再看,養成習慣,微信搜一搜【一角錢小助手】關注更多原創技術文章。
本文 GitHub org_hejianhui/JavaStudy 已收錄,有我的系列文章。

前言

Zookeeper 系列文章最近會繼續輸出,上一篇我們主要講了Zookeeper的主要概念和特性,介紹單機部署和客戶端主要命令操作以及對節點說明。接下來會對Zookeeper 客戶端 API的使用、叢集部署與特性、典型使用場景實踐以及ZAB一致性協議核心原始碼剖析分幾塊來繼續講一講。

客戶端API常規應用

Zookeeper 提供了 Java 和 C 兩種語言的客戶端。本文學習的是 Java 客戶端。引入最新的maven依賴:

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

知識點:

  1. 初始連線
  2. 建立、檢視節點
  3. 監聽節點
  4. 設定節點許可權
  5. 第三方客戶端ZkClient

初始連線

常規的客戶端類是 org.apache.zookeeper.ZooKeeper,例項化該類之後將會自動與叢集建立連線。構造引數說明如下:

建立、檢視節點

建立節點

通過org.apache.zookeeper.ZooKeeper#create()即可建立節點,其引數說明如下:

檢視節點

通過org.apache.zookeeper.ZooKeeper#getData()即可建立節點,其引數說明如下:

檢視子節點

通過org.apache.zookeeper.ZooKeeper#getChildren()即可獲取子節點,其引數說明如下:

監聽節點

在getData() 與getChildren()兩個方法中可分別設定監聽資料變化和子節點變化。通過設定watch為true,當前事件觸發時會呼叫zookeeper()構建函式中Watcher.process()方法。也可以新增watcher引數來實現自定義監聽。一般採用後者。

:所有的監聽都是一次性的,如果要持續監聽需要觸發後在新增一次監聽。

設定節點ACL許可權

ACL包括結構為 scheme:``id:``permission(有關ACL的介紹參照上一篇《Zookeeper特性與節點說明》

客戶端中由org.apache.zookeeper.data.ACL 類表示,類結構如下:

  1. ACL
  2. Id
  3. scheme // 對應許可權模式scheme
  4. id // 對應模式中的id值
  5. perms // 對應許可權位permission

關於許可權位的表示方式:

每個許可權位都是一個唯一數字,將其合時通過或執行生成一個全新的數字即可

@InterfaceAudience.Public
public interface Perms {
    int READ = 1 << 0;
    int WRITE = 1 << 1;
    int CREATE = 1 << 2;
    int DELETE = 1 << 3;
    int ADMIN = 1 << 4;

    int ALL = READ | WRITE | CREATE | DELETE | ADMIN;
}

第三方客戶端ZkClient

zkClient 是在Zookeeper客戶端基礎之上封裝的,使用上更加友好。主要變化如下:

  • 可以設定持久監聽,或刪除某個監聽
  • 可以插入JAVA物件,自動進行序列化和反序列化
  • 簡化了基本的增刪改查操作。
<!-- https://mvnrepository.com/artifact/com.101tec/zkclient -->
<dependency>
	<groupId>com.101tec</groupId>
	<artifactId>zkclient</artifactId>
	<version>0.11</version>
</dependency>

Zookeeper叢集

知識點:

  1. 叢集部署
  2. 叢集角色說明
  3. 選舉機制
  4. 資料同步機制
  5. 四字運維命令

Zookeeper叢集的目的是為了保證系統的效能承載更多的客戶端連線設專門提供的機制。通過叢集可以實現以下功能:

  • 讀寫分離:提高承載,為更多的客戶端提供連線,並保障效能。
  • 主從自動切換:提高服務容錯性,部分節點故障不會影響整個服務叢集。

半數以上執行機制說明:

叢集至少需要三臺伺服器,並且強烈建議使用奇數個伺服器。因為zookeeper 通過判斷大多數節點的存活來判斷整個服務是否可用。比如3個節點,掛掉了2個表示整個叢集掛掉,而用偶數4個,掛掉了2個也表示其並不是大部分存活,因此也會掛掉。

叢集部署

配置語法

server.<節點ID>=:<資料同步埠>:<選舉埠>
  • 節點ID:服務id手動指定1至125之間的數字,並寫到對應服務節點的 {dataDir}/myid 檔案中。
  • IP地址:節點的遠端IP地址,可以相同。但生產環境就不能這麼做了,因為在同一臺機器就無法達到容錯的目的。所以這種稱作為偽叢集。
  • 資料同步埠:主從同時資料複製埠,(做偽叢集時埠號不能重複)。
  • 選舉埠:主從節點選舉埠,(做偽叢集時埠號不能重複)。

配置檔案示例:

tickTime=2000
dataDir=/var/lib/zookeeper/
clientPort=2181
initLimit=5
syncLimit=2
#以下為叢集配置,必須配置在所有節點的zoo.cfg檔案中
server.1=zoo1:2888:3888
server.2=zoo2:2888:3888
server.3=zoo3:2888:3888

叢集配置流程

  1. 分別建立3個data目錄用於儲存各節點資料
mkdir data
mkdir data/1
mkdir data/3
mkdir data/3
  1. 編寫myid檔案
echo 1 > data/1/myid
echo 3 > data/3/myid
echo 2 > data/2/myid
  1. 編寫配置檔案

conf/zoo1.cfg

tickTime=2000
initLimit=10
syncLimit=5
dataDir=data/1
clientPort=2181
#叢集配置
server.1=127.0.0.1:2887:3887
server.2=127.0.0.1:2888:3888
server.3=127.0.0.1:2889:3889

conf/zoo2.cfg

tickTime=2000
initLimit=10
syncLimit=5
dataDir=data/2
clientPort=2182
#叢集配置
server.1=127.0.0.1:2887:3887
server.2=127.0.0.1:2888:3888
server.3=127.0.0.1:2889:3889

conf/zoo3.cfg

tickTime=2000
initLimit=10
syncLimit=5
dataDir=data/3
clientPort=2183
#叢集配置
server.1=127.0.0.1:2887:3887
server.2=127.0.0.1:2888:3888
server.3=127.0.0.1:2889:3889

4.分別啟動

./bin/zkServer.sh start conf/zoo1.cfg
./bin/zkServer.sh start conf/zoo2.cfg
./bin/zkServer.sh start conf/zoo3.cfg

5.分別檢視狀態

./bin/zkServer.sh status conf/zoo1.cfg
Mode: follower
./bin/zkServer.sh status conf/zoo2.cfg
Mode: leader
./bin/zkServer.sh status conf/zoo3.cfg
Mode: follower

檢查叢集複製情況

  1. 分別連線指定節點

zkCli.sh 後加引數-server 表示連線指定IP與埠。

./bin/zkCli.sh -server 127.0.0.1:2181
./bin/zkCli.sh -server 127.0.0.1:2182
./bin/zkCli.sh -server 127.0.0.1:2183
  • 任意節點中建立資料,檢視其它節點已經同步成功。

注意: -server引數後同時連線多個服務節點,並用逗號隔開 127.0.0.1:2181,127.0.0.1:2182

叢集角色說明

zookeeper 叢集中總共有三種角色,分別是leader(主節點)follower(子節點) observer(次級子節點)

observer配置:
只要在叢集配置中加上observer字尾即可,示例如下:

server.3=127.0.0.1:2889:3889:observer

選舉機制

通過 ./bin/zkServer.sh status <zoo配置檔案> 命令可以檢視到節點狀態

./bin/zkServer.sh status conf/zoo1.cfg
Mode: follower
./bin/zkServer.sh status conf/zoo2.cfg
Mode: leader
./bin/zkServer.sh status conf/zoo3.cfg
Mode: follower

可以發現中間的2182 是leader狀態.其選舉機制如下圖:

投票機制說明

  • 第一輪投票全部投給自己
  • 第二輪投票給myid比自己大的相鄰節點
  • 如果得票超過半數,選舉結束。

Zookeeper預設採用了FastLeaderElection演算法,且投票數大於半數則勝出的機制;

選舉觸發

當叢集中的伺服器出現以下兩種情況時會進行Leader的選舉

  1. 服務節點初始化啟動(全新叢集選舉)
  2. 半數以上的節點無法和Leader建立連線

當節點初始起動時會在叢集中尋找Leader節點,如果找到則與Leader建立連線,其自身狀態變化followerobserver。如果沒有找到Leader,當前節點狀態將變化LOOKING,進入選舉流程。

在叢集執行期間如果有follower或observer節點當機只要不超過半數並不會影響整個叢集服務的正常執行。但如果leader當機,將暫停對外服務,所有follower將進入LOOKING 狀態,進入選舉流程。

全新叢集選舉:

全新叢集選舉是新搭建起來的,沒有資料ID和邏輯時鐘資料影響叢集的選舉;

假設當前叢集有5臺伺服器,它們的編號為 1 – 5,按編號依次啟動 Zookeeper 服務:

  1. 伺服器1啟動,首先會給自己投1票,由於其他機器還沒有啟動,所以它還無法接收到投票資訊的反饋;因此伺服器1一直處於 LOOKING 狀態;
  2. 伺服器2啟動,首先給自己投1票;發起投票對比,這是它會與伺服器1交換結果,由於伺服器2的編號大,所以伺服器2勝出,此時伺服器1將票投給伺服器2,伺服器2的票數沒有大於叢集半數,所以伺服器1、2兩個的狀態都是 LOOKING;
  3. 伺服器3啟動,首先給自己投1票;與之前的伺服器1、2交換結果,由於伺服器3的編號最大所以伺服器3勝出,那麼伺服器1、2都會將票投給伺服器3,此時伺服器3的票數為3,正好大於半數(5/2);所以伺服器3成為 leader,服務1、2成為 follower
  4. 伺服器4啟動,首先給自己投1票;與之前的伺服器1、2、3交換結果,發現已經產生了 leader,則伺服器4直接為 follower;
  5. 伺服器5啟動和伺服器4一樣,也成為 follower;

非全新叢集選舉

對於正常執行的 Zookeeper 叢集,一旦中途有伺服器當機(leader),則需要重新選舉時,選舉的時候就要引入伺服器ID、資料ID(zxid)和邏輯時鐘(投票次數,每一輪投票晚上,投票次數都會增加)。

  1. 首先統計邏輯時鐘是否相同,邏輯時鐘小,則說明途中存在當機問題。因此該資料不完整,那麼選舉結果被忽略,重新投票選舉;
  2. 統一邏輯時鐘之後,對比資料ID值,資料ID反映了資料的新舊程度,因此資料ID大的節點勝出;
  3. 如果邏輯時鐘和資料ID值都相同,那麼就去比較資料ID,資料ID大的勝出;

簡單的講,非全新叢集選舉時是優中選優,保證 Leader 是 Zookeeper 叢集中資料最完整、最可靠的一臺伺服器。

在叢集執行期間如果有follower或observer節點當機,只要不超過半數並不會影響整個叢集服務的正常執行。但如果leader當機,將暫停對外服務,所有follower將進入LOOKING 狀態,進入選舉流程。

資料同步機制

Zookeeper 的資料同步是為了保證各節點中資料的一致至性,同步時涉及兩個流程,一個是正常的客戶端資料提交,另一個是叢集某個節點當機在恢復後的資料同步。

客戶端寫入請求

寫入請求的大致流程是,收leader接收客戶端寫請求,並同步給各個子節點。如下圖:

但實際情況要複雜的多,比如client 它並不知道哪個節點是leader 有可能寫的請求會發給follower ,由follower在轉發給leader進行同步處理

客戶端寫入流程說明:

  1. client向zk中的server傳送寫請求,如果該server不是leader,則會將該寫請求轉發給leader server,leader將請求事務以proposal形式分發給follower;
  2. 當follower收到收到leader的proposal時,根據接收的先後順序處理proposal;
  3. 當Leader收到follower針對某個proposal過半的ack後,則發起事務提交,重新發起一個commit的proposal
  4. Follower收到commit的proposal後,記錄事務提交,並把資料更新到記憶體資料庫;
  5. 當寫成功後,反饋給client。

服務節點初始化同步

在叢集執行過程當中如果有一個follower節點當機,由於當機節點沒過半,叢集仍然能正常服務。當leader 收到新的客戶端請求,此時無法同步給當機的節點。造成資料不一致。為了解決這個問題,當節點啟動時,第一件事情就是找當前的Leader,比對資料是否一致。不一致則開始同步,同步完成之後在進行對外提供服務。

如何比對Leader的資料版本呢?這裡通過ZXID事物ID來確認。比Leader就需要同步。

ZXID說明

ZXID是一個長度64位的數字,其中低32位是按照數字遞增,任何資料的變更都會導致,低32位的數字簡單加1。高32位是leader週期編號,每當選舉出一個新的leader時,新的leader就從本地事物日誌中取出ZXID,然後解析出高32位的週期編號,進行加1,再將低32位的全部設定為0。這樣就保證了每次新選舉的leader後,保證了ZXID的唯一性而且是保證遞增的。

思考題:
如果leader 節點當機,在恢復後它還能被選為leader嗎?

四字運維命令

ZooKeeper響應少量命令。每個命令由四個字母組成。可通過telnet或nc向ZooKeeper發出命令。

這些命令預設是關閉的,需要配置4lw.commands.whitelist來開啟,可開啟部分或全部示例如下:

#開啟指定命令
4lw.commands.whitelist=stat, ruok, conf, isro
#開啟全部
4lw.commands.whitelist=*

安裝Netcat工具,已使用nc命令

#安裝Netcat 工具
yum install -y nc
#檢視伺服器及客戶端連線狀態
echo stat | nc localhost 2181

: mac os 10.13 後就沒有內建telnet函式,要新安裝:

brew install telnet

常用命令

echo stat|nc 127.0.0.1 2181 檢視哪個節點被選擇作為follower或者leader
echo ruok|nc 127.0.0.1 2181 測試是否啟動了該Server,若回覆imok表示已經啟動。
echo dump| nc 127.0.0.1 2181 ,列出未經處理的會話和臨時節點。
echo kill | nc 127.0.0.1 2181 ,關掉server
echo conf | nc 127.0.0.1 2181 ,輸出相關服務配置的詳細資訊。
echo cons | nc 127.0.0.1 2181 ,列出所有連線到伺服器的客戶端的完全的連線 / 會話的詳細資訊。
echo envi |nc 127.0.0.1 2181 ,輸出關於服務環境的詳細資訊(區別於 conf 命令)。
echo reqs | nc 127.0.0.1 2181 ,列出未經處理的請求。
echo wchs | nc 127.0.0.1 2181 ,列出伺服器 watch 的詳細資訊。
echo wchc | nc 127.0.0.1 2181 ,通過 session 列出伺服器 watch 的詳細資訊,它的輸出是一個與 watch 相關的會話的列表。
echo wchp | nc 127.0.0.1 2181 ,通過路徑列出伺服器 watch 的詳細資訊。它輸出一個與 session 相關的路徑。

命令列表

  1. conf:3.3.0中的新增功能:列印有關服務配置的詳細資訊。
  2. cons:3.3.0中的新增功能:列出了連線到該伺服器的所有客戶端的完整連線/會話詳細資訊。包括有關已接收/已傳送的資料包數量,會話ID,操作等待時間,最後執行的操作等資訊。
  3. crst:3.3.0中的新增功能:重置所有連線的連線/會話統計資訊。
  4. dump:列出未完成的會話和臨時節點。這僅適用於領導者。
  5. envi:列印有關服務環境的詳細資訊
  6. ruok:測試伺服器是否以非錯誤狀態執行。如果伺服器正在執行,它將以imok響應。否則,它將完全不響應。響應“ imok”不一定表示伺服器已加入仲裁,只是伺服器程式處於活動狀態並繫結到指定的客戶端埠。使用“ stat”獲取有關狀態仲裁和客戶端連線資訊的詳細資訊。
  7. srst:重置伺服器統計資訊。
  8. srvr:3.3.0中的新功能:列出伺服器的完整詳細資訊。
  9. stat:列出伺服器和連線的客戶端的簡要詳細資訊。
  10. wchs:3.3.0中的新增功能:列出有關伺服器監視的簡要資訊。
  11. wchc:3.3.0中的新增功能:按會話列出有關伺服器監視的詳細資訊。這將輸出具有相關監視(路徑)的會話(連線)列表。請注意,根據手錶的數量,此操作可能會很昂貴(即影響伺服器效能),請小心使用。
  12. dirs:3.5.1中的新增功能:以位元組為單位顯示快照和日誌檔案的總大小
  13. wchp:3.3.0中的新增功能:按路徑列出有關伺服器監視的詳細資訊。這將輸出具有關聯會話的路徑(znode)列表。請注意,根據手錶的數量,此操作可能會很昂貴(即影響伺服器效能),請小心使用。
  14. mntr:3.4.0中的新增功能:輸出可用於監視叢集執行狀況的變數列表。

Zookeeper 系列文章,敬請關注,下篇講講典型使用場景實踐以及ZAB一致性協議!

部分圖片來源於網路,版權歸原作者,侵刪。

相關文章