ZooKeeper架構設計及其應用要點

ZooKeeper是一個開源的分佈式服務框架,它是Apache Hadoop項目的一個子項目,主要用來解決分佈式應用場景中存在的一些問題,如:統一命名服務、狀態同步服務、集羣管理、分佈式應用配置管理等,它支持Standalone模式和分佈式模式,在分佈式模式下,可以爲分佈式應用提供高性能和可靠地協調服務,並且使用ZooKeeper能夠大大簡化分佈式協調服務的實現,爲開發分佈式應用極大地下降了成本。html

整體架構java

ZooKeeper分佈式協調服務框架的整體架構,如圖所示: zkservice ZooKeeper集羣由一組Server節點組成,這一組Server節點中存在一個角色爲Leader的節點,其餘節點都爲Follower。當客戶端Client鏈接到ZooKeeper集羣,而且執行寫請求時,這些請求會被髮送到Leader節點上,而後Leader節點上數據變動會同步到集羣中其餘的Follower節點。 Leader節點在接收到數據變動請求後,首先將變動寫入本地磁盤,以做恢復之用。當全部的寫請求持久化到磁盤之後,纔會將變動應用到內存中。 ZooKeeper使用了一種自定義的原子消息協議,在消息層的這種原子特性,保證了整個協調系統中的節點數據或狀態的一致性。Follower基於這種消息協議可以保證本地的ZooKeeper數據與Leader節點同步,而後基於本地的存儲來獨立地對外提供服務。 當一個Leader節點發生故障失效時,失敗故障是快速響應的,消息層負責從新選擇一個Leader,繼續做爲協調服務集羣的中心,處理客戶端寫請求,並將ZooKeeper協調系統的數據變動同步(廣播)到其餘的Follower節點。node

設計要點apache

ZooKeeper是基於以下4個目標來進行權衡和設計的,咱們從設計及其特性的角度來詳細說明:網絡

    • 簡單

分佈式應用中的各個進程能夠經過ZooKeeper的命名空間(Namespace)來進行協調,這個命名空間是共享的、具備層次結構的,更重要的是它的結構足夠簡單,像咱們平時接觸到的文件系統的目錄結構同樣容易理解,如圖所示: zknamespace 在ZooKeeper中每一個命名空間(Namespace)被稱爲ZNode,你能夠這樣理解,每一個ZNode包含一個路徑和與之相關的元數據,以及繼承自該節點的孩子列表。與傳統文件系統不一樣的是,ZooKeeper中的數據保存在內存中,實現了分佈式同步服務的高吞吐和低延遲。 在上圖示例的ZooKeeper的數據模型中,有以下要點:session

      1. 每一個節點(ZNode)中存儲的是同步相關的數據(這是ZooKeeper設計的初衷,數據量很小,大概B到KB量級),例如狀態信息、配置內容、位置信息等。
      2. 一個ZNode維護了一個狀態結構,該結構包括:版本號、ACL變動、時間戳。每次ZNode數據發生變化,版本號都會遞增,這樣客戶端的讀請求能夠基於版本號來檢索狀態相關數據。
      3. 每一個ZNode都有一個ACL,用來限制是否能夠訪問該ZNode。
      4. 在一個命名空間中,對ZNode上存儲的數據執行讀和寫請求操做都是原子的。
      5. 客戶端能夠在一個ZNode上設置一個監視器(Watch),若是該ZNode數據發生變動,ZooKeeper會通知客戶端,從而觸發監視器中實現的邏輯的執行。
      6. 每一個客戶端與ZooKeeper鏈接,便創建了一次會話(Session),會話過程當中,可能發生CONNECTING、CONNECTED和CLOSED三種狀態。
      7. ZooKeeper支持臨時節點(Ephemeral Nodes)的概念,它是與ZooKeeper中的會話(Session)相關的,若是鏈接斷開,則該節點被刪除。
    • 冗餘

ZooKeeper被設計爲複製集羣架構,每一個節點的數據均可以在集羣中複製傳播,使集羣中的每一個節點數據同步一致,從而達到服務的可靠性和可用性。前面說到,ZooKeeper將數據放在內存中來提升性能,爲了不發生單點故障(SPOF),支持數據的複製來達到冗餘存儲,這是必不可少的。架構

    • 有序

