Zookeeper的選舉機制和同步機制超詳細講解,面試經常問到!

鉑賽東發表於2021-10-08

前言

zookeeper相信大家都不陌生,很多分散式中介軟體都利用zk來提供分散式一致性協調的特性。dubbo官方推薦使用zk作為註冊中心,zk也是hadoop和Hbase的重要元件。其他知名的開源中介軟體中也都出現了zk的身影。

有很多童鞋認識zk很久了,知道其基本理念,知道如何使用。但當面試時問到叢集zk之間的選舉和資料同步機制時,就陷入了盲區。

其實很多的分散式中介軟體的選舉和同步,都和zk有異曲同工之妙。這篇文章我就來重點聊下關於zk叢集之間的選舉和同步機制。

ZK叢集的部署

首先我們來看下半數執行機制:

叢集至少需要三臺伺服器,且官方文件強烈建議使用奇數個伺服器,因為zookeeper是通過判斷大多數節點的的存活來判斷整個服務叢集是否可用的,比如3個節點,它的一半是1.5,向上取整就是2。掛掉了2個就是表示整個叢集掛掉。而用偶數4個的話,掛掉2個也表示不是大部分存活,因此也會掛掉。所以用4臺伺服器的話 ,從使用資源上來說,並不划算。

配置語法:

server.<節點ID>=<IP>:<資料同步埠>:<選舉埠>
  • 節點ID:為1到125之間的數字,寫到對應服務節點的{dataDir}/myid檔案中。
  • IP地址:節點的遠端IP地址,可以相同,生產環境建議用不同的機器,否則無法達到容錯的目的。
  • 資料同步埠:主從同步時資料複製埠。
  • 選舉埠:主從節點選舉埠。

假設現在有3個zookeeper節點,我們要為其編寫config配置,也要編寫3份,分別放在不同的伺服器上,其配置如下:

initLimit=10
syncLimit=5
dataDir=/data/zk_data
clientPort=2181
# 叢集配置
server.1=192.168.1.1:2888:3888
server.2=192.168.1.2:2888:3888
server.3=192.168.1.3:2888:3888

其中dataDir引數指定的是一個目錄,用來存放zk的資料,裡面有個檔案myid,3臺機器上myid檔案裡面分別存放1,2,3。對應各自節點ID。

配置好3個配置檔案後,分別啟動,這樣我們一個3個節點的叢集zookeeper就搭建好了。

./bin/zkServer.sh start conf/zoo.cfg

ZK叢集中的角色

zookeeper叢集中公共有三種角色,分別是leaderfollowerobserver

角色描述
leader主節點,又名領導者。用於寫入資料,通過選舉產生,如果當機將會選舉新的主節點。
follower子節點,又名追隨者。用於實現資料的讀取。同時他也是主節點的備選節點,並擁有投票權。
observer次級子節點,又名觀察者。用於讀取資料,與follower區別在於沒有投票權,不能被選為主節點。並且在計算叢集可用狀態時不會將observer計算入內。

關於observer的配置:

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

server.3=127.0.0.1:2883:3883:observer

其中leader只有一個,剩下的都是follower和observer,但是我們一般生產上不會配置observer,因為observer並沒有選舉權,可以理解為observer是一個臨時工,不是正式員工,沒法獲得晉升。除此之外,它和follower的功能是一樣的。

什麼時候需要用到observer呢,因為zk一般讀的請求會大於寫。當整個叢集壓力過大時,我們可以增加幾個臨時工observer來獲得效能的提升。在不需要的時候的時候,可以隨時撤掉observer。

zk進行連線時,一般我們都會把zk所有的節點都配置上去,用逗號分隔。其實連線叢集中的任意一個或者多個都是可以的。只是如果只連一個節點,當這個節點當機的時候,我們就斷開了連線。所以還是推薦配置多個節點進行連線。

如何檢視ZK叢集中的角色

我們可以利用以下命令來檢視zk叢集中的角色

./bin/zkServer.sh status conf/zoo.cfg

我在自己機器上搭建了3個節點的偽叢集(共用一臺機器),配置檔案分別命名為zoo1.cfg,zoo2.cfg,zoo3.cfg。使用以上命令檢視的結果為:

image.png

可以看到,其中節點2為leader,其他的為follower。但是如果你按照zoo1.cfg,zoo2.cfg,zoo3.cfg的順序啟動,無論你啟動多少遍,節點2總是leader,而這時如果把節點2關掉,進行檢視角色,發現節點3成了leader。

image.png

以上這些現象都和zookeeper的選舉機制有關

ZK叢集的選舉機制

我們就拿3個節點的zk作一個簡單選舉的說明

image.png

zk會進行多輪的投票,直到某一個節點的票數大於或等於半數以上,在3個節點中,總共會進行2輪的投票:

  • 第一輪,每個節點啟動時投票給自己,那這樣zk1,zk2,zk3各有一票。
  • 第二輪,每個節點投票給大於自己myid,那這樣zk2啟動時又獲得一票。加上自己給自己投的那一票。總共有2票。2票大於了當前節點總數的半數,所以投票終止。zk2當選leader。

