構建高可用ZooKeeper集群

来源:http://www.cnblogs.com/cyfonly/archive/2016/06/30/5626532.html
-Advertisement-
Play Games

ZooKeeper 是 Apache 的一個頂級項目,為分散式應用提供高效、高可用的分散式協調服務,提供了諸如數據發佈/訂閱、負載均衡、命名服務、分散式協調/通知和分散式鎖等分散式基礎服務。由於 ZooKeeper 便捷的使用方式、卓越的性能和良好的穩定性,被廣泛地應用於諸如 Hadoop、HBas... ...


   ZooKeeper 是 Apache 的一個頂級項目,為分散式應用提供高效、高可用的分散式協調服務,提供了諸如數據發佈/訂閱、負載均衡、命名服務、分散式協調/通知和分散式鎖等分散式基礎服務。由於 ZooKeeper 便捷的使用方式、卓越的性能和良好的穩定性,被廣泛地應用於諸如 Hadoop、HBase、Kafka 和 Dubbo 等大型分散式系統中。

  本文的目標讀者是對 ZooKeeper 有一定瞭解的技術人員,將從 ZooKeeper 運行模式、集群組成、容災和水平擴容四方面逐步深入,最終構建出高可用的 ZooKeeper 集群。

運行模式

  Zookeeper 有三種運行模式:單機模式、偽集群模式和集群模式。

單機模式

  這種模式一般適用於開發測試環境,一方面我們沒有那麼多機器資源,另外就是平時的開發調試並不需要極好的穩定性。

  在 Linux 環境下運行單機模式需要執行以下步驟:

  1. 準備 Java 運行環境

    安裝 Java 1.6 或更高版本的 JDK,並配置好 Java 相關的環境變數 $JAVA_HOME 。

  2. 下載 ZooKeeper 安裝包

    下載地址:http://zookeeper.apache.org/releases.html。選擇最新的 stable 版本並解壓到指定目錄,我們用 $ZK_HOME 表示該目錄。

  3. 配置 zoo.cfg

    首次使用 ZooKeeper,需要將 $ZK_HOME 下的 zoo_sample.cfg 文件重命名為 zoo.cfg,併進行以下配置

tickTime=2000    ##Zookeeper最小時間單元,單位毫秒(ms),預設值為3000
dataDir=/var/lib/zookeeper    ##Zookeeper伺服器存儲快照文件的目錄,必須配置
dataLogDir=/var/lib/log ##Zookeeper伺服器存儲事務日誌的目錄,預設為dataDir clientPort=2181 ##伺服器對外服務埠,一般設置為2181 initLimit=5 ##Leader伺服器等待Follower啟動並完成數據同步的時間,預設值10,表示tickTime的10倍 syncLimit=2 ##Leader伺服器和Follower之間進行心跳檢測的最大延時時間,預設值5,表示tickTime的5倍

  4. 啟動服務

    使用 $ZK_HOME/bin 目錄下的 zkServer.sh 腳本進行服務的啟動。

集群模式

  一個 ZooKeeper 集群通常由一組機器組成,一般 3 台以上就可以組成一個可用的 ZooKeeper 集群了。

  組成 ZooKeeper 集群的每台機器都會在記憶體中維護當前的伺服器狀態,並且每台機器之間都會互相保持通信。重要的一點是,只要集群中存在超過一半的機器能夠正常工作,那麼整個集群就能夠正常對外服務。

  ZooKeeper 的客戶端程式會選擇和集群中的任意一臺伺服器創建一個 TCP 連接,而且一旦客戶端和伺服器斷開連接,客戶端就會自動連接到集群中的其他伺服器。

  那麼如何運行 ZooKeeper 集群模式呢?首先假如我們有三台伺服器,IP 分別為 IP1、IP2 和 IP3,則需要執行以下步驟:

  1. 準備 Java 運行環境(同上)

  2. 下載 ZooKeeper 安裝包(同上)

  3. 配置 zoo.cfg

tickTime=2000
dataDir=/var/lib/zookeeper
dataLogDir=/var/lib/log
clientPort=2181
initLimit=5
syncLimit=2
server.1=IP1:2888:3888
server.2=IP2:2888:3888
server.3=IP3:2888:3888

  可以看到,相比於單機模式,集群模式多了 server.id=host:port1:port2 的配置。其中,id 被稱為 Server ID,用來標識該機器在集群中的機器序號(在每台機器的 dataDir 目錄下創建 myid 文件,文件內容即為該機器對應的 Server ID 數字)。host 為機器 IP,port1 用於指定 Follower 伺服器與 Leader 伺服器進行通信和數據同步的埠,port2 用於進行 Leader 選舉過程中的投票通信。

  4. 創建 myid 文件

    在 dataDir 目錄下創建名為 myid 的文件,在文件第一行寫上對應的 Server ID。

  5. 按照相同步驟,為其他機器配置 zoo.cfg 和 myid文件

  6. 啟動服務