ZooKeeper使用時間戳來記錄致使狀態變動的事務性操做,也就是說,一組事務經過時間戳來保證有序性。基於這一特性。ZooKeeper能夠實現更加高級的抽象操做,如同步等。app

    • 快速

ZooKeeper包括讀寫兩種操做,基於ZooKeeper的分佈式應用,若是是讀多寫少的應用場景(讀寫比例大約是10:1),那麼讀性能更可以體現出高效。框架

數據模型異步

ZooKeeper有一個分層的命名空間,結構相似文件系統的目錄結構,很是簡單而直觀。其中,ZNode是最重要的概念,前面咱們已經描述過。另外,有ZNode有關的還包括Watches、ACL、臨時節點、序列節點(Sequence Node)。

    • ZNode結構

ZooKeeper中使用Zxid(ZooKeeper Transaction Id)來表示每次節點數據變動,一個Zxid與一個時間戳對應,因此多個不一樣的變動對應的事務是有序的。下面是ZNode的組成結構,引用文檔以下所示:

      • czxid – The zxid of the change that caused this znode to be created.
      • mzxid – The zxid of the change that last modified this znode.
      • ctime – The time in milliseconds from epoch when this znode was created.
      • mtime – The time in milliseconds from epoch when this znode was last modified.
      • version – The number of changes to the data of this znode.
      • cversion – The number of changes to the children of this znode.
      • aversion – The number of changes to the ACL of this znode.
      • ephemeralOwner – The session id of the owner of this znode if the znode is an ephemeral node. If it is not an ephemeral node, it will be zero.
      • dataLength – The length of the data field of this znode.
      • numChildren – The number of children of this znode.
    • Watches(監視)

ZooKeeper中的Watch是隻能觸發一次。也就是說,若是客戶端在指定的ZNode設置了Watch,若是該ZNode數據發生變動,ZooKeeper會發送一個變動通知給客戶端,同時觸發設置的Watch事件。若是ZNode數據又發生了變動,客戶端在收到第一次通知後沒有從新設置該ZNode的Watch,則ZooKeeper就不會發送一個變動通知給客戶端。 ZooKeeper異步通知設置Watch的客戶端。可是ZooKeeper可以保證在ZNode的變動生效以後纔會異步地通知客戶端,而後客戶端纔可以看到ZNode的數據變動。因爲網絡延遲,多個客戶端可能會在不一樣的時間看到ZNode數據的變動,可是看到變動的順序是可以保證有序一致的。 ZNode能夠設置兩類Watch,一個是Data Watches(該ZNode的數據變動致使觸發Watch事件),另外一個是Child Watches(該ZNode的孩子節點發生變動致使觸發Watch事件)。調用getData()和exists() 方法能夠設置Data Watches,調用getChildren()方法能夠設置Child Watches。調用setData()方法觸發在該ZNode的註冊的Data Watches。調用create()方法建立一個ZNode,將觸發該ZNode的Data Watches;調用create()方法建立ZNode的孩子節點,則觸發ZNode的Child Watches。調用delete()方法刪除ZNode,則同時觸發Data Watches和Child Watches,若是該被刪除的ZNode還有父節點,則父節點觸發一個Child Watches。 另外,若是客戶端與ZooKeeper Server斷開鏈接,客戶端就沒法觸發Watches,除非再次與ZooKeeper Server創建鏈接。

    • Sequence Nodes(序列節點)

在建立ZNode的時候,能夠請求ZooKeeper生成序列,以路徑名爲前綴,計數器緊接在路徑名後面,例如,會生成相似以下形式序列:

qn-0000000001, qn-0000000002, qn-0000000003, qn-0000000004, qn-0000000005, qn-0000000006, qn-0000000007

 

對於ZNode的父節點來講,序列中的每一個計數器字符串都是惟一的,最大值爲2147483647。

  • ACLs(訪問控制列表)

ACL能夠控制訪問ZooKeeper的節點,只能應用於特定的ZNode上,而不能應用於該ZNode的全部孩子節點上。它主要有以下五種權限:

  • CREATE 容許建立Child Nodes
  • READ 容許獲取ZNode的數據,以及該節點的孩子列表
  • WRITE 能夠修改ZNode的數據
  • DELETE 能夠刪除一個孩子節點
  • ADMIN 能夠設置權限

