ZooKeeper之Leader選舉
Leader選舉是ZooKeeper中最重要的技術之一,也是保證分散式資料一致性的關鍵所在。
1.leader選舉
Leader選舉主要分為伺服器啟動時期的Leader選舉和伺服器執行期間的Leader選舉。以下是兩種情況的選舉大致步驟。
伺服器啟動時期的Leader選舉
1.每個Server會發出一個投票:投票包含要選舉的伺服器的SID和ZXID等資訊,將這個投票傳送給叢集中其它所有機器。
2.接收來自各個伺服器的投票:每個伺服器都會接收來自其它伺服器的投票。叢集中的每個伺服器在接收到投票後,首先會判斷該投票的有效性,包括檢查是否是本輪投票、是否來自LOOKING狀態的伺服器。
3.處理投票:在收到來自其它伺服器的有效投票後,針對每一個投票,伺服器都需要將別人的投票和自己的投票按規則進行投票。
4.統計投票:每次投票後,伺服器都會統計所有投票,判斷是否已經有過半的及其接收到相同的投票資訊,確認Leader。
5.改變伺服器狀態:一旦確定了Leader,每個伺服器就會更新自己的狀態:如果是Follower,那麼就變更為FOLLOWING,如果是Leader,就變更為LEADING。
伺服器執行期間的Leader選舉
這種情況只有在原有的Leader伺服器掛了,才會進入新一輪的選舉。大致流程如下:
1.變更狀態:當Leader掛了之後,餘下的非Observer伺服器(Observer伺服器不參與投票,Observer伺服器的主要作用就是為了緩解讀的壓力)都會將自己的伺服器狀態變更為LOOKING,然後開始進入Leader選舉流程。
2.每個Server會發出一個投票。
3.接收來自各個伺服器的投票。
4.處理投票。
5.統計投票。
6.改變伺服器狀態。
觀察上面兩種情況的流程可以看出,兩者就是第一步存在不同,執行期間進入選舉,需要進行狀態的變更。
2.Leader選舉的演算法分析與具體實現
Leader選舉演算法主要是PK階段的實現。因為投票資訊包含了兩個最基本的資訊:推選的伺服器的SID和ZXID,分別代表了被推選伺服器的唯一標識和最新事務ID.當叢集中每臺機器發出自己的投票後,也會接收到來自叢集中其它機器的投票。每次對於收到的投票的處理。
1.如果接收到的投票的ZXID大於當前機器的ZXID,則將接收到的投票變更成自己的投票再次發出去。
2.如果接收到的投票的ZXID小於當前機器的ZXID,不作任何變更。
3.如果兩者的ZXID相等,則選擇SID大的作為Leader。
以上是Leader選舉演算法的核心規則,以下便是該演算法在ZooKeeper中是如何實現的。
首先,先了解一下伺服器的幾個狀態:
LOOKING:尋找Leader狀態。
FOLLOWING:跟隨者狀態,表明當前伺服器角色是Follower
LEADING:領導者狀態,表明當前伺服器角色是Leader。
OBSERVING:觀察者狀態,表明當前伺服器角色是Observer。
在Leader選舉過程中,QuorumCnxManager負責各臺伺服器之間的底層Leader選舉過程中的網路通訊。每臺伺服器啟動的時候,都會啟動一個QuorumCnxManager。
訊息佇列
QuorumCnxManager內部維護了一系列的佇列和集合。
recvQueue:訊息接收佇列,用於存放那些從其它伺服器接收的訊息。
queueSendMap:訊息傳送佇列,是一個Map,key為伺服器的SID,值分別為SID對應的機器分配的一個單獨佇列。
senderWorkerMap:傳送器集合。key為伺服器的SID,值為每個伺服器對應的SendWorker訊息傳送器。
lastMessageSent:最近傳送過的訊息集合,這個集合為每個SID保留最近傳送過的一個訊息。
訊息接收與傳送
首先ZooKeeper叢集中的所有機器需要兩兩建立起網路連線。開啟埠監聽後,ZooKeeper就不斷地接收到來自其它伺服器的"建立連線"請求,在接收到其它伺服器的TCP連線請求時,會交由receiveConnection函式來處理。為了避免兩臺機器之間重複地建立TCP連線,ZooKeeper設計了一種TCP連線的規則:只允許SID大的伺服器主動和其它伺服器建立連線,否則斷開連線。建立起連線之後,就會根據遠端伺服器的SID來建立相應的訊息傳送器SendWorker和訊息接收器RecvWorker,並啟動他們。
ZooKeeper會為每個遠端伺服器分配一個RecvWorker,而每個RecvWorker從這個TCP連結中讀取訊息,並將其儲存到recvQueue佇列中。同時,ZooKeeper為每個遠端伺服器單獨分配了訊息傳送器SendWorker,每個SendWorker不斷地從對應的訊息傳送佇列中獲取出一個訊息傳送,並將這個訊息放入lastMessageSent中來作為最近傳送過的訊息。注意:如果ZooKeeper發現針對當前遠端伺服器的訊息傳送佇列為空,那麼這個時候需要從lastMessageSent中取出一個最近傳送過的訊息來進行再次傳送。
而進行選舉的核心演算法是基於TCP的FastLeaderElection演算法。WorkerReceiver不斷地從QuorumCnxManager中獲取出其它伺服器發來的選舉訊息,並將其轉換成一個選票,然後儲存到recvqueue佇列中去。在選票的接收過程中,如果發現該外部投票的接收過程中,如果發現該外部投票的選舉輪次小於當前伺服器,就直接忽略這個外部投票,同時立即發出自己的內部投票;當前伺服器不是LOOKING狀態,忽略這個外部投票,將Leader資訊以投票的形式傳送出去;如果接收到的訊息來自Observer伺服器,就忽略該訊息,並把自己當前的投票傳送出去。而WorkerSender會不斷地從sendqueue佇列中獲取待發送的選票,並將其傳遞到底層QuorumCnxManager中去。然後根據對應的接收到的選票按照規則進行處理,直到選出Leader。