Zookeeper選舉算法原理


Zookeeper選舉算法原理

Leader選舉

Leader選舉是保證分布式數據一致性的關鍵所在。當Zookeeper集群中的一台服務器出現以下兩種情況之一時,需要進入Leader選舉。

 (1) 服務器初始化啟動。(集群的每個節點都沒有數據 → 以SID的大小為准)

 (2) 服務器運行期間無法和Leader保持連接。(集群的每個節點都有數據 ,或者Leader 宕機→ 以ZXID 和 SID 的最大值為准)

 

1. 服務器啟動時期的Leader選舉

  若進行Leader選舉,則至少需要2台機器,兩台的高可用性會差一些,如果Leader 宕機,就剩下一台,自己沒辦法選舉。這里選取3台機器組成的服務器集群為例。

  在集群初始化階段,當有一台服務器Server1啟動時,其單獨無法進行和完成Leader選舉,當第二台服務器Server2啟動時,此時兩台機器可以相互通信,每台機器都試圖找到Leader,於是進入Leader選舉過程。選舉過程如下

(1) 每個Server發出一個投票。由於是初始情況,Server1和Server2都會將自己作為Leader服務器來進行投票,每次投票會包含所推舉的服務器的myid和ZXID,使用(myid, ZXID)來表示,此時Server1的投票為(1, 0),Server2的投票為(2, 0),然后各自將這個投票發給集群中其他機器。

(2) 接受來自各個服務器的投票。集群的每個服務器收到投票后,首先判斷該投票的有效性,如檢查是否是本輪投票、是否來自LOOKING狀態的服務器。

(3) 處理投票。針對每一個投票,服務器都需要將別人的投票和自己的投票進行PK,PK規則如下:

· 優先檢查ZXID。ZXID比較大的服務器優先作為Leader。(這個很重要:是數據最新原則,保證數據的完整性)

· 如果ZXID相同,那么就比較myid。myid較大的服務器作為Leader服務器。(集群的節點標識)

  對於Server1而言,它的投票是(1, 0),接收Server2的投票為(2, 0),首先會比較兩者的ZXID,均為0。再比較myid,此時Server2的myid最大,於是更新自己的投票為(2, 0),然后重新投票,對於Server2而言,其無須更新自己的投票,只是再次向集群中所有機器發出上一次投票信息即可。

(4) 統計投票。每次投票后,服務器都會統計投票信息,判斷是否已經有過半機器接受到相同的投票信息,對於Server1、Server2而言,都統計出集群中已經有兩台機器接受了(2, 0)的投票信息,此時便認為已經選出了Leader。

(5) 改變服務器狀態。一旦確定了Leader,每個服務器就會更新自己的狀態,如果是Follower,那么就變更為FOLLOWING,如果是Leader,就變更為LEADING。

 

2. 服務器運行時期的Leader選舉

  在Zookeeper運行期間,Leader與非Leader服務器各司其職,即便當有非Leader服務器宕機或新加入,此時也不會影響Leader,但是一旦Leader服務器掛了,那么整個集群將暫停對外服務,進入新一輪Leader選舉,其過程和啟動時期的Leader選舉過程基本一致。

假設正在運行的有Server1、Server2、Server3三台服務器,當前Leader是Server2,若某一時刻Leader掛了,此時便開始Leader選舉。

  選舉過程如下:

(1) 變更狀態。Leader掛后,余下的非Observer服務器都會講自己的服務器狀態變更為LOOKING,然后開始進入Leader選舉過程。

(2) 每個Server會發出一個投票。在運行期間,每個服務器上的ZXID可能不同,此時假定Server1的ZXID為123,Server3的ZXID為122;在第一輪投票中,Server1和Server3都會投自己,產生投票(1, 123),(3, 122),然后各自將投票發送給集群中所有機器。

(3) 接收來自各個服務器的投票。與啟動時過程相同。

(4) 處理投票。與啟動時過程相同,此時,Server1將會成為Leader。

(5) 統計投票。與啟動時過程相同。

(6) 改變服務器的狀態。與啟動時過程相同。


2.2 Leader選舉算法分析

在3.4.0后的Zookeeper的版本只保留了TCP版本的FastLeaderElection選舉算法。當一台機器進入Leader選舉時,當前集群可能會處於以下兩種狀態

    · 集群中已經存在Leader。

    · 集群中不存在Leader。

  對於集群中已經存在Leader而言,此種情況一般都是某台機器啟動得較晚,在其啟動之前,集群已經在正常工作,對這種情況,該機器試圖去選舉Leader時,會被告知當前服務器的Leader信息,對於該機器而言,僅僅需要和Leader機器建立起連接,並進行狀態同步即可。而在集群中不存在Leader情況下則會相對復雜,其步驟如下