偽集群模式

  這是一種特殊的集群模式,即集群的所有伺服器都部署在一臺機器上。當你手頭上有一臺比較好的機器,如果作為單機模式進行部署,就會浪費資源,這種情況下,ZooKeeper允許你在一臺機器上通過啟動不同的埠來啟動多個 ZooKeeper 服務實例,以此來以集群的特性來對外服務。

  這種模式下,只需要把 zoo.cfg 做如下修改:

tickTime=2000
dataDir=/var/lib/zookeeper
dataLogDir=/var/lib/log
clientPort=2181
initLimit=5
syncLimit=2
server.1=IP1:2888:3888
server.2=IP1:2889:3889
server.3=IP1:2890:3890

集群組成

  要搭建一個高可用的 ZooKeeper 集群,我們首先需要確定好集群的規模。關於 ZooKeeper 集群的伺服器組成,相信很多對 ZooKeeper 瞭解但是理解不夠深入的讀者,都存在或曾經存在過這樣一個錯誤的認識:為了使得 ZooKeeper 集群能夠順利地選舉出 Leader,必須將 ZooKeeper 集群的伺服器數部署成奇數這裡我們需要澄清的一點是:任意台 ZooKeeper 伺服器都能部署且能正常運行。

  那麼存在於這麼多讀者中的這個錯誤認識是怎麼回事呢?其實關於 ZooKeeper 集群伺服器數,ZooKeeper 官方確實給出了關於奇數的建議,但絕大部分 ZooKeeper 用戶對於這個建議認識有偏差。在本書前面提到的“過半存活即可用”特性中,我們已經瞭解了,一個 ZooKeeper 集群如果要對外提供可用的服務,那麼集群中必須要有過半的機器正常工作並且彼此之間能夠正常通信。基於這個特性,如果想搭建一個能夠允許 N 台機器 down 掉的集群,那麼就要部署一個由 2*N+1 台伺服器構成的 ZooKeeper 集群。因此,一個由 3 台機器構成的 ZooKeeper 集群,能夠在掛掉 1 台機器後依然正常工作,而對於一個由 5 台伺服器構成的 ZooKeeper 集群,能夠對 2 台機器掛掉的情況進行容災。註意,如果是一個由6台伺服器構成的 ZooKeeper 集群,同樣只能夠掛掉 2 台機器,因為如果掛掉 3 台,剩下的機器就無法實現過半了。

  因此,從上面的講解中,我們其實可以看出,對於一個由 6 台機器構成的 ZooKeeper 集群來說,和一個由 5 台機器構成的 ZooKeeper 集群,其在容災能力上並沒有任何顯著的優勢,反而多占用了一個伺服器資源。基於這個原因,ZooKeeper 集群通常設計部署成奇數台伺服器即可。

容災

  所謂容災,在 IT 行業通常是指我們的電腦信息系統具有的一種在遭受諸如火災、地震、斷電和其他基礎網路設備故障等毀滅性災難的時候,依然能夠對外提供可用服務的能力。

  對於一些普通的應用,為了達到容災標準,通常我們會選擇在多台機器上進行部署來組成一個集群,這樣即使在集群的一臺或是若幹台機器出現故障的情況下,整個集群依然能夠對外提供可用的服務。

  而對於一些核心應用,不僅要通過使用多台機器構建集群的方式來提供服務,而且還要將集群中的機器部署在兩個機房,這樣的話,即使其中一個機房遭遇災難,依然能夠對外提供可用的服務。

  上面講到的都是應用層面的容災模式,那麼對於 ZooKeeper 這種底層組件來說,如何進行容災呢?講到這裡,可能多少讀者會有疑問,ZooKeeper 既然已經解決了單點問題,那為什麼還要進行容災呢?

