演算法領頭羊丨分散式系統如何選舉領導?

微軟技術棧發表於2022-04-09

大家好,我是本期的實驗室研究員——李帥。領導選舉是分散式系統中最棘手的事情之一。同時,理解 Leader 是如何選舉產生的以及Leader 的職責,是理解分散式系統的關鍵。

828d432e498755590115256b84c814f.png


在分散式系統中,通常一個服務由多個節點或例項組成服務叢集,提供可擴充套件性、高可用的服務。

這些節點可以同時工作,提升服務處理、計算能力,但是,如果這些節點同時操作共享資源時,那就必須要協調它們的操作,防止每個節點覆蓋其他節點所做的更改,從而產生資料錯亂的問題。

所以,我們需要在所有節點中選出一個領導者 Leader,來管理、協調叢集的所有節點,這種也是最常見的 Master-Slave 架構。

在分散式環境中的多個節點中選取主節點 Leader,通常會使用以下幾種策略:

  1. 根據程式 Id 或者例項 Id,選擇最大值,或者最小值作為主節點。
  2. 實現一種常見的領導者選舉演算法,比如 Bully 等。
  3. 通過分散式互斥鎖,保證只有一段時間只有一個節點可以獲取到鎖,併成為主節點。

在本文中,我會介紹幾種常見的選舉演算法,包括 ZAB、Bully、Token Ring Election。

Bully 演算法

Garcia-Monila 在 1982 年的一篇論文中發明了 Bully 演算法,這是分散式系統中很常見的選舉演算法,它的選舉原則是“長者”為大,也就是在所有存活的節點中,選取 ID 最大的節點作為主節點。

假如有一個叢集, 各個節點可以相互連線,並且每個節點都知道其他節點的資訊( Id 和節點地址),如下:

叢集初始化時,各個節點首先判斷自己是不是存活的節點中 ID 最大的,如果是,就向其他節點傳送 Victory 訊息,宣佈自己成為主節點,根據規則,此時,叢集中的 P6 節點成為 Master 主節點。

現在叢集中出現了一些故障,導致節點下線。如果下線的是從節點, 叢集還是一主多從,影響不大, 但是如果下線的是 P6 主節點,那就變成了一個群龍無首的場面。

現在我們需要重新選舉一個主節點!

我們的節點是可以相互連線,並且節點間定時進行心跳檢查, 此時 P3 節點檢測到 P6 主節點失敗,然後 P3 節點就發起了新的選舉。

首先 P3 會向比自己 ID 大的所有節點傳送 Election 訊息。

由於 P6 已經下線,請求無響應,而 P4,P5 可以接收到 Election 請求,並響應 Alive 訊息。P3 節點收到訊息後,停止選舉,因為現在有比自己 Id 大的節點存活,他們接管了選舉。

接下來,P4 節點向 P5 和 P6 節點傳送選舉訊息。

P5 節點響應 Alive 訊息,並接管選舉。

同樣,P5 節點向 P6 節點傳送選舉訊息。

此時,P6 節點沒有響應,而現在 P5 是存活節點中 ID 最大的節點,所以 P5 理所應當成為了新的主節點,並向其他節點傳送 Victory 訊息,宣佈自己成為 Leader !

一段時間後,故障恢復,P6 節點重新上線,因為自己是 ID 最大的節點, 所以直接向其他節點傳送 Victory 訊息,宣佈自己成為主節點,而 P5 收到比自己 ID 大的節點發起的選舉請求後,降級變成從節點。

Token Ring 演算法

Token Ring Election 演算法和叢集節點的網路拓撲有較大關係,它的特點是,所有的節點組成一個環,而每個節點知道下游節點,並能與之通訊,如下:

叢集初始化的時候,其中一個節點會向下一個節點先發起選舉訊息,其中包含了當前節點的 ID,下一個節點收到訊息後,會在訊息中附加上自己的 ID,然後繼續往下傳遞,最終形成閉環。

本次選舉從 P3 節點發起。

P3 節點收到 P4 的訊息後,發現訊息中包含自己的節點 ID,可以確定選舉訊息已經走了整個環,這時還是按照 “長者為大” 的原則,從訊息 "3,6,5,2,1,4" 中選取最大的 Id 為主節點,也就是選舉 P6 為 Leader。