有的童鞋會問,zk3呢,因為zk2已經當選了,投票終止了。所以zk2也不會投票給zk3了。

當然這是一個比較簡單版的選舉,其實真正的選舉還要比較zxid,這個後面會講到。

zk選舉什麼時候會被觸發呢?一是啟動時會被觸發,二是leader當機時會被觸發。上面的例子中,如果節點2當機,根據規則,那獲得leader的就應該是zk3了。

ZK叢集的資料同步機制

zookeeper的資料同步是為了保證每個節點的資料一致性,大致分為2個流程,一個是正常的客戶端資料提交流程,二是叢集中某個節點當機後資料恢復流程。

正常客戶端資料提交流程

客戶端寫入資料提交流程大致為:leader接受到客戶端的寫請求,然後同步給各個子節點:

image.png

但是有童鞋就產生疑惑了,客戶端一般連線的是所有節點,客戶端並不知道哪個是leader呀。

的確,客戶端會和所有的節點建立連結,並且發起寫入請求是挨個遍歷節點進行的,比如第一次是節點1,第二次是節點2。以此類推。

如果客戶端正好連結的節點的角色是leader,那就按照上面的流程走。那如果連結的節點不是leader,是follower呢,則有以下流程:

image.png

如果Client選擇連結的節點是Follower的話,這個Follower會把請求轉給當前Leader,然後Leader會走藍色的線把請求廣播給所有的Follower,每個節點同步完資料後會走綠色的線告訴Leader資料已經同步完成(但是還未提交),當Leader收到半數以上的節點ACK確認訊息後,那麼Leader就認為這個資料可以提交了,會廣播給所有的Follower節點,所有的節點就可以提交資料。整個同步工作就結束了。

那我們再來說說節點當機後的資料同步流程

當zookeeper叢集中的Leader當機後,會觸發新的選舉,選舉期間,整個叢集是沒法對外提供服務的。直到選出新的Leader之後,才能重新提供服務。

我們重新回到3個節點的例子,zk1,zk2,zk3,其中z2為Leader,z1,z3為Follower,假設zk2當機後,觸發了重新選舉,按照選舉規則,z3當選Leader。這時整個叢集只整下z1和z3,如果這時整個叢集又建立了一個節點資料,接著z2重啟。這時z2的資料肯定比z1和z3要舊,那這時該如何同步資料呢。

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

檢視某個資料節點的ZXID的命令為:

先進入zk client命令列
./bin/zkCli.sh -server 127.0.0.1:2181,127.0.0.1:2182,127.0.0.1:2183 
stat加上資料節點名稱
stat /test

執行結果為:

image.png

可以看到,有3個ZXID,這3個ZXID各自代表:

cZxid:該節點建立時的事務ID

mZxid:該節點最近一次更新時的事務ID

pZxid:該節點的子節點的最新一次建立/更新/刪除的事務ID

檢視節點最新ZXID的命令為:

echo stat|nc 127.0.0.1 2181
這個命令需提前在cfg檔案後追加:4lw.commands.whitelist=*,然後重啟

image.png

這裡的ZXID就是當前節點最後一次事務的ID。

如果整個叢集資料為一致的,那麼所有節點的ZXID應該一樣。所以zookeeper就通過這個有序的ZXID來確保各個節點之間的資料的一致性,帶著之前的問題,如果Leader當機之後,再重啟後,會去和目前的Leader去比較最新的ZXID,如果節點的ZXID比最新Leader裡的ZXID要小,那麼就會去同步資料。

再看ZK中的選舉

我們帶著ZXID的概念再來看ZK中的選舉機制。

假設還是有一個3個節點的叢集,zk2為Leader,這時候如果zk2掛了。zk3當選Leader,zk1為Follower。這時候如果更新叢集中的一個資料。然後把zk1和zk3都關閉。然後挨個再重啟zk1,zk2,zk3。這時候啟動後,zk2還能當選為Leader嗎?

其實這個問題,換句話說就是:在挨個啟動zk節點的時候,zk1和zk3的資料為最新,而zk2的資料不是最新的,按照之前的選舉規則的話,zk2是否能順利當選Leader?

答案為否,最後當選的為zk1。

這是為什麼呢。

因為zk2的最新ZXID已經不是最新了,zk的選舉過程會優先考慮ZXID大的節點。這時ZXID最大的有zk1和zk3,選舉只會在這2個節點中產生,根據之前說的選舉規則。在第一輪投票的時候,zk1只要獲得1票,就能達到半數了,就能順利當選為Leader了。

最後

其實zk的選舉和同步並不複雜,如果能試著在本地去搭建3個節點的偽叢集,去試著跑一下上面的案例。應該就能明白整個過程。zk作為老牌的一致性協調中介軟體,也是諸多面試的頻次很高的問點。如果你能理解本篇的核心內容,再次碰到這類問題的時候,這將不會是你的盲區。最後,喜歡本篇內容的朋友希望點贊,關注,轉發。

image.png

相關文章