天天看點

Apache ZooKeeper -從初始化到對外提供服務的過程解析( 叢集模式 )流程圖Pre什麼是叢集模式?ZooKeeper 叢集模式的特點底層實作原理Leader 伺服器啟動過程Follow 伺服器啟動過程小結

流程圖

Apache ZooKeeper -從初始化到對外提供服務的過程解析( 叢集模式 )流程圖Pre什麼是叢集模式?ZooKeeper 叢集模式的特點底層實作原理Leader 伺服器啟動過程Follow 伺服器啟動過程小結

Pre

Apache ZooKeeper -從初始化到對外提供服務的過程解析( 單機模式 )

我們知道了 ZooKeeper 在單機模式下從啟動運作到對外提供服務的整個過程。在日常工作中,無論是出于性能上的優勢還是可靠性的考慮,單機模式都無法滿足要求。是以,ZooKeeper 也采用叢集的方式運作。我們就來學習一下 ZooKeeper 叢集模式下,啟動過程的底層實作。

什麼是叢集模式?

為了解決單機模式下性能的瓶頸問題,以及出于對系統可靠性的高要求,叢集模式的系統架構方式被業界普遍采用。

叢集模式可以簡單了解為将單機系統複制成幾份,部署在不同主機或網絡節點上,最終構成了一個由多台計算機組成的系統“叢集”。而組成叢集中的每個伺服器叫作叢集中的網絡節點。

那問題來了 我們應該如何使用叢集?當用戶端發送一個請求到叢集伺服器的時候,究竟是哪個機器為我們提供服務呢?

為了解決這個問題,先介紹一個概念名詞“排程者”。排程者的工作職責就是在叢集收到用戶端請求後,根據目前叢集中機器的使用情況,決定将此次用戶端請求交給哪一台伺服器或網絡節點進行處理,例如我們都很熟悉的負載均衡伺服器就是一種排程者的實作方式。

ZooKeeper 叢集模式的特點

通過上面的介紹,我們知道了叢集是由網絡中不同機器組成的一個系統,叢集工作是通過叢集中排程者伺服器來協同工作的。

那麼現在我們來看一下 ZooKeeper 中的叢集模式,在 ZooKeeper 叢集模式中,相比上面說到的叢集架構方式,在 ZooKeeper 叢集中将伺服器分成 Leader 、Follow 、Observer 三種角色伺服器,在叢集運作期間這三種伺服器所負責的工作各不相同:

  • Leader 角色伺服器負責管理叢集中其他的伺服器,是叢集中工作的配置設定和排程者。
  • Follow 伺服器的主要工作是選舉出 Leader 伺服器,在發生 Leader 伺服器選舉的時候,系統會從 Follow 伺服器之間根據多數投票原則,選舉出一個 Follow 伺服器作為新的 Leader 伺服器。
  • Observer 伺服器則主要負責處理來自用戶端的擷取資料等請求,并不參與 Leader 伺服器的選舉操作,也不會作為候選者被選舉為 Leader 伺服器。

接下來我們看看在 ZooKeeper 中叢集是如何架構和實作的。

底層實作原理

到目前為止我們對 ZooKeeper 中叢集相關的知識有了大體的了解,接下來我們就深入到 ZooKeeper 的底層,看看在服務端,叢集模式是如何啟動到對外提供服務的。

在上一篇博文中已經對 ZooKeeper 單機版服務的啟動過程做了詳細的介紹。而叢集中的啟動過程和單機版的啟動過程有很多地方是一樣的。是以本次我們隻對 ZooKeeper 叢集中的特有實作方式做重點介紹。

程式啟動

首先,在 ZooKeeper 服務啟動後,系統會調用入口 QuorumPeerMain 類中的 main 函數。在 main 函數中的 initializeAndRun 方法中根據 zoo.cfg 配置檔案,判斷服務啟動方式是叢集模式還是單機模式。

在函數中首先根據 arg 參數和 config.isDistributed() 來判斷,如果配置參數中配置了相關的配置項,并且已經指定了叢集模式運作,那麼在服務啟動的時候就會跳轉到 runFromConfig 函數完成之後的叢集模式的初始化工作。

protected void initializeAndRun(String[] args){
  ...
  if (args.length == 1 && config.isDistributed()) {
            runFromConfig(config);
        } else {
            
            ZooKeeperServerMain.main(args);
        }
}           

複制

QuorumPeer 類

在 ZooKeeper 服務的叢集模式啟動過程中,一個最主要的核心類是 QuorumPeer 類。我們可以将每個 QuorumPeer 類的執行個體看作叢集中的一台伺服器。在 ZooKeeper 叢集模式的運作中,一個 QuorumPeer 類的執行個體通常具有 3 種狀态,分别是參與 Leader 節點的選舉、作為 Follow 節點同步 Leader 節點的資料,以及作為 Leader 節點管理叢集中的 Follow 節點。

介紹完 QuorumPeer 類後,下面我們看一下在 ZooKeeper 服務的啟動過程中,針對 QuorumPeer 類都做了哪些工作。如下面的代碼所示,在一個 ZooKeeper 服務的啟動過程中,首先調用 runFromConfig 函數将服務運作過程中需要的核心工具類注冊到 QuorumPeer 執行個體中去。

這些核心工具就是我們在單機版服務的啟動中介紹的諸如 FileTxnSnapLog 資料持久化類、ServerCnxnFactory 類 NIO 工廠方法等。這之後還需要配置伺服器位址清單、Leader 選舉算法、會話逾時時間等參數到 QuorumPeer 執行個體中。

