ZooKeeper 基礎知識、部署和應用程式

風靈使發表於2018-07-28

簡介

讓我們首先討論一下為什麼想使用 ZooKeeperZooKeeper 是一個面向分散式系統的構建塊。當設計一個分散式系統時,一般需要設計和開發一些協調服務:

  • 名稱服務— 名稱服務是將一個名稱對映到與該名稱有關聯的一些資訊的服務。電話目錄是將人的名字對映到其電話號碼的一個名稱服務。同樣,DNS服務也是一個名稱服務,它將一個域名對映到一個 IP 地址。在分散式系統中,您可能想跟蹤哪些伺服器或服務在執行,並通過名稱檢視其狀態。ZooKeeper暴露了一個簡單的介面來完成此工作。也可以將名稱服務擴充套件到組成員服務,這樣就可以獲得與正在查詢其名稱的實體有關聯的組的資訊。
  • 鎖定— 為了允許在分散式系統中對共享資源進行有序的訪問,可能需要實現分散式互斥(distributed mutexes)。ZooKeeper 提供一種簡單的方式來實現它們。
  • 同步— 與互斥同時出現的是同步訪問共享資源的需求。無論是實現一個生產者-消費者佇列,還是實現一個障礙,ZooKeeper 都提供一個簡單的介面來實現該操作。您可以在 Apache ZooKeeper 維基上檢視示例,瞭解如何做到這一點(參閱 參考資料)。
  • 配置管理— 您可以使用 ZooKeeper 集中儲存和管理分散式系統的配置。這意味著,所有新加入的節點都將在加入系統後就可以立即使用來自ZooKeeper的最新集中式配置。這還允許您通過其中一個 ZooKeeper 客戶端更改集中式配置,集中地更改分散式系統的狀態。
  • 領導者選舉— 分散式系統可能必須處理節點停機的問題,您可能想實現一個自動故障轉移策略。ZooKeeper 通過領導者選舉對此提供現成的支援。

雖然可以從頭開始設計和實現所有這些服務,但除錯任何問題、競爭條件或死鎖都需要執行額外的工作,並且很難實現。就像您不會在程式碼中隨處編寫自己的隨機數發生器或雜湊函式一樣,這裡有一個要求:人們不應該在每次有需要時就到處從頭編寫自己的名稱服務或領導者選舉服務。此外,您可以相對容易地一起解決一個非常簡單的組成員服務,但是,要編寫它們來提供可靠性、複製和可擴充套件性,可能需要做更多的工作。這導致了 Apache ZooKeeper 的開發和開源,Apache ZooKeeper 是一個針對分散式系統的、開箱即用的、可靠的、可擴充套件的、高效能的協調服務。

InfoSphere® BigInsights™ Quick Start EditionIBM 的大資料產品,以開源的 Apache Hadoop 專案為基礎。它包括 ZooKeeper 和其他大資料技術,以及增加了該平臺的價值的 IBM 技術。在本文中,我們只是使用了 ZooKeeper,但是,如欲瞭解有關 InfoSphere BigInsights 的更多資訊,請參閱 參考資料,其中包括一個下載產品的連結。

ZooKeeper 雖然是一個針對分散式系統的協調服務,但它本身也是一個分散式應用程式。ZooKeeper 遵循一個簡單的客戶端-伺服器模型,其中客戶端 是使用服務的節點(即機器),而伺服器 是提供服務的節點。ZooKeeper 伺服器的集合形成了一個 ZooKeeper 集合體(ensemble)。在任何給定的時間內,一個 ZooKeeper 客戶端可連線到一個 ZooKeeper 伺服器。每個 ZooKeeper 伺服器都可以同時處理大量客戶端連線。每個客戶端定期傳送 ping 到它所連線的 ZooKeeper 伺服器,讓伺服器知道它處於活動和連線狀態。被詢問的 ZooKeeper 伺服器通過 ping 確認進行響應,表示伺服器也處於活動狀態。如果客戶端在指定時間內沒有收到伺服器的確認,那麼客戶端會連線到集合體中的另一臺伺服器,而且客戶端會話會被透明地轉移到新的 ZooKeeper 伺服器。

