分布式开发必须了解的Zookeeper的Leader选举机制(源码解析)
分布式開發必須知道的Zookeeper知識及其的Leader選舉機制(ZAB原子廣播協議)
??ZooKeeper是Hadoop下的一個子項目,它是一個針對大型分布式系統的可靠協調系統,提供的功能包括:配置維護、名字服務、分布式同步、組服務等; 它的目標就是封裝好復雜易出錯的關鍵服務,將簡單易用的接口和性能高效、功能穩定的系統提供給用戶。
ZooKeeper系統架構
??下圖就是Zookeeper的架構圖:
??從上面的架構圖中,我們需要了解的主要的信息有:
??①ZooKeeper分為服務器端(Server)和客戶端(Client),客戶端可以連接到整個ZooKeeper服務的任意服務器上(Leader除外)。
??②ZooKeeper 啟動時,將從實例中選舉一個Leader,Leader 負責處理數據更新等操作,一個更新操作成功的標志是當且僅當大多數Server在內存中成功修改數據(Quorom機制)。每個Server 在內存中存儲了一份數據。
??③Zookeeper是可以集群復制的,集群間通過Zab協議(Zookeeper Atomic Broadcast)來保持數據的一致性;
??④Zab協議包含兩個階段:Leader Election階段和Atomic Brodcast階段。群中將選舉出一個Leader,其他的機器則稱為Follower,所有的寫操作都被傳送給Leader,并通過Brodcast將所有的更新告訴給Follower。 當Leader被選舉出來,且大多數服務器完成了和leader的狀態同步后,Leadder Election 的過程就結束了,就將會進入到Atomic Brodcast的過程。Atomic Brodcast同步Leader和Follower之間的信息,保證Leader和Follower具有形同的系統狀態。
Quorom機制簡介
??在分布式系統中,冗余數據是保證可靠性的手段,因此冗余數據的一致性維護就非常重要。一般而言,一個寫操作必須要對所有的冗余數據都更新完成了,才能稱為成功結束。比如一份數據在5臺設備上有冗余,因為不知道讀數據會落在哪一臺設備上,那么一次寫操作,必須5臺設備都更新完成,寫操作才能返回。
??對于寫操作比較頻繁的系統,這個操作的瓶頸非常大。Quorum算法可以讓寫操作只要寫完3臺就返回。剩下的由系統內部緩慢同步完成。而讀操作,則需要也至少讀3臺,才能保證至少可以讀到一個最新的數據。
Zookeeper中的四種角色
①Leader:領導者,負責進行投票的發起和決議,更新系統狀態。
②Learner:學習者
③Follower(Learner的子類):跟隨者,用于接受客戶端請求并向客戶端返回結結果,在選主過程中參與投票,Follower可以接收Client請求,如果是寫請求將轉發給Leader來更新系統狀態。
④Observer:觀察者,可以接收客戶端連接,將寫請求轉發給Leader節點,但是不參與投票過程,只是同步Leader狀態,因為Follower增多會導致投票階段延遲增大,影響性能。Observer的目的是為了擴展系統,提高讀取數據。
為什么Zookeeper中的Server數目一般為基數?
??我們知道在Zookeeper中 Leader 選舉算法采用了Quorom算法。該算法的核心思想是當多數Server寫成功,則任務數據寫成功。假設有3個Server,則最多允許一個Server掛掉;如果有4個Server,則同樣最多允許一個Server掛掉。既然3個或者4個Server,同樣最多允許1個Server掛掉,那么它們的可靠性是一樣的,所以選擇奇數個ZooKeeper Server即可,這里選擇3個Server。
Zookeeper用于Leader選舉的算法
①基于UDP的LeaderElection
②基于UDP的FastLeaderElection
③基于UDP和認證的FastLeaderElection
④基于TCP的FastLeaderElection(默認值)
FastLeaderElection機制
??接下來要說的就是Zookeeper的Leader選舉機制核心算法FastLeaderElection類。FastLeaderElection實現了Election接口,其需要實現接口中定義的lookForLeader(核心的選舉算法入口)方法和shutdown方法FastLeaderElection選舉算法是標準的Fast Paxos算法實現,可解決LeaderElection選舉算法收斂速度慢的問題。
術語介紹
sid(myid)
??每個Zookeeper服務器,都需要在數據文件夾下創建一個名為myid的文件,該文件包含整個Zookeeper集群唯一的ID(整數)。例如某Zookeeper集群包含三臺服務器,hostname分別為zoo1、zoo2和zoo3,其myid分別為1、2和3,則在配置文件中其ID與hostname必須一一對應,如下所示。在該配置文件中,server.后面的數據即為myid(Leader選舉時用的sid或者leader)。
server.1=zoo1:2888:3888 server.2=zoo2:2888:3888 server.3=zoo3:2888:3888 復制代碼zxid
??類似于RDBMS中的事務ID,用于標識一次更新操作的Proposal ID。為了保證順序性,該zkid必須單調遞增。因此Zookeeper使用一個64位的數來表示,高32位是Leader的epoch,從1開始,每次選出新的Leader,epoch加一。低32位為該epoch內的序號,每次epoch變化,都將低32位的序號重置。這樣保證了zxid的全局遞增性。
Zookeeper節點的四種狀態
??截圖為Zookeeper定義的四種服務器節點狀態:
-
①LOOKING: 不確定Leader狀態。該狀態下的服務器認為當前集群中沒有Leader,會發起Leader選舉。
-
②FOLLOWING: 跟隨者狀態。表明當前服務器角色是Follower,并且它知道Leader是誰。
-
③LEADING: 領導者狀態。表明當前服務器角色是Leader,它會維護與Follower間的心跳。
-
④OBSERVING: 觀察者狀態。表明當前服務器角色是Observer,與Folower唯一的不同在于不參與選舉,也不參與集群寫操作時的投票。
FastLeaderElection內部類
FastLeaderElection的內部類的情況如下圖:
- **Notification:**表示收到的選舉投票信息(其他服務器發來的選舉投票信息),其包含了被選舉者的id、zxid、選舉周期等信息。
- **ToSend:**表示發送給其他服務器的選舉投票信息(其他服務器發來的選舉投票信息),其包含了被選舉者的id、zxid、選舉周期等信息。
- Messenger:包含了WorkerReceiver和WorkerSender兩個內部類。WorkerReceiver實現了Runnable接口,是選票接收器。WorkerSender也實現了Runnable接口,為選票發送器。
Notification(收到的投票信息)
- **leader:**被推選的leader的id。
- **zxid:**被推選的leader的事務id。
- **electionEpoch:**推選者的選舉周期。
- **state:**推選者的狀態。
- **sid:**推選者的id。
- **peerEpoch:**被推選者的選舉周期。
ToSend(發送的投票信息)
- **leader:**被推選的leader的id。
- **zxid:**被推選的leader的事務id。
- **electionEpoch:**推選者的選舉周期。
- **state:**推選者的狀態。
- **sid:**推選者的id。
- **peerEpoch:**被推選者的選舉周期。
WorkerSender(選票發送器)
??WorkerSender也實現了Runnable接口,為選票發送器,其會不斷地從sendqueue中獲取待發送的選票,并將其傳遞到底層QuorumCnxManager中。
- 獲取選票
- 發送選票
WorkerReceiver(選票接收器)
??WorkerReceiver實現了Runnable接口,是選票接收器。其會不斷地從QuorumCnxManager中獲取其他服務器發來的選舉消息中。先會從QuorumCnxManager中的pollRecvQueue隊列中取出其他服務器發來的選舉消息,消息封裝在Message數據結構中。然后判斷消息中的服務器id是否包含在可以投票的服務器集合中,若不是,則會將本服務器的內部投票發送給該服務器,其流程如下:
??若包含該服務器,則根據消息(Message)解析出投票服務器的投票信息并將其封裝為Notification,然后判斷當前服務器是否為LOOKING,若為LOOKING,則直接將Notification放入FastLeaderElection的recvqueue。然后判斷投票服務器是否為LOOKING狀態,并且其選舉周期小于當前服務器的邏輯時鐘,則將本(當前)服務器的內部投票發送給該服務器,否則,直接忽略掉該投票。其流程如下:
??若本服務器的狀態不為LOOKING,則會根據投票服務器中解析的version信息來構造ToSend消息,放入sendqueue,等待發送,起流程如下:
核心函數分析
sendNotifications函數
??其會遍歷所有的參與者投票集合,然后將自己的選票信息發送至上述所有的投票者集合,其并非同步發送,而是將ToSend消息放置于sendqueue中,之后由WorkerSender進行發送。
totalOrderPredicate函數
??該函數將接收的投票與自身投票進行PK,查看是否消息中包含的服務器id是否更優,其按照epoch、zxid、id的優先級進行PK。
- 判斷消息里的epoch是不是比當前的大,如果大則消息中id對應的服務器就是leader。
- 如果epoch相等則判斷zxid,如果消息里的zxid大,則消息中id對應的服務器就是leader。
- 如果前面兩個都相等那就比較服務器id,如果大,則其就是leader。
termPredicate函數
??該函數用于判斷Leader選舉是否結束,即是否有一半以上的服務器選出了相同的Leader,其過程是將收到的選票與當前選票進行對比,選票相同的放入同一個集合,之后判斷選票相同的集合是否超過了半數。
checkLeader函數
??該函數檢查是否已經完成了Leader的選舉,此時Leader的狀態應該是LEADING狀態。
lookForLeader函數
??該函數就是leader選舉的核心方法,代碼行數有點多,這里僅分析其中的一部分。
- 更新邏輯時鐘、更新選票、發送選票
- 獲取投票數據、連接服務器
- 選舉Leader
- LEADING狀態處理
??以上就是關于zookeeper的所有基本知識與Leader選舉機制的講解。喜歡的話記得轉發額。
??歡迎大家關注我的微信公眾號,不定期分享各類面試題、踩坑記錄、高階知識分享。
轉載于:https://juejin.im/post/5d00c019e51d4577770e7370
總結
以上是生活随笔為你收集整理的分布式开发必须了解的Zookeeper的Leader选举机制(源码解析)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 讨论一下文章的阅读量 (个人观点)
- 下一篇: 大家都在用并发,小编带你了解并发的背景