接下來,P3 節點向下遊節點傳送訊息,宣佈 P6 是主節點,直到訊息走了整個環,回到 P3,至此,本次選舉完成。

現在叢集中出現了一些故障, 導致主節點 P6 下線,位於上游的 P3 節點首先發現了(通過心跳檢查),然後 P3 節點重新發起選舉,當下遊的 P3 節點無法連線時,會嘗試連線下游的下游節點 P5,傳送選舉訊息,並帶上自己的節點 Id,訊息逐步往下游傳遞。

直到選舉訊息重新回到 P3 節點,從 "3,5,2,1,4" 節點列表中選取最大的 ID,也就是現在 P5 成為主節點。

接下來,P3 節點向下遊節點傳送訊息,宣佈 P5 是主節點。

直到訊息走了整個環,回到 P3,至此,本次選舉完成。

ZAB - ZooKeeper的原子廣播協議

眾所周知,Apache ZooKeeper 是雲端計算的分散式框架, 它的核心是一個基於 Paxos 實現的原子廣播協議(ZooKeeper Atomic Broadcast),但實際上它既不是 Basic-Paxos 也不是 Multi-Paxos。

目前在 ZooKeeper 中有兩種領導選舉演算法:LeaderElection 和 FastLeaderElection(預設), 而 FastLeaderElection 選舉演算法是標準的 Fast-Paxos演算法實現。

下面我會介紹 ZAB 協議中的領導選舉的實現。

首先,我們有三個節點,S1,S2,S3 , 每個節點在本地都有一份資料和投票箱,資料包括myid, zxid 和 epoch。

  • myid 每個節點初始化的時候需要配置自己的節點 Id,不重複的數字。
  • epoch 選舉的輪數,預設為0,選舉時做累加操作。epoch 的大小可以表示選舉的先後順序。
  • zxid ZooKeeper 的全域性事務Id, 64位不重複的數字,前 32 位是 epoch,後32位是 count 計數器, zxid 是怎麼做到全域性唯一的呢?實際上叢集選中 Leader 後,一個寫的操作,首先會統一在 Leader 節點遞增 zxid,然後同步到 Follower 節點,在一個節點上保證一個數字遞增並且不重複就簡單多了, zxid 的大小可以表示事件發生的先後順序。

現在開始投票,投票內容就是上面說的節點的本地資料,【myid,zxid,epoch】, 每個節點先給自己投一票,並放到自己的投票箱,然後把這張票廣播到其他節點。

一輪投票交換後,現在,每個節點的投票箱都有所有節點的投票。

根據投票箱裡的投票的節點資訊,進行競爭,規則如下:

首先會對比 zxid,zxid 最大的勝出(zxid越大,表示資料越新), 如果 zxid 相同,再比較 myid (也就是 節點的 serverId),myid 較大的則勝出, 然後更新自己的投票結果,再次向其他節點廣播自己更新後的投票 。

節點 S3:根據競爭規則,勝出的票是 S3 自己,就無需更新本地投票和再次廣播。

節點 S1 和 S2: 根據競爭規則, 重新投票給 S3,覆蓋之前投給自己的票,再次把投票廣播出去。

注意,如果接收到同一個節點同一輪選舉的多次投票,那就用最後的投票覆蓋之前的投票。

此時,節點 S3 收到節點 S1和S2的重新投票,都是投給自己,符合 "過半原則",節點 S3 成為 Leader,而 S1 和 S2 變成 Follower, 同時 Leader 向 Follower 定時傳送心跳進行檢查。

總結

本文主要介紹了分散式系統中幾個經典的領導選舉演算法,ZAB、Bully、Token Ring Election, 選舉規則有的是 "長者為大",而有的是 "民主投票",少數服從多數, 大家可以對比他們的優勢和缺點,在實際應用中選擇合適的選舉演算法。

為什麼沒有介紹 Paxos 演算法呢?因為 Paxos 是共識演算法,而 Basic-Paxos 中,是不需要 Leader 節點即可達成共識,可謂 "眾生平等", 而在 Multi-Paxos 中提到的 Leader 概念,也僅僅是為了提高效率。當然 Paxos 是非常重要的,可以說它是分散式系統的根基。

微軟MVP,期待你加入

相關文章