圖 1 描述了 ZooKeeper 的客戶端-伺服器架構。

圖 1. ZooKeeper 的客戶端-伺服器架構

ZooKeeper 有一個類似於檔案系統的資料模型,由 znodes 組成。可以將 znodesZooKeeper 資料節點)視為類似 UNIX 的傳統系統中的檔案,但它們可以有子節點。另一種方式是將它們視為目錄,它們可以有與其相關的資料。每個這些目錄都被稱為一個 znode。圖 2 顯示的圖代表與兩個城市中的運動隊相同的層次結構。
圖 2. 該圖表示了兩個城市中的運動隊的層次結構
影象顯示了兩個城市中的運動隊的層次結構

znode 層次結構被儲存在每個 ZooKeeper 伺服器的記憶體中。這實現了對來自客戶端的讀取操作的可擴充套件的快速響應。每個 ZooKeeper 伺服器還在磁碟上維護了一個事務日誌,記錄所有的寫入請求。因為 ZooKeeper 伺服器在返回一個成功的響應之前必須將事務同步到磁碟,所以事務日誌也是 ZooKeeper 中對效能最重要的組成部分。可以儲存在 znode 中的資料的預設最大大小為 1 MB。因此,即使 ZooKeeper 的層次結構看起來與檔案系統相似,也不應該將它用作一個通用的檔案系統。相反,應該只將它用作少量資料的儲存機制,以便為分散式應用程式提供可靠性、可用性和協調。

當客戶端請求讀取特定 znode 的內容時,讀取操作是在客戶端所連線的伺服器上進行的。因此,由於只涉及集合體中的一個伺服器,所以讀取是快速和可擴充套件的。然而,為了成功完成寫入操作,要求 ZooKeeper 集合體的嚴格意義上的多數節點都是可用的。在啟動 ZooKeeper 服務時,集合體中的某個節點被選舉為領導者。當客戶端發出一個寫入請求時,所連線的伺服器會將請求傳遞給領導者。此領導者對集合體的所有節點發出相同的寫入請求。如果嚴格意義上的多數節點(也被稱為法定數量(quorum))成功響應該寫入請求,那麼寫入請求被視為已成功完成。然後,一個成功的返回程式碼會返回給發起寫入請求的客戶端。如果集合體中的可用節點數量未達到法定數量,那麼 ZooKeeper 服務將不起作用。

法定數量是通過嚴格意義上的多數節點來表示的。在集合體中,可以包含一個節點,但它不是一個高可用和可靠的系統。如果在集合體中有兩個節點,那麼這兩個節點都必須已經啟動並讓服務正常執行,因為兩個節點中的一個並不是嚴格意義上的多數。如果在集合體中有三個節點,即使其中一個停機了,您仍然可以獲得正常執行的服務(三個中的兩個是嚴格意義上的多數)。出於這個原因,ZooKeeper 的集合體中通常包含奇數數量的節點,因為就容錯而言,與三個節點相比,四個節點並不佔優勢,因為只要有兩個節點停機,ZooKeeper 服務就會停止。在有五個節點的叢集上,需要三個節點停機才會導致 ZooKeeper 服務停止運作。

現在,我們已經清楚地瞭解到,節點數量應該是奇數,讓我們再來思考一下 ZooKeeper 集合體中需要有多少個節點。讀取操作始終從連線到客戶端的 ZooKeeper 伺服器讀取資料,所以它們的效能不會隨著集合體中的伺服器數量額變化而變化。但是,僅在寫入法定數量的節點時,寫入操作才是成功的。這意味著,隨著在集合體中的節點數量的增加,寫入效能會下降,因為必須將寫入內容寫入到更多的伺服器中,並在更多伺服器之間進行協調。