ZooKeeper內置了4種方式實現ACL:

  • world 一個單獨的ID,表示任何人均可以訪問
  • auth 不使用ID,只有認證的用戶能夠訪問
  • digest 使用username:password生成MD5哈希值做爲認證ID
  • ip 使用客戶端主機IP地址來進行認證
  • ZooKeeper Session

當客戶端鏈接到ZooKeeper集羣時,創建了會話。會話過程當中的狀態變遷,如圖所示: state_dia 創建鏈接過程當中,會話狀態爲CONNECTING;當鏈接創建成功後,會話狀態變爲CONNECTED。會話過程當中,若是正常的話,會話的狀態只能是CONNECTING和CONNECTED兩者之一。若是在會話過程當中鏈接斷開,則變爲CLOSED狀態。

應用陷阱

並不是任何分佈式應用都適合使用ZooKeeper來構建協調服務,咱們根據ZooKeeper提供的文檔,給出哪些狀況下使用會出現問題,又是如何應對這種問題的。總結以下:

    1. 丟失ZNode上的變動通知

客戶端鏈接到ZooKeeper Server之後,會維護一個TCP鏈接。在CONNECTED狀態下,客戶端設置了某個ZNode的Watch監聽器,能夠收到來自該節點變動的通知(後續會觸發必定的邏輯執行流程)。可是,若是因爲網絡異常,客戶端斷開了與ZooKeeper Server的鏈接,在斷開的過程當中,是沒法收到ZooKeeper在ZNode上發送的節點數據變動通知的。 因此,若是使用ZooKeeper的Watch,必需要尋找保持CONNECTED的Watch,才能保證不會丟失該Watch監控的ZNode上的數據變動通知。

    1. 無效ZooKeeper集羣節點列表

與ZooKeeper集羣交互時,通常狀況下客戶端會持有一個ZooKeeper集羣節點的列表,或者列表的子集,那麼會存在以下兩種狀況: 一種狀況是,若是客戶端持有的列表或者列表子集,其中節點都處於Active狀態,可以提供協調服務,那麼客戶端訪問ZooKeeper集羣沒有任何問題。 另外一種狀況,客戶端持有ZooKeeper集羣節點列表或列表子集,若是列表中的某些節點由於故障退出了集羣,若是客戶端再次鏈接這一類失效的節點,就沒法獲取服務。 因此,咱們在應用中使用ZooKeeper集羣時,必定要明確這一點,或者跳過無效的節點,或者從新尋找有效的節點繼續業務處理,或者檢查ZooKeeper集羣,使整個集羣恢復正常。

    1. 配置致使的性能問題

若是設置Java堆內存(Heap)不合理,會致使ZooKeeper內存不足,會在內存與文件系統之間進行數據交換,致使ZooKeeper的性能極大地降低,從而可能會影響應用程序。 爲了不Swapping問題的出現,主要考慮設置足夠的Java堆內存,同時減小被操做系統和Cache使用的內存,儘可能避免在內存與文件系統之間發生數據交換,或者能夠將交換限制在必定的範圍以內。

    1. 事務日誌存儲設備性能

ZooKeeper會同步事務到存儲設備,若是存儲設備不是專用的,而是和其餘I/O密集型應用共享同一磁盤,會致使ZooKeeper的效率。由於客戶端請求ZNode數據變動而發生的事務,ZooKeeper會在響應以前將事務日誌寫入存儲設備,若是存儲設備是專用的,那麼整個服務以致外部應用都會得到極大地性能提高。

    1. ZNode存儲大量數據致使性能問題

ZooKeeper的設計初衷是,每一個ZNode只存放少許的同步數據,若是存儲了大量數據,致使ZooKeeper每次節點發生變動時須要將事務寫入存儲設備,同時還要在集羣內部複製傳播,這將致使不可避免的延遲和性能問題。 因此,若是須要與大量的數據相關,能夠將大量數據存儲在其餘設備中,而只是在ZooKeeper中存儲一個簡單的映射,如指針、引用等等。

參考連接

本文基於署名-非商業性使用-相同方式共享 4.0許可協議發佈,歡迎轉載、使用、從新發布,但務必保留文章署名時延軍(包含連接:http://shiyanjun.cn),不得用於商業目的,基於本文修改後的做品務必以相同的許可發佈。若有任何疑問,請與我聯繫。
相關文章
相關標籤/搜索