天天看點

分布式專題|面試官常問的zookeeper選舉、消息廣播、崩潰恢複原理,你都知道了麼?

分布式專題|面試官常問的zookeeper選舉、消息廣播、崩潰恢複原理,你都知道了麼?

zookeeper選舉過程

我們先介紹幾個主要的參數:

  • zxid:目前節點最新的事務ID,ID值越大,則說明資料越新
  • serverId:對應每個節點的myid,myid越大在選舉過程中的權重就越大。
  • epoch:代表選舉的次數,ZXID高位的編号和其值相等
  • 選舉狀态:
  • LOOKING,競選狀态。
  • FOLLOWING,随從狀态,同步leader狀态,參與投票。
  • OBSERVING,觀察狀态,同步leader狀态,不參與投票。
  • LEADING,上司者狀态。

有了上面的基本概念之後,我們現在來開始說選舉的過程吧:

選舉的過程發生在以下兩個場景下:

  • 節點初始化啟動的時候
  • 伺服器運作時期,leader崩潰後的重新選舉

節點初始化時的選舉過程(以三台機器為例)

  1. 每個節點先給自己投票,加入現在有兩台機器上線,分别為A(myid:1,zxid:0),B(myid:2,zxid:0),那麼這兩個節點在投票給自己之後,會将投票結果發到叢集,如:A伺服器發出的投票則為(myid:1,zxid:0),B為(myid:2,zxid:0);
  2. 每個節點接收到來自叢集中其他節點的投票,首先判斷該投票的有效性(判斷epoch參數是否低于目前節點的epoch)
  3. 處理投票:
  • 先檢查ZXID,ZXID較大的伺服器優先作為leader
  • 如果zxid相同,則檢查myid,myid較大的作為leader
  1. 統計投票

    每次投票後,伺服器都會統計投票資訊,判斷是否已經有過半機器接受到相同的投票資訊,對于A、B而言,都統計出叢集中已經有兩台機器接受了(myid:2,zxid:0)的投票資訊,此時便認為已經選出了Leader;

  2. 改變選舉狀态

    在選舉開始前,會将每個節點的狀态修改為Looking狀态,選舉完畢後,如果當選為leader,則節點狀态為LEADING,follow節點為FOLLOWING狀态,OBSERVE為OBSERVING狀态,當伺服器為LOOKING狀态時,整個叢集停止對外提供服務。

伺服器運作期間,Leader崩潰,重新選舉過程

  1. 變更followering狀态為 LOOKING狀态,OBSERVING不參與投票,不需要變更;
  2. 每個follower節點發起投票,這一步也都是投自己;
  3. 每個follow接受叢集中的其他節點的投票,然後處理投票,處理投票的規則和初始狀态過程處理規則一樣,先比較zxid,如果zxid一緻,則比較myid,大者勝出,成為每個節點最終投票的節點;
  4. 統計投票,選出投票數最多的節點作為leader;
  5. 變更節點狀态;

資料同步(消息廣播)

  1. Leader收到消息後,或者是follow收到寫消息,轉發給Leader後,将會賦予消息一個全局64位自增zxid;
  2. leader位每個follow節點準備一個FIFO隊列,并将消息作為一個提案并帶上zxid發送給follow節點‘
  3. 當follow節點收到leader發過來的提案之後,會先把消息寫到磁盤中,然後給leader回複一個ack消息,代表我已經收到了消息,并儲存了下來;
  4. 當leader收到過半數的ack之後(這個和二段送出有點不同,二段送出要求收到所有的ack),就會像所有的follow發送commit消息,并本地執行該消息,并送出
  5. follow節點收到commit消息後,會比較commit攜帶的zxid是否是曆史隊列中最小的,如果是則執行,否則一緻等待,進而保證了順序執行。

崩潰恢複

崩潰恢複需要處理的兩個主要問題,這在網上也有很多文章說過,但是說的都不是很全面,我在這裡用自己的了解和大家說一遍,如果說的不對的歡迎指正:

已經被送出的提案不能被丢棄

假設有這種場景,如果在leader發出了commit之後,各個follow收到commit之前,leader挂掉了,導緻follow并沒有執行已經送出的提案。這個時候,這個消息是不能丢失的;

  • 解決方案

    leader失效後,重新選舉出來的leader肯定具備最大的zxid(不考慮這個zxid有沒有被送出),隻要zxid最大,那麼就會被選為leader(myid也得考慮,這裡不是重點),zxid最大說明這個節點肯定包括了所有的最新的提案,當這個節點當選為leader之後,新的leader會檢查自身有沒有未被送出的提案,如果有的,則會向叢集中發送請求,詢問其他follow節點是否存在其提案,如果超過半數回複ok,則執行送出操作,之後進行資料同步操作,這樣就保證了已經被送出的提案不會被丢失。

沒有被送出的提案應該被丢棄

假設有這種場景,如果在leader生成提案後,廣播之前,leader崩潰了,這個時候的提案是應該被丢棄了,這個ZAB協定時如何解決的呢?

  • 解決方案:

    Zab 通過巧妙的設計 zxid 來實作這一目的

    zxid占據64位,高32位存儲epoch編号,這個編号是每選舉出一次leader之後都會加一,有種朝代的感覺哈,低32位從0開始,當有新的請求或出現新的提案時,就會加1,但是重新選擇leader之後,就會進行清零;

    那麼zab時如何借助zxid來解決沒有被送出的提案應該丢棄的問題呢?

    在舊的leader重新開機後,因為已經經過一次新的選舉了,舊的leader所處的朝代已經落後了,新的leader會要求舊的leader将 它所處的朝代沒有被送出 的提案清除,重新同步最新的提案,這就保證了未被送出的提案進行丢棄;

繼續閱讀