ZooKeeper 的美妙之處在於,想執行多少伺服器完全由您自己決定。如果想執行一臺伺服器,從 ZooKeeper 的角度來看是沒問題的;只是您的系統不再是高度可靠或高度可用的。三個節點的 ZooKeeper 集合體支援在一個節點故障的情況下不丟失服務,這對於大多數使用者而言,這可能是沒問題的,也可以說是最常見的部署拓撲。不過,為了安全起見,可以在您的集合體中使用五個節點。五個節點的集合體讓您可以拿出一臺伺服器進行維護或滾動升級,並能夠在不中斷服務的情況下承受第二臺伺服器的意外故障。

因此,在 ZooKeeper 集合體中,三、五或七是最典型的節點數量。請記住,ZooKeeper 集合體的大小與分散式系統中的節點大小沒有什麼關係。分散式系統中的節點將是 ZooKeeper 集合體的客戶端,每個 ZooKeeper 伺服器都能夠以可擴充套件的方式處理大量客戶端。例如,HBaseHadoop 上的分散式資料庫)依賴​​於 ZooKeeper 實現區域伺服器的領導者選舉和租賃管理。您可以利用一個相對較少(比如說,五個)節點的 ZooKeeper 集合體執行有 50 個節點的大型 HBase 叢集。

設定並部署 ZooKeeper 集合體

現在讓我們設定並部署有三個節點的 ZooKeeper 集合體。在這裡,我們將使用撰寫本文時的最新版的 ZooKeeper:3.4.5(請參閱 參考資料 獲得有關的下載資訊)。我們用於此演示的節點被命名為 zkserver1.mybiz.com、zkserver2.mybiz.comzk3server3.mybiz.com。必須在每個節點上遵循下面的步驟來啟動 ZooKeeper 伺服器:

  1. 如果尚未安裝 JDK,請下載安裝它(參閱 參考資料)。這是必需的,因為 ZooKeeper 伺服器在 JVM 上執行。
  2. 下載 ZooKeeper 3.4.5. tar.gz tarball 並將它解壓縮到適當的位置。
    清單 1. 下載 ZooKeeper tarball 並將它解壓縮到適當的位置

    wget
    http://www.bizdirusa.com/mirrors/apache/ZooKeeper/stable/zookeeper3.4.5.
    tar.gz tar xzvf zookeeper3.4.5.tar.gz
  3. 建立一個目錄,用它來儲存與 ZooKeeper 伺服器有關聯mkdir /var/lib/zookeeper將這個目錄建立為根目錄,並在以後將這個目錄的所有者更改為您希望執行ZooKeeper 伺服器的使用者。
  4. 設定配置。建立或編輯 zookeeper3.4.5/conf/zoo.cfg 檔案,使其與 清單 2 相似。
    清單 2. 設定配置

    tickTime=2000
    dataDir=/var/lib/zookeeper clientPort=2181
    initLimit=5 syncLimit=2
    server.1=zkserver1.mybiz.com:2888:3888
    server.2=zkserver2.mybiz.com:2888:3888
    server.3=zkserver3.mybiz.com:2888:3888

    值得重點注意的一點是,所有三個機器都應該開啟埠 2181、28883888。在本例中,埠 2181ZooKeeper 客戶端使用,用於連線到 ZooKeeper 伺服器;埠 2888 由對等 ZooKeeper伺服器使用,用於互相通訊;而埠 3888 用於領導者選舉。您可以選擇自己喜歡的任何埠。通常建議在所有 ZooKeeper伺服器上使用相同的埠。

    1. 建立一個 /var/lib/zookeeper/myid 檔案。此檔案的內容將只包含 zkserver1.mybiz.com上的數字 1、zkserver2.mybiz.com 上的數字 2 和 zkserver3.mybiz.com 上的數字 3。清單 3 顯示了來自 zkserver1.mybiz.com 的此檔案的 cat 輸出。