(1) 第一次投票。無論哪種導致進行Leader選舉,集群的所有機器都處於試圖選舉出一個Leader的狀態,即LOOKING狀態,LOOKING機器會向所有其他機器發送消息,該消息稱為投票。投票中包含了SID(服務器的唯一標識)和ZXID(事務ID),(SID, ZXID)形式來標識一次投票信息。假定Zookeeper由5台機器組成,SID分別為1、2、3、4、5,ZXID分別為9、9、9、8、8,並且此時SID為2的機器是Leader機器,某一時刻,1、2所在機器出現故障,因此集群開始進行Leader選舉。在第一次投票時,每台機器都會將自己作為投票對象,於是SID為3、4、5的機器投票情況分別為(3, 9),(4, 8), (5, 8)。

(2) 變更投票。每台機器發出投票后,也會收到其他機器的投票,每台機器會根據一定規則來處理收到的其他機器的投票,並以此來決定是否需要變更自己的投票,這個規則也是整個Leader選舉算法的核心所在,其中術語描述如下

· vote_sid:接收到的投票中所推舉Leader服務器的SID。

· vote_zxid:接收到的投票中所推舉Leader服務器的ZXID。

· self_sid:當前服務器自己的SID。

· self_zxid:當前服務器自己的ZXID。

  每次對收到的投票的處理,都是對(vote_sid, vote_zxid)和(self_sid, self_zxid)對比的過程。

    規則一:如果vote_zxid大於self_zxid,就認可當前收到的投票,並再次將該投票發送出去。

    規則二:如果vote_zxid小於self_zxid,那么堅持自己的投票,不做任何變更。

    規則三:如果vote_zxid等於self_zxid,那么就對比兩者的SID,如果vote_sid大於self_sid,那么就認可當前收到的投票,並再次將該投票發送出去。

    規則四:如果vote_zxid等於self_zxid,並且vote_sid小於self_sid,那么堅持自己的投票,不做任何變更。

  結合上面規則,給出下面的集群變更過程。

 


(3) 確定Leader。經過第二輪投票后,集群中的每台機器都會再次接收到其他機器的投票,然后開始統計投票,如果一台機器收到了超過半數的相同投票,那么這個投票對應的SID機器即為Leader。此時Server3將成為Leader。

  由上面規則可知,通常那台服務器上的數據越新(ZXID會越大),其成為Leader的可能性越大,也就越能夠保證數據的恢復。如果ZXID相同,則SID越大機會越大。

2.3 Leader選舉實現細節

1. 服務器狀態

  服務器具有四種狀態,分別是LOOKING、FOLLOWING、LEADING、OBSERVING。

LOOKING:尋找Leader狀態。當服務器處於該狀態時,它會認為當前集群中沒有Leader,因此需要進入Leader選舉狀態。

FOLLOWING:跟隨者狀態。表明當前服務器角色是Follower。

LEADING:領導者狀態。表明當前服務器角色是Leader。

OBSERVING:觀察者狀態。表明當前服務器角色是Observer。

 2. 投票數據結構

  每個投票中包含了兩個最基本的信息,所推舉服務器的SID和ZXID,投票(Vote)在Zookeeper中包含字段如下

id:被推舉的Leader的SID。

zxid:被推舉的Leader事務ID。

electionEpoch:邏輯時鍾,用來判斷多個投票是否在同一輪選舉周期中,該值在服務端是一個自增序列,每次進入新一輪的投票后,都會對該值進行加1操作。

peerEpoch:被推舉的Leader的epoch。

state:當前服務器的狀態。

 
 

為什么zookeeper集群是單數?

1、容錯

由於在增刪改操作中需要半數以上服務器通過,來分析以下情況。

2台服務器,至少2台正常運行才行(2的半數為1,半數以上最少為2),正常運行1台服務器都不允許掛掉

3台服務器,至少2台正常運行才行(3的半數為1.5,半數以上最少為2),正常運行可以允許1台服務器掛掉

4台服務器,至少3台正常運行才行(4的半數為2,半數以上最少為3),正常運行可以允許1台服務器掛掉

5台服務器,至少3台正常運行才行(5的半數為2.5,半數以上最少為3),正常運行可以允許2台服務器掛掉

6台服務器,至少3台正常運行才行(6的半數為3,半數以上最少為4),正常運行可以允許2台服務器掛掉

 

