您好,登錄后才能下訂單哦!
本篇文章給大家分享的是有關ZooKeeper的選舉機制是怎樣的,小編覺得挺實用的,因此分享給大家學習,希望大家閱讀完這篇文章后可以有所收獲,話不多說,跟著小編一起來看看吧。
今天開始我們將深入 ZK 選舉相關的知識
一、選舉的基本規則
ZKr~這次我決定一反常態,先不講故事了~先得聊聊在 ZK 選舉中非常重要的一些東西。
1.1 zxid
zxid 就是我們之前提到的事務編號,是一個 8 字節的整型數字,但是 ZK 設計的時候把這一個數字拆成了兩部分使用,一魚兩吃!
8 個字節的整數一共有 64 位長度,前 32 位用來記錄 epoch,后 32 位就是用來計數。你可能要問了?epoch?是啥?
zxid 初始化是 0,也就是這樣
00000000000000000000000000000000 00000000000000000000000000000000
每一次寫請求都會增加后 32 位,假設現在進行了 10 次寫請求(無論該請求有沒有真的修改到數據),zxid 就會變成這樣
00000000000000000000000000000000 00000000000000000000000000001010
當進行一次選舉的時候,前 32 位就會增加 1,并且清零后 32 位
00000000000000000000000000000001 00000000000000000000000000000000
除了選舉以外,當后 32 位徹底用完(變成全 1,也就是 ZK 正常執行了 2^32 - 1 次寫請求都沒進行過一次選舉,牛逼!)也會讓前 32 位增加 1,相當于進位
# 進位前 00000000000000000000000000000000 11111111111111111111111111111111 # 進位后 00000000000000000000000000000001 00000000000000000000000000000000
到這里我就可以回答大家前面的問題了,epoch 就是 zxid 前 32 位的這個數字,epoch 本身的翻譯是“紀元,時代”的意思,意味著更新換代,而 zxid 的后 32 位數字僅僅是寫請求的計數罷了
1.2 myid
在之前的小故事里,我給 ZK 的集群中的各個節點都起了一個好記的名字(神特么好記!)。但是 ZK 官方自己是如何給每一個集群中的節點起名字的呢?用的就是 myid!
ZK 的啟動配置 zoo.cfg 中有一項 dataDir 指定了數據存放的路徑(默認是 /tmp/zookeeper),在此路徑下新建一個文本文件,命名為 myid, 文本內容就是一個數字,這個數字就是當前節點的 myid
/tmp └── zookeeper ├── myid └── ...
然后在 zoo.cfg 是這樣配置集群信息
server.1=zoo1:2888:3888 server.2=zoo2:2888:3888 server.3=zoo3:2888:3888
這個 server. 之后的數字就是 myid,這個 myid 在整個集群中,各個節點之間是不能重復的。我忘記之前在哪兒看到的了,說是 myid 只能是 1 到 255 的數字,我一直信以為真,直到這次,我本著嚴謹的態度去做了實踐,一切以事實為主,并且我的實驗覆蓋了 3.4、3.5、3.6 三大版本(都是三臺機器的簡單集群),結論是:myid 只要是不等于 -1 就行(-1 是一個固定的值會導致當前節點啟動報錯),不能大于 Long.MAX_VALUE 或者小于 Long.MIN_VALUE,但是如果在當前的節點中配置了 zookeeper.extendedTypesEnabled=true 那當前節點的最大 myid 是 254(負數不影響,我也不知道這個 254 的用意,但是代碼中的確有判斷) 是不是奇怪的知識又增加了呢~
關于配置更多的信息,之后單獨再整理,今天就點到為止
1.3 選舉規則
知道了上面這些有什么用呢?非常重要!因為選舉 Leader 完全看的就是這幾個值
epoch
寫請求次數
myid
優先級從上到下逐級比較,誰大誰就更有資格成為 Leader,當前級一樣就比較下一級,直到分出勝負為止!因為 myid 是不能重復的,所以最終是一定能分出勝負的!
好了,現在大家知道了最基本的選舉規則了~讓我們進入下一節吧
二、三馬之爭
馬果果一定想不到,這輩子自己可以和兩位鼎鼎大名的明星企業家相提并論,讓我們一起去看看發生了什么吧~
2.1 準備開工
之前馬果果規定了三個辦事處在對外開張前必須選出一個 Leader,在正式開始選之前,每一個辦事處也有一些準備工作需要做:
每一個辦事處必須得知道一共有多少個辦事處
額外聘請一些專門負責和其他辦事處溝通的話務員
準備好一個票箱用來對投票統計和歸票
為每一個辦事處設置一個固定的 myid
所以現在辦公室的布置變成了這樣(我省略了之前章節的其他要素):
有了這些準備工作以后所有辦事處都可以進入選舉的階段了,并且村委會規定了幾種狀態用于表示當前辦事處正處在的階段:
LOOKING,正在尋找 Leader,處于此階段的辦事處不能對外提供服務
LEADING,當前辦事處就是 Leader,可以對外提供服務
FOLLOWING,當前辦事處正在跟隨 Leader,可以對外提供服務
很明顯剛剛準備好的各個辦事處現在都處于 LOOKING 狀態,下面讓我們正式進入選舉流程吧
2.2 開始選舉
由于各個辦事處剛準備好,所以彼此之間還沒有通過信,又加上大家都是姓馬的,心里面都是想當老大的,所以每一個辦事都會率先擬一張寫著自己的選票發給其他辦事處。主要有這些信息:
sid:我是誰
leader:我選誰
state:我當前的狀態
epoch:我當前的 epoch
zxid:我選擇的 leader 的最大的事務編號
以馬果果舉例:
馬小云和馬小騰也一樣,一開始都選了自己做 Leader 候選人,并且都把自己認為的候選人(當前場景下就是自己)的票分別發送給了其他兩位(以及自己)
2.2.1 馬果果視角
每個辦事處各自也會收到來自其他辦事處的選票(也有可能是自己的),每拿到一張選票,都需要和當前自己認為的 Leader 候選人做比較,理論上自己投給自己的選票會先一步達到自己的票箱,因為不需要經過通訊減少了傳輸的路徑,自己的選票和自己的候選人是一致的所以不需要比較,只需要在票箱中記上一筆,我們還是以馬果果舉例:
=》的左邊是辦事處的名字,右邊是該辦事處選的 Leader。當前投票統計是指,當前節點所選的 Leader 獲得的選票統計。
假設他再收到了馬小云的選票:
馬果果首先看到的是馬小云也處在 LOOKING 狀態
接著就會比較自己候選人和馬小云的選票(左邊代表當前辦事處的候選人,右邊代表收到的選票信息,下同)
e:0 == e:0 z:0 == z:0 l: 馬果果(69) > l: 馬小云(56)
最終因為馬果果的 myid 69 要比馬小云的 myid 56 要大,所以馬果果最終勝出!雖然馬小云勝出了,但是當前投票統計是不能修改的,因為馬小云這一輪的選票就是選的馬小云,需要等待他重新改票后再投才能修改投票統計。
之后會往投票箱記錄:
緊接著是馬小騰的投票:
e:0 == e:0 z:0 == z:0 l: 馬果果(69) > l: 馬小騰(49)
馬果果還是勝出!
記錄投票箱:
每次收到投票的時候,馬果果都會依據當前的投票統計進行歸票,但是很遺憾選舉仍然無法結束,因為結束的規則必須有某一個辦事處獲得半數以上的選票,現在只有一個馬果果自己的選票,不滿足半數以上,所以馬果果只能再等等了。
而在馬果果這邊忙的熱火朝天的同時,馬小云和馬小騰也在進行著同樣的動作。
2.2.2 馬小云視角
我們這省略描述馬小云記錄自己選票的過程,假設他這邊是先收到馬果果的選票,是怎么處理的呢?
e:0 == e:0 z:0 == z:0 l: 馬小云(56) < l: 馬果果(69)
馬小云看到自己認為的 Leader 候選人被馬果果的選票擊敗了,所以將自己的候選人改為馬果果,并將新的選票重新廣播出去
然后在自己的投票箱中記錄:
為了敘述的完整性,我們還是把馬小騰的票也看完
e:0 == e:0 z:0 == z:0 l: 馬果果(69) > l: 馬小騰(49)
馬果果還是勝出了,所以馬小云的投票箱最終變成這樣:
講道理接下來應該以馬小騰為主視角,再講一遍剛才的過程,但是可以認為幾乎和馬小云是一樣的,為了故事的順暢,我們需要回到馬果果的視角,因為馬小云輸給馬果果之后改票了,又發了一輪選票
2.2.3 馬果果視角(再)
馬果果又再一次收到了馬小云的選票(改票后),投票箱就會改成這樣:
收到這個投票后,當前投票統計就會增加馬小云的記錄,然后馬果果進行歸票就發現了這次自己的選票超過半數了,然后會進行二次確認,會等待一會看看還能不能收到更新的選票,這里假設沒有收到更新的投票,就會進行判斷,當前過半數的候選人是不是自己?如果是的話,那自己就是 Leader,不是的話,自己就是 Follower。
很明顯,馬果果就是 Leader,然后會把自己的狀態修改為 LEADING。
與此同時,馬小云、馬小騰也進行歸票,歸票結果自己為 Follower,把自己狀態修改為 FOLLOWING,然后各自都會和 Leader 進行數據的同步,同步完成之后整個辦事處就都可以對外提供服務了。
2.3 馬小騰停電啦
選舉本身涉及到集群間的通信、節點自身的狀態管理和狀態變更,本身就是一個比較復雜的過程,剛才只是舉例了一個最簡單的啟動選舉流程,下面會舉更多的例子幫助大家能理解整個選舉的邏輯。
現在假設辦事處安然無恙得對外提供了一段時間服務后,馬小騰的辦事處突然停電了,就不能和另外兩馬進行通訊了,而另外兩馬在一段時間內都沒有收到過馬小騰的信息的時候就知道,出事了!但是各自盤點了下目前仍然還有兩個辦事處可以對外提供服務,是達到整個集群總數的半數以上的,是可以繼續讓村民們來辦理業務的,所以現在整個集群變成了這樣:
沒過一會,因為電力公司的積極搶修,馬小騰的辦事處恢復供電了,重新開張了,但是每一個辦事處在開張前都是處在 LOOKING 狀態的,還是會優先投票給自己,并會通過復盤本地的存檔來得到自己辦事處最新的數據,假設馬小騰停電前是這樣:
e:0 z:21 l: 馬小騰(49) LOOKING
他和之前一樣會給另外兩個辦事處發自己的選票
但和之前的情況不同,無論是馬果果還是馬小云他們現在都處在工作的狀態,收到了馬小騰的選票后就會把當前的 Leader 也就是馬果果的選票信息以及自己當前的狀態發送給他。
馬果果發送的選票信息:
e:0 z:30 l: 馬果果(69) LEADING
馬小云發送的選票信息:
e:0 z:30 l: 馬果果(69) FOLLOWING
馬小騰收到兩位的選票信息后,知道了當前的 Leader 是馬果果,并且馬果果本人也確認了是 LEADING 狀態,就馬上把自己的狀態修改為了 FOLLOWING 狀態,并且會和之前一樣與 Leader 進行數據的同步,關于具體怎么同步的,我打算留到之后再進行講解~
同步之后,馬小騰的狀態變成了和馬小云一樣的了。
我再假設這里有一個平行世界,回到馬小騰剛恢復完供電準備開張上線的時候,此時的馬小騰的狀態假設是這樣的:
e:1 z:7 l: 馬小騰(49) LOOKING
哪怕 epoch 比目前的 Leader 還要大,其實照道理是更有資格當 Leader,但是由于當前集群中的其他辦事處已經有了一個明確的 Leader,馬小騰也只能忍辱負重(誰讓你停電了呢)還是以 Follower 的身份加入到集群中來,并且仍然以當前 Leader 的信息來同步,你也可以理解為降級(把自己的 epoch 降級回 0 )
職場就是這么殘忍,你稍微請個長假再回來可能已經是物是人非了~
2.4 馬果果又病啦
馬果果畢竟年事已高,又又又生病了,辦事處只能含淚關門,但是和上一次馬小騰停電不同,這次是作為 Leader 的馬果果停止服務了,因為之前定下的規定,整個辦事處集群必須得有一個 Leader。現在馬小云和馬小騰發現 Leader 聯系不上了,說明 Leader 無法服務了,他們就知道必須選出一個新的 Leader。于是紛紛將自己的狀態都修改為 LOOKING 狀態,并且再次把候選人選為自己,重新向其他仍然可以提供服務的辦事處廣播自己的選票(當前這個場景就是互相發選票了)。
無論誰收到選票后經過比較后都會知道是馬小騰勝出
e:1 == e:1 z:77 < z:80 l: 馬小云(56) l: 馬小騰(49)
馬小云會把自己的候選人修改為馬小騰之后重新再把自己的選票發出去,現在馬小騰就獲得了 2 票通過,同時也滿足大于整個辦事處集群半數以上,所以馬小騰和馬小云各自修改狀態為 LEADING 和 FOLLOWING 后,并且會和之前說的一樣,把 epoch 加 1 同時清空計數部分,最后重新恢復對村民提供服務。
而馬果果這邊病好以后,會重新開張和之前的例子一樣也是先從 LOOKING 狀態開始,最后會從其他兩馬那里得知目前的 Leader 是馬小騰之后,就會主動和馬小騰同步數據并以 Follower 的身份加入到辦事處集群中對外提供服務。
2.5 招商引資
辦事處的熱火朝天被村委會看在了眼里,心想只有三個辦事處就能達到這樣的效果,如果有更多的辦事處呢?于是和三馬商量了下,決定對外招商引入社會資本,讓他們自己按照現有模式建立新的辦事處,這樣村委會不用出一分錢,村民還能獲得實在的好處,秒啊!
圖片
此舉一度引來社會資本的大量關注,但是商量過后,三馬又覺得如果過多的引入外部力量勢必會削弱自己手中的權力,所以又出了一個規定,三馬自封為 Participant 只有他們三個才有資格進行 Leader 的競選,而引入的社會資本所創建的辦事處只能作為 Observer 加入辦事處的集群中對外提供只讀服務,沒有資格競爭 Leader,這樣就可以在不增加選舉復雜程度的同時,提升整個辦事處集群對讀請求的吞吐量。
要聲明當前節點是 Observer,需要在 zoo.cfg 中先配置 peerType=observer
同時聲明的集群信息最后要多加一個 :observer 用來標識,這樣其他節點也會知道當前 myid 為 1 和 2 都是 Observer
server.69=maguoguo:2888:3888 server.56=maxiaoyun:2888:3888 server.49=maxiaoteng:2888:3888 server.1=dongdong:2888:3888:observer server.2=jitaimei:2888:3888:observer
而在 LOOKING 狀態的 Observer 一開始的 Leader 候選人也會選自己,但是選票信息被設置成了這樣,以東東舉例:
e:Long.MIN_VALUE z:Long.MIN_VALUE l: 東東(1) LOOKING
因為 epoch 被設置成了最小值所以這個選票等同于形同虛設,可以被直接忽略,并且在三馬那里會維護一個 Participant 的列表,如果他們收到了來自 Participant 以外的辦事處的選票會直接選擇忽略,所以可以說 Observer 的選票對選舉結果是完全沒有影響的。最終是等待 Participant 之間的選舉結果通知,Observer 自身修改狀態為 OBSERVING,開始和 Leader 進行同步數據,這點和 Follower 沒區別,之后 Observer 和 Follower 會統稱為 Learner
2.6 小結
競選 Leader 看的是 epoch、寫請求操作數、myid 三個字段,依次比較誰大誰就更有資格成為 Leader
獲選超過半數以上的辦事處正式成為 Leader,修改自己狀態為 LEADING
其他 Participant 修改為 FOLLOWING,Observer 則修改為 OBSERVING
如果集群中已經存在一個 Leader,其他辦事處如果中途加入的話,直接跟隨該 Leader 即可
還得提一句,如果當前可提供服務的節點已經不足半數以上了,那么這個選舉就永遠無法選出結果,每個節點都會一直處在 LOOKING 狀態,整個辦事處集群也就無法對外提供服務了
三、猿話一下
扯蛋扯完了,現在用咱的行話對有一些概念再深入一下。
首先我必須要說的是,故事里的三馬,為了一定的節目效果,我描述成了三個角色,但是實際中 ZK 服務端是不會做這樣的區分的,都是相同的代碼,根據不同的配置啟動,才有了運行時期 Leader、Follower、Observer 的角色之分,所以更貼近于實際的應該類似于火影里的影分身或者龍珠里的殘像拳之類的(好像混入了什么奇怪的東西)。
我畫了下選舉的簡單流程圖:
其他地方我基本上都講過了,這里再講下紅色部分,因為可能一些網絡因素,發出去的選票對方卻沒收到,這個發起重新廣播投票就是為了能讓對方再重新發一次剛剛的選票。
同監聽客戶端 2181 端口不同的是,服務端集群之間相互通信,直接使用的是原生的 Socket 并沒有使用 NIO 或者是 Netty,因為服務端節點一共就這么幾個而且針對每一個其他節點都會啟動一個線程去監聽,所以直接采用了這種比較原始的并且是阻塞的方式通信,更簡單直接,而且假設對方服務不可用了的話, Socket 會直接報錯退出。
收發選票也是采用了 ZK 中非常常見的生產者-消費者模式,分別維護了兩個阻塞隊列,一個對應發送出去的選票,一個對應收到的選票,各自使用一個子線程去輪詢該阻塞隊列。
之前的 ZK 是擁有 3 種選舉策略的,雖然另外兩種之前都是被廢棄的狀態,不建議使用,但是通過配置文件還是可以強行使用的。不過在最新的 3.6.2 中另兩種策略直接從源碼中刪除了,現在只有一種選舉的策略,源碼中對應 FastLeaderElection,另外兩個我也沒研究過,就不展開了。
關于服務端之間的心跳檢測:
服務端之間的心跳檢測(PING)是由 Leader 發起的,發向所有集群中的其他節點
Follower 收到 PING 后會回一個PING 給 Leader 并帶上自己這邊的客戶端會話數據
而 Leader 收到 Follower 的 PING 后,就會對這些客戶端進行會話連接
以上就是ZooKeeper的選舉機制是怎樣的,小編相信有部分知識點可能是我們日常工作會見到或用到的。希望你能通過這篇文章學到更多知識。更多詳情敬請關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。