單點問題

  單點問題是分散式環境中最常見也是最經典的問題之一,在很多分散式系統中都會存在這樣的單點問題。具體地說,單點問題是指在一個分散式系統中,如果某一個組件出現故障就會引起整個系統的可用性大大下降甚至是處於癱瘓狀態,那麼我們就認為該組件存在單點問題。

  ZooKeeper 確實已經很好地解決了單點問題。我們已經瞭解到,基於“過半”設計原則,ZooKeeper 在運行期間,集群中至少有過半的機器保存了最新的數據。因此,只要集群中超過半數的機器還能夠正常工作,整個集群就能夠對外提供服務。

容災

  解決了單點問題,是不是該考慮容災了呢?答案是否定的,在搭建一個高可用的集群的時候依然需要考慮容災問題。正如上面講到的,如果集群中超過半數的機器還在正常工作,集群就能夠對外提供正常的服務。那麼,如果整個機房出現災難性的事故,這時顯然已經不是單點問題的範疇了。

  在進行 ZooKeeper 的容災方案設計過程中,我們要充分考慮到“過半原則”。也就是說,無論發生什麼情況,我們必須保證 ZooKeeper 集群中有超過半數的機器能夠正常工作。因此,通常有以下兩種部署方案。

雙機房部署

  在進行容災方案的設計時,我們通常是以機房為單位來考慮問題。在現實中,很多公司的機房規模並不大,因此雙機房部署是個比較常見的方案。但是遺憾的是,在目前版本的 ZooKeeper 中,還沒有辦法能夠在雙機房條件下實現比較好的容災效果——因為無論哪個機房發生異常情況,都有可能使得 ZooKeeper 集群中可用的機器無法超過半數。當然,在擁有兩個機房的場景下,通常有一個機房是主要機房(一般而言,公司會花費更多的錢去租用一個穩定性更好、設備更可靠的機房,這個機房就是主要機房,而另外一個機房則更加廉價一些)。我們唯一能做的,就是儘量在主要機房部署更多的機器。例如,對於一個由 7 台機器組成的 ZooKeeper 集群,通常在主要機房中部署 4 台機器,剩下的 3 台機器部署到另外一個機房中。

三機房部署

  既然在雙機房部署模式下並不能實現好的容災效果,那麼對於有條件的公司,選擇三機房部署無疑是個更好的選擇,無論哪個機房發生了故障,剩下兩個機房的機器數量都超過半數。假如我們有三個機房可以部署服務,並且這三個機房間的網路狀況良好,那麼就可以在三個機房中都部署若幹個機器來組成一個 ZooKeeper 集群。

  我們假定構成 ZooKeeper 集群的機器總數為 N,在三個機房中部署的 ZooKeeper 伺服器數分別為 N1、N2 和 N3,如果要使該 ZooKeeper 集群具有較好的容災能力,我們可以根據如下演算法來計算 ZooKeeper 集群的機器部署方案。

1. 計算 N1

  如果 ZooKeeper 集群的伺服器總數是 N,那麼:

N1 = (N-1)/2

  在 Java 中,“/” 運算符會自動對計算結果向下取整操作。舉個例子,如果 N=8,那麼 N1=3;如果 N=7,那麼 N1 也等於 3。

2. 計算 N2 的可選值

  N2 的計算規則和 N1 非常類似,只是 N的取值是在一個取值範圍內:

N2 的取值範圍是 1~(N-N1)/2

  即如果 N=8,那麼 N1=3,則 N2 的取值範圍就是 1~2,分別是 1 和 2。註意,1 和 2 僅僅是 N2 的可選值,並非最終值——如果 N2 為某個可選值的時候,無法計算出 N3 的值,那麼該可選值也無效。

3. 計算 N3,同時確定 N2 的值

   很顯然,現在只剩下 N3 了,可以簡單的認為 N3 的取值就是剩下的機器數,即:

N3 = N - N1 - N2

  只是 N3 的取值必須滿足 N3 < N1+N2。在滿足這個條件的基礎下,我們遍歷步驟 2 中計算得到的 N2 的可選值,即可得到三機房部署時每個機房的伺服器數量了。

  現在我們以 7 台機器為例,來看看如何分配三機房的機器分佈。根據演算法的步驟 1,我們首先確定 N1 的取值為 3。根據演算法的步驟 2,我們確定了 N2 的可選值為 1 和 2。最後根據步驟 3,我們遍歷 N2 的可選值,即可得到兩種部署方案,分別是 (3,1,3) 和 (3,2,2)。以下是 Java 程式代碼對以上演算法的一種簡單實現:

public class Allocation {
    