清單 3. cat 輸出

    mark@zkserver1.mybiz.com:~# cat
    /var/lib/zookeeper/myid 1

現在,您已經做好了在每臺機器上啟動 ZooKeeper 伺服器的準備。

清單 4. 啟動 ZooKeeper 伺服器

zookeeper3.4.5/ bin/zkServer.sh
start

現在,您可以從其中一臺正在執行 ZooKeeper 伺服器的機器上啟動一個 CLI 客戶端。

清單 5. 啟動 CLI 客戶端

zookeeper3.4.5/ bin/zkCli.sh server
zkserver1.mybiz.com:2181,zkserver2.mybiz.com:2181,zkserver3.mybiz.com:2181

客戶端提供一個伺服器列表,可以任意選中一個進行連線。如果在連線過程中失去與該伺服器的連線,則會選中列表中的另一臺伺服器,而且客戶端會話也會轉移到該伺服器。一旦啟動了客戶端,您就可以建立、編輯和刪除 znode。讓我們在 /mynode 建立一個znode,使用 helloworld 作為關聯的資料。

清單 6. 在 /mynode 上建立一個 znode

    [zk:127.0.0.1:2181(CONNECTED) 2] create /mynode
    helloworld Created /mynode

現在,讓我們在 /mynode 驗證和檢索資料。

清單 7. 在 /mynode 驗證和檢索資料

    [zk:127.0.0.1:2181(CONNECTED) 6] get /mynode
    helloworld cZxid = 0x200000005 ctime = Sat Jul 20
    19:53:52 PDT 2013 mZxid = 0x200000005 mtime = Sat
    Jul 20 19:53:52 PDT 2013 pZxid = 0x200000005
    cversion = 0 dataVersion = 0 aclVersion = 0
    ephemeralOwner = 0x0 dataLength = 11 numChildren = 0

您會發現,在獲取一個 znode 資料時,客戶端也返回了一些與 znode 有關的後設資料。此後設資料中的一些重要欄位包括,與建立和最後修改 znode 的時間有關的階段時間戳(ctimemtime)、每次修改資料都會更改的資料版本(dataVersion)、資料長度(dataLength)、這個 znode 的子節點的數量(numChildren)。我們現在可以刪除 znode

清單 8. 刪除 znode

    [zk:127.0.0.1:2181(CONNECTED) 7]
    rmr /mynode

讓我們在 /mysecondnode 建立另一個 znode

清單 9. 建立另一個 znode

    [zk:127.0.0.1:2181(CONNECTED) 10] create
    /mysecondnode hello Created /mysecondnode

現在,讓我們在 /mysecondnode 驗證和檢索資料。這一次,我們在最後提供了一個可選引數 1。此引數為 /mysecondnode 上的資料設定了一個一次性的觸發器(名稱為 watch)。如果另一個客戶端在 /mysecondnode 上修改資料,該客戶端將會獲得一個非同步通知。請注意,該通知只傳送一次,除非 watch 被重新設定,否則不會因資料發生改變而再次傳送通知。

清單 10. 在 /mysecondnode 上驗證和檢索資料

   [zk:127.0.0.1:2181(CONNECTED) 12] get
    /mysecondnode 1 hello cZxid = 0x200000007 ctime =
    Sat Jul 20 19:58:27 PDT 2013 mZxid = 0x200000007
    mtime = Sat Jul 20 19:58:27 PDT 2013 pZxid =
    0x200000007 cversion = 0 dataVersion = 0
    aclVersion = 0 ephemeralOwner = 0x0 dataLength = 5
    numChildren = 0

現在,從不同的客戶端(比如,從不同的機器)更改與 /mysecondnode 有關聯的資料的值。

