1. 程式人生 > >17. ZooKeeper常見的分布式系統任務——屏障

17. ZooKeeper常見的分布式系統任務——屏障

per java 開始 例子 重要 行處理 避免 註冊 進程

以後幾節中主要介紹以下內容:

  • 如何執行領導者選舉,組員管理和兩階段提交協議等常見的分布式系統任務
  • 如何實現一些分布式數據結構,如屏障(barrier),鎖(lock)和隊列(queue)

這一章中概述的高層次構建也被稱為『ZooKeeper recipes』。這些都是在客戶端使用ZooKeeper的編程模型實現的,並且不需要從服務器端獲得特別的支持。在沒有ZooKeeper和它的API的情況下,這些recipes的實現將會是相當復雜和困難的。

一些第三方和社區開發的ZooKeeper客戶端綁定也提供這些高級分布式系統的構建作為其客戶端類庫的一部分。 例如,Netflix Curator是ZooKeeper的一個功能豐富的Java客戶端框架。

ZooKeeper發行版本中附帶了領導選舉和分發鎖和隊列的recipes,這些可以在分布式應用程序中使用。 這三個recipes的Java實現可以在發行版的recipes目錄中找到。

ZooKeeper recipes

在本節中學習使用ZooKeeper來開發高級分布式系統構建和數據結構。正如前面提到的,這些構建和方法在建立可伸縮的分布式體系結構中是非常重要的,但是從頭開始實現它們是相當復雜的。開發人員常常會在實現這些和集成他們的應用程序邏輯時陷入困境。在本節中,將學習如何使用ZooKeeper的數據模型和原語構建一些高級功能,並了解管理員如何使其簡單、可伸縮和沒有錯誤,而且代碼量更少。

1. Barrier

Barrier是分布式系統中使用的一種同步方法,用於阻塞一組節點的處理,直到滿足條件。它定義了一個點,所有節點必須停止它們的處理,直到所有其他節點到達這個Barrier時才進行處理。

使用ZooKeeper實現屏障的Barrier如下:

  1. 首先,將znode指定為屏障znode,例如/zk_barrier
  2. 如果這個屏障znode存在,則說屏障在系統中是活躍的。
  3. 每個客戶端通過在屏障 znode上註冊監視事件(監視事件設置為true),在/zk_barrier上調用ZooKeeper API的exists()方法。
  4. 如果exists()方法返回false,則說明屏障不再存在,客戶端繼續運算。
  5. 否則,如果exists
    方法返回true,則客戶端等待監視事件。
  6. 當屏障條件滿足退出時,負責屏障的客戶端將刪除/zk_barrier
  7. 刪除觸發監視事件,並且在獲取此通知時,客戶端再次調用/zk_barrier上的exists()方法。
  8. 步驟7返回true,客戶端可以繼續進行。

Note
屏障一直存在,直到屏障znode終止存在!

通過這種方式,我們可以不費力地使用ZooKeeper來實現一個屏障。

到目前為止所舉的例子是一個簡單的屏障,它可以阻止一組分布式進程在某些條件下等待,然後在條件滿足時進行處理。還有另一種類型的障礙有助於同步計算的開始和結束;這就是所謂的雙重屏障。雙重屏障的邏輯表明,當需要的進程數量加入屏障時,計算就開始了。在完成計算後,進程會離開,當參與屏障的進程數變為0時,計算就會結束。

雙重屏障的算法是通過具有屏障znode來實現的,該屏障znode的作用是作為參與計算的個體過程znode的父節點。 其算法概述如下:

階段1:加入屏障znode的方式如下:

  1. 假設屏障znode由znode/barrier表示。 每個客戶端進程通過創建一個以/barrier作為父節點的ephemeral znode來註冊。 在真實情況下,客戶端可能使用主機名進行註冊。
  2. 客戶端進程為在/barrier節點下的另一個存在的‘ready`節點設置監視事件,等待節點的出現。
  3. 數字N是在系統中預定義的; 這是在開始計算之前管理加入屏障的最小數量的客戶端。
  4. 在加入屏障時,每個客戶端進程查找/barrier的子節點數量:M = getChildren(/barrier, watch=false)
  5. 如果M小於N,則客戶端等待步驟3中註冊的監視事件。
  6. 否則,如果M等於N,則客戶端進程在/barrier下創建ready znode。
  7. 5步中創建的ready節點會觸發監視事件,每個客戶端都會啟動他們到目前為止所做的計算。

階段2:離開屏障的方式如下:

  1. 在完成計算的過程中,客戶端進程刪除了在/barrier下創建的znode(在第1階段的第2步:加入屏障)。
  2. 客戶進程接著查找/barrier 節點的子節點數量:M = getChildren(/barrier, watch=True)

如果M不等於0,則該客戶端等待通知(註意,在前面的調用中,已將監視事件設置為True)。
如果M等於0,則客戶端退出屏障znode。

前面的程序有一種潛在的羊群效應,當觸發通知時,所有的客戶端進程都將喚醒以檢查在barrier中留下的子節點的數量。為了避免這種情況,我們可以使用在第1階段第2步中創建的sequential ephemeral znode加入barrier。每一個客戶端進程都要註意它的下一個最短的sequential ephemeral的znode作為退出標準。這樣,為任何完成計算的客戶端只生成一個事件,因此,並不是所有的客戶端都需要一起醒來檢查它的退出條件。對於大量參與到一個barrier中的客戶端進程,羊群效應會對ZooKeeper服務的可伸縮性產生負面影響,開發人員應該意識到這種情況。

Note
雙重屏障的Java語言實現可以在http://zookeeper.apache.org/doc/r3.4.6/zookeeperTutorial.html的ZooKeeper文檔中找到。

17. ZooKeeper常見的分布式系統任務——屏障