    static final int n = 7;
    public static void main(String[] args){
        int n1,n2,n3;
        n1 = (n-1) / 2;
        int n2_max = (n-n1) / 2;
        for(int i=1; i<=n2_max; i++){
            n2 = i;
            n3 = n - n1 -n2;
            if(n3 >= (n1+n2)){
                continue;
            }
            System.out.println("("+n1+","+n2+","+n3+")");
        }
    }
}

水平擴容

  水平可擴容可以說是對一個分散式系統在高可用性方面提出的基本的,也是非常重要的一個要求,通過水平擴容能夠幫助系統在不進行或進行極少改進工作的前提下,快速提高系統對外的服務支撐能力。簡單地講,水平擴容就是向集群中添加更多的機器,以提高系統的服務質量。

  很遺憾的是,ZooKeeper 在水平擴容擴容方面做得並不十分完美,需要進行整個集群的重啟。通常有兩種重啟方式,一種是集群整體重啟,另外一種是逐台進行伺服器的重啟。

整體重啟

  所謂集群整體重啟,就是先將整個集群停止,然後更新 ZooKeeper 的配置,然後再次啟動。如果在你的系統中,ZooKeeper 並不是個非常核心的組件,並且能夠允許短暫的服務停止(通常是幾秒鐘的時間間隔),那麼不妨選擇這種方式。在整體重啟的過程中,所有該集群的客戶端都無法連接上集群。等到集群再次啟動,這些客戶端就能夠自動連接上——註意,整體啟動前建立起的客戶端會話,並不會因為此次整體重啟而失效。也就是說,在整體重啟期間花費的時間將不計入會話超時時間的計算中。

逐台重啟

  這種方式更適合絕大多數的實際場景。在這種方式中,每次僅僅重啟集群中的一臺機器,然後逐台對整個集群中的機器進行重啟操作。這種方式可以在重啟期間依然保證集群對外的正常服務。

 

 

 

參考文章:《從Paxos到Zookeeper  分散式一致性原理與實踐》

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • Linux編程時候,如果我們需要調用shell命令或腳本通常使用system方法。如system("ls") 該方法返回值為0或-1,即成功或失敗。而有的時候我們想要獲取shell命令執行的結果,該怎麼辦呢? 我們可以將shell命令結果重定向到文件中,然後再讀取這個文件,如: system("ls ...
  • CentOS_配置_docker CentOS_6.5 1、CentOS_6.5在安裝docker-io之前需要首先卸載docker包(沒下載過可以省略) $ sudo yum -y remove docker 2、CentOS_6.5在安裝docker之前需要首先安裝並啟用EPEL源 $ yum ...
  • 什麼是運算符 什麼是運算符?運算符是告訴PHP做相關運算的標識符號。例如,你需要計算123乘以456等於多少,這時候就需要一個符號,告訴伺服器,你需要做乘法運算。 PHP中的運算符有哪些?PHP運算符一般分為算術運算符、賦值運算符、比較運算符、三元運算符、邏輯運算符、字元串連接運算符、錯誤控制運算符 ...
  • 恢復內容開始 輸入需要用scanner機制 代碼: 啟用scanner機制 Scanner input = new Scanner(System.in); //String x= input.next(); //String x = input.nextLine(); //int num = inp ...
  • 簡介 最小(少)原則,是安全的重要原則。最小的許可權,最小的用戶,最少的服務,最少的進程,是最安全的。 系統安全包括:文件系統保護、用戶管理安全、進程的保護以及日誌的管理。 場景 1. 確保服務最少,每個都是有用,而且許可權最小化。 2. 確保用戶最少,每個都是有用,而且許可權最小化。 3. 確保文件許可權 ...
  • 之前已經介紹過, skynet 只是一個輕量框架,不是一個開箱即用的引擎 。能不能用好它,取決於使用者是否清楚知道自己要乾什麼,如果是用 skynet 做網路游戲伺服器,那麼就必須先知道網路游戲伺服器應該如何設計。 在 skynet 發佈版中帶的 example 中,有類似 gate watchdo ...
  • 資源的表現層狀態轉化。 簡單的理解即: 1 URI對應一種"資源"。 2 客戶端與服務端傳輸資源的某種"表現層"。 3 客戶端通過HTTP協議的動詞,對資源進行操作,實現"表現層狀態轉化" 。 ...
  • 上一篇:《 "DDD 領域驅動設計-領域模型中的用戶設計?" 》 開源地址: "https://github.com/yuezhongxin/CNBlogs.Apply.Sample" (代碼已更新) 在之前的項目開發中,只有一個 JsPermissionApply 實體(JS 許可權申請),所以,C ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...