清單 11. 更改與 /mysecondnode 有關聯的資料的值

  [zk: localhost:2181(CONNECTED)
    1] set /mysecondnode hello2 cZxid = 0x200000007
    ctime = Sat Jul 20 19:58:27 PDT 2013 mZxid =
    0x200000009 mtime = Sat Jul 20 20:02:37 PDT 2013
    pZxid = 0x200000007 cversion = 0 dataVersion = 1
    aclVersion = 0 ephemeralOwner = 0x0 dataLength = 6
    numChildren = 0

您會發現,在第一個客戶端上獲得了一個 watch 通知。

清單 12. 在第一個客戶端上獲得了一個 watch 通知

    [zk:127.0.0.1:2181(CONNECTED) 13] WATCHER::
    WatchedEvent state:SyncConnected
    type:NodeDataChanged path:/mysecondnode

繼續下去,因為 znode 形成了一個分層名稱空間,所以您還可以建立子節點。

清單 13. 建立子節點

    [zk:
    localhost:2181(CONNECTED) 2] create /mysecondnode/
    subnode 123 Created /mysecondnode/ subnode

您可以獲得關於某個 znode 的其他統計後設資料。

清單 14. 獲得關於某個 znode 的其他統計後設資料

    [zk:127.0.0.1:2181(CONNECTED)
    14] stat /mysecondnode cZxid = 0x200000007 ctime =
    Sat Jul 20 19:58:27 PDT 2013 mZxid = 0x200000009
    mtime = Sat Jul 20 20:02:37 PDT 2013 pZxid =
    0x20000000a cversion = 1 dataVersion = 1
    aclVersion = 0 ephemeralOwner = 0x0 dataLength = 6
    numChildren = 1

在上面的示例中,我們使用了 ZooKeeperCLI 客戶端與 ZooKeeper 伺服器進行互動。ZooKeeper 提供了 Java™CPython 和其他繫結。您可以通過這些繫結呼叫客戶端 API,將 JavaCPython 應用程式轉換為ZooKeeper 客戶端。

ZooKeeper 的應用程式

由於 ZooKeeper 在分散式系統中提供了一些多功能的用例,ZooKeeper 有一組不同的實用應用程式。我們將在這裡列出部分這些應用程式。這些應用程式大多取自 Apache ZooKeeper 維基,那裡還提供了一個更完整的最新列表。請參閱 參考資料,獲得這些技術的連結:

  • Apache Hadoop 依靠 ZooKeeper 來實現 Hadoop HDFS NameNode 的自動故障轉移,以及 YARN ResourceManager 的高可用性。
  • Apache HBase 是構建於 Hadoop 之上的分散式資料庫,它使用 ZooKeeper 來實現區域伺服器的主選舉(master election)、租賃管理以及區域伺服器之間的其他通訊。
  • Apache Accumulo 是構建於 Apache ZooKeeper(和 Apache Hadoop)之上的另一個排序分散式鍵/值儲存。
  • Apache Solr 使用 ZooKeeper 實現領導者選舉和集中式配置。
  • Apache Mesos 是一個叢集管理器,提供了分散式應用程式之間高效的資源隔離和共享。Mesos 使用 ZooKeeper 實現了容錯的、複製的主選舉。
  • Neo4j 是一個分散式圖形資料庫,它使用 ZooKeeper 寫入主選擇和讀取從協調(read slave coordination)。
  • Cloudera Search 使用 ZooKeeper(通過 Apache Solr)整合了搜尋功能與 Apache Hadoop,以實現集中式配置管理。

結束語

實現您自己的協議來協調分散式系統,這可能是一個令人感到沮喪的費時的過程。這正是 ZooKeeper 發揮其作用的地方。ZooKeeper 是一個穩定的、簡單的、高效能的協調服務,為您提供編寫正確的分散式應用程式所需的工具,而無需擔心競爭條件、死鎖和不一致。在下一次編寫分散式應用程式時,您就可以利用 ZooKeeper 支援所有協調需求。

相關文章