public void runFromConfig(QuorumPeerConfig config){
  ServerCnxnFactory cnxnFactory = null;
  ServerCnxnFactory secureCnxnFactory = null;
  ...
  quorumPeer = getQuorumPeer()
  quorumPeer.setElectionType(config.getElectionAlg());
  quorumPeer.setCnxnFactory(cnxnFactory);
  ...
}           

複制

與開篇中提到的一般建構叢集的方式不同,ZooKeeper 将叢集中的機器分為 Leader 、 Follow 、Obervser 三種角色,每種角色伺服器在叢集中起到的作用都各不相同。

比如 Leader 角色伺服器主要負責處理用戶端發送的資料變更等事務性請求操作,并管理協調叢集中的 Follow 角色伺服器。

而 Follow 伺服器則主要處理用戶端的擷取資料等非事務性請求操作。

Observer 角色伺服器的功能和 Follow 伺服器相似,唯一的不同就是不參與 Leader 頭節點伺服器的選舉工作。

在 ZooKeeper 中的這三種角色伺服器,在服務啟動過程中也有各自的不同,下面我們就以 Leader 角色伺服器的啟動和 Follow 伺服器服務的啟動過程來看一下各自的底層實作原理。

Leader 伺服器啟動過程

在 ZooKeeper 叢集中,Leader 伺服器負責管理叢集中其他角色伺服器,以及處理用戶端的資料變更請求。是以,在整個 ZooKeeper 伺服器中,Leader 伺服器非常重要。是以在整個 ZooKeeper 叢集啟動過程中,首先要先選舉出叢集中的 Leader 伺服器。

在 ZooKeeper 叢集選舉 Leader 節點的過程中,首先會根據伺服器自身的伺服器 ID(SID)、最新的 ZXID、和目前的伺服器 epoch (currentEpoch)這三個參數來生成一個選舉标準。之後,ZooKeeper 服務會根據 zoo.cfg 配置檔案中的參數,選擇參數檔案中規定的 Leader 選舉算法,進行 Leader 頭節點的選舉操作。

而在 ZooKeeper 中提供了三種 Leader 選舉算法,分别是

  • LeaderElection
  • AuthFastLeaderElection
  • FastLeaderElection

在我們日常開發過程中,可以通過在 zoo.cfg 配置檔案中使用 electionAlg 參數屬性來制定具體要使用的算法類型。

在 ZooKeeper 叢集模式下服務啟動後。首先會建立用來選舉 Leader 節點的工具類 QuorumCnxManager 。

下面這段代碼給出了 QuorumCnxManager 在建立執行個體的時候首先要執行個體化 Listener 對象用于監聽 Leader 選舉端口。

package org.apache.zookeeper.server.quorum;
public class QuorumCnxManager {
...
  public QuorumCnxManager(QuorumPeer self) {
    String cnxToValue = System.getProperty("zookeeper.cnxTimeout")
    listener = new Listener();
    listener.setName("QuorumPeerListener");
  }
 ...
}           

複制

而在 ZooKeeper 中,Leader 選舉的大概過程,總體說來就是在叢集中的所有機器中直接進行一次選舉投票,選舉出一個最适合的機器作為 Leader 節點。

而具體的評價标準就是我們上面提到的三種選舉算法。而從 3.4.0 版本開始,ZooKeeper 隻支援 FastLeaderElection 這一種選舉算法。同時沒有被選舉為 Leader 節點的機器則作為 Follow 或 Observer 節點機器存在。

Follow 伺服器啟動過程

現在,我們已經選舉出 ZooKeeper 叢集模式下的 Leader 節點機器了。我們再看一下 Follow 節點機器在 ZooKeeper 叢集模式下伺服器的啟動過程。

在伺服器的啟動過程中,Follow 機器的主要工作就是和 Leader 節點進行資料同步和互動。當 Leader 機器啟動成功後,Follow 節點的機器會收到來自 Leader 節點的啟動通知。而該通知則是通過 LearnerCnxAcceptor 類來實作的。該類就相當于一個接收器。專門用來接收來自叢集中 Leader 節點的通知資訊。

下面這段代碼中 LearnerCnxAcceptor 類首先初始化要監聽的 Leader 伺服器位址和設定收到監聽的處理執行方法等操作 。

class LearnerCnxAcceptor extends ZooKeeperCriticalThread {
    private volatile boolean stop = false;
    public LearnerCnxAcceptor() {
        super("LearnerCnxAcceptor-" + ss.getLocalSocketAddress(), zk
                .getZooKeeperServerListener());
    }
 }              

複制

在接收到來自 Leader 伺服器的通知後,Follow 伺服器會建立一個 LearnerHandler 類的執行個體,用來處理與 Leader 伺服器的資料同步等操作。

package org.apache.zookeeper.server.quorum;
public class LearnerHandler extends ZooKeeperThread {
  protected final Socket sock;
  final Leader leader;
  ...
}           

複制

在完成資料同步後,一個 ZooKeeper 服務的叢集模式下啟動的關鍵步驟就完成了,整個服務就處于運作狀态,可以對外提供服務了。

小結

主要學習了 ZooKeeper 叢集模式的啟動過程和底層實作。與一般的叢集架構不同,ZooKeeper 叢集模式把其中的機器分成 Leader 、Follow 、Obsever 三種角色。

當作為 Leader 節點的機器失效時,系統會根據配置檔案中的選舉算法産生新的節點。這種方式避免了一般叢集模式中産生的單點失效等問題。

那在我們日常使用 ZooKeeper 叢集伺服器的時候,叢集中的機器個數應該如何選擇?

答案是最好使用奇數原則,最小的叢集配置應該是三個伺服器或者節點。而如果采用偶數,在 Leader 節點選舉投票的過程中就不滿足大多數原則,這時就産生“腦裂”這個問題。