通過以上可以發現,3台服務器和4台服務器都最多允許1台服務器掛掉,5台服務器和6台服務器都最多允許2台服務器掛掉

但是明顯4台服務器成本高於3台服務器成本,6台服務器成本高於5服務器成本。這是由於半數以上投票通過決定的。

 

2、防腦裂

一個zookeeper集群中,可以有多個follower、observer服務器,但是必需只能有一個leader服務器。

如果leader服務器掛掉了,剩下的服務器集群會通過半數以上投票選出一個新的leader服務器。

集群互不通訊情況:

一個集群3台服務器,全部運行正常,但是其中1台裂開了,和另外2台無法通訊。3台機器里面2台正常運行過半票可以選出一個leader。

一個集群4台服務器,全部運行正常,但是其中2台裂開了,和另外2台無法通訊。4台機器里面2台正常工作沒有過半票以上達到3,無法選出leader正常運行。

一個集群5台服務器,全部運行正常,但是其中2台裂開了,和另外3台無法通訊。5台機器里面3台正常運行過半票可以選出一個leader。

一個集群6台服務器,全部運行正常,但是其中3台裂開了,和另外3台無法通訊。6台機器里面3台正常工作沒有過半票以上達到4,無法選出leader正常運行。

 

通可以上分析可以看出,為什么zookeeper集群數量總是單出現,主要原因還是在於第2點,防腦裂,對於第1點,無非是正常控制,但是不影響集群正常運行。但是出現第2種裂的情況,zookeeper集群就無法正常運行了。

 

ZooKeeper的腦裂的出現和解決方案

 
出現:

       在搭建hadoop的HA集群環境后,由於兩個namenode的狀態不一,當active的namenode由於網絡等原因出現假死狀態,standby接收不到active的心跳,因此判斷active的namenode宕機,但實際上active並沒有死亡。此時standby的namenode就會切換成active的狀態,保證服務能夠正常使用。若原來的namenode復活,此時在整個集群中就出現2個active狀態的namenode,該狀態成為腦裂。腦裂現象可能導致這2個namenode爭搶資源,從節點不知道該連接哪一台namenode,導致節點的數據不統一,這在企業生產中是不可以容忍的。

解決方案:

      1、添加心跳線。

            原來兩個namenode之間只有一條心跳線路,此時若斷開,則接收不到心跳報告,判斷對方已經死亡。此時若有2條心跳線路,一條斷開,另一條仍然能夠接收心跳報告,能保證集群服務正常運行。2條心跳線路同時斷開的可能性比1條心跳線路斷開的小得多。再有,心跳線路之間也可以HA(高可用),這兩條心跳線路之間也可以互相檢測,若一條斷開,則另一條馬上起作用。正常情況下,則不起作用,節約資源。

      2、啟用磁盤鎖。

            由於兩個active會爭搶資源,導致從節點不知道該連接哪一台namenode,可以使用磁盤鎖的形式,保證集群中只能有一台namenode獲取磁盤鎖,對外提供服務,避免數據錯亂的情況發生。但是,也會存在一個問題,若該namenode節點宕機,則不能主動釋放鎖,那么其他的namenode就永遠獲取不了共享資源。因此,在HA上使用"智能鎖"就成為了必要措施。"智能鎖"是指active的namenode檢測到了心跳線全部斷開時才啟動磁盤鎖,正常情況下不上鎖。保證了假死狀態下,仍然只有一台namenode的節點提供服務。

       3、設置仲裁機制

            腦裂導致的后果最主要的原因就是從節點不知道該連接哪一台namenode,此時如果有一方來決定誰留下,誰放棄就最好了。因此出現了仲裁機制,比如提供一個參考的IP地址,當出現腦裂現象時,雙方接收不到對方的心跳機制,但是能同時ping參考IP,如果有一方ping不通,那么表示該節點網絡已經出現問題,則該節點需要自行退出爭搶資源的行列,或者更好的方法是直接強制重啟,這樣能更好的釋放曾經占有的共享資源,將服務的提供功能讓給功能更全面的namenode節點。

以上的3種方式可以同時使用,這樣更能減少集群中腦裂情況的發生。但是還是不能保證完全不出現,如果仲裁機制中2台機器同時宕機,那么此時集群中沒有namenode可以使用。此時需要運維人員人工的搶修,或者提供一台新的機器作為namenode,這個時間是不可避免的。希望未來能有更好的解決辦法,能徹底杜絕這類情況的發生吧~


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM