1. 程式人生 > >巧用zookeeper實現分散式平行計算

巧用zookeeper實現分散式平行計算

雲端計算的技術話題中少不了“分散式”,“平行計算” 這些個關鍵詞,我們知道硬體擴充套件的條件(​Scale-up)始終是有限制的,將計算分散到網路中更多機器的CPU上提供更高的計算效能(Scale-out),並在這基礎上能將計算同時進行,那麼總體計算瓶頸會減小,計算的效能會顯著提高,也就是說將序列計算變為平行計算,將大量的計算在同一時間發生,,將任務分配到每一個處理器上。這裡面需要一個重要的角色,分散式計算資源中的協調者。

有這樣一個場景:系統中有大約100w的使用者,每個使用者平均有3個郵箱賬號,每隔5分鐘,每個郵箱賬需要收取100封郵件,最多3億份郵件需要下載到伺服器中(不含附件和正文)。用20臺機器劃分計算的壓力,從多個不同的網路出口進行訪問外網,計算的壓力得到緩解,那麼每臺機器的計算壓力也不會很大了。

通過我們的討論和以往的經驗判斷在這場景中可以實現平行計算,但我們還期望能對平行計算的節點進行動態的新增/刪除,做到線上更新平行計算的數目並且不會影響計算單元中的其他計算節點,但是有4個問題需要解決,否則會出現一些嚴重的問題:
1.20臺機器同時工作時,有一臺機器down掉了,其他機器怎麼進行接管計算任務,否則有些使用者的業務不會被處理,造成使用者服務終斷。
2.隨著使用者數量增加,新增機器是可以解決計算的瓶頸,但需要重啟所有計算節點,如果需要,那麼將會造成整個系統的不可用。
3.使用者數量增加或者減少,計算節點中的機器會出現有的機器資源使用率繁忙,有的卻空閒,因為計算節點不知道彼此的執行負載狀態。
4.怎麼去通知每個節點彼此的負載狀態,怎麼保證通知每個計算節點方式的可靠性和實時性。

先不說那麼多專業名詞,白話來說我們需要的是:1記錄狀態,2事件通知 ,3可靠穩定的中央排程器,4易上手、管理簡單。
採用Zookeeper完全可以解決我們的問題,分散式計算中的協調員,觀察者,分散式鎖  都可以作為zookeeper的關鍵詞,在系統中利用Zookeeper來處理事件通知,佇列,優先佇列,鎖,共享鎖等功能,利用這些特色在分散式計算中發揮重要的作用。

zookeeper的伺服器端是採用Java編寫,而zookeeper的客戶端不僅可以支援java還可以支援C語言的客戶端,在zookeeper服務端可以建立一個樹狀的Key/Vaule 存在著父子節點之間的關係。

Zookeeper允許多個Client對一個或多個ZNode資料進行監控,當ZNode有變化時能夠通知到監控這個ZNode的各個Client,所有監聽這個節點的成員都會知道了,Zookeeper使用Watcher察覺事件資訊,當客戶端接收到事件資訊,比如連線超時,節點資料改變,子節點改變,可以呼叫相應的行為來處理業務邏輯。相反,如果zookeeper客戶端對服務端的znode不關注,不Watcher,那麼發生任何變化zookeeper的客戶端都不會收到事件通知。

zookeeper中znode的資料模型
data model

每次zookeeper客戶端與伺服器端連線後都會建立一個session ID 給客戶端,客戶端將會定期心跳協議到伺服器端驗證這個連線的有效性。如果由於某種原因,客戶端無法傳送心跳到伺服器,將導致伺服器驗證過期的會話,會話ID將變為無效。客戶持有的連線/物件將不可用,因此應用程式必須建立一個新的客戶物件。如果zookeeper客戶端連線到伺服器沒有任何響應,首先客戶端會作丟擲的異常並且被捕獲,清除當前與Server相關的網路資源和連線會話,然後客戶端逐個嘗試配置列表中Server的連線地址,選擇可用的伺服器繼續進行工作。

上述Zookeeper客戶端和伺服器端的關係又是一個典型的“觀察者”模式,客戶端關注自己關心的物件(znode),一旦傳送變化就立刻通知。在《Head First設計模式》中有這樣的一張圖來表達 觀察者模式的。
“觀察者”模式
如圖所示,此係統中的三個部分是氣象站(獲取實際氣象資料的物理裝置)、WeatherData物件(追蹤來自氣象站的資料,並更新佈告板)和佈告板,再來看看百度百科對“觀察者”模式的解釋:http://baike.baidu.com/view/1854779.htm

通過對Zookeeper的瞭解,實現我們系統中需要的Failure detection和Load detection 功能,只需要在每個計算的節點中實現zookeeper客戶端程式,計算節點關注zookeeper伺服器上znode節點變化。可以在zookeeper伺服器的znode上建立一個根節點/clusterA,下面根據計算節點機器名建立對應的子節點,子節點中的value就是這臺計算節點的ip地址。
如:/clusterA/node1,/clusterA/node2,/clusterA/nodeN,這些節點都是臨時節點(EPHEMERAL),一旦連線斷開,建立的節點自動會被刪除,關注/clusterA這個根節點/clusterA的機器都會知道現在哪臺機器離開計算單元了,並且獲知現在有多少個計算節點在這個計算單元中。
如果有新的計算節點新增,在程式執行的第一步將會到zookeeper伺服器上的/clusterA 的znode上建立一個子節點/clusterA/nodeZ,這樣關注 /clusterA這個znode的機器都會知道現在多了一個計算節點。
通過zookeeper客戶端API中的getChildren()方法對應的資料型別是java.util.List,其返回/clusterA下面的機器列表,這樣還能判斷出自己在這個列表中排行位置,通過列表中排行位置可以對應使用者列表中的數目,這樣就知道自己去獲得需要計算總數中的幾分之分。
例如:有100w使用者,20個節點時,每個節點處理5w使用者進行同時計算,node3計算節點承載使用者總數中10w-15w使用者之間的計算壓力,有200w使用者,20個節點時,每個節點處理10w個使用者的業務進行同時計算,node3計算節點承載使用者總數中30w-40w使用者的計算壓力,以此類推。
這樣一來無論計算節點的數目發生變化還是,需要計算的數目發生變化,都可以保證計算壓力的平均分載。

我的廢話:
1.
根據節點數對應使用者數算出百分比之後進行計算分載,貌似我們通常的分頁查詢,只不過將每頁的分頁結果同時顯示在N多個顯示器上輸出,希望這樣比喻能讓您更好的理解。
2.在計算的中間有新的使用者數量增加,將會通知每個計算節點 下次輪詢時需要重新統計使用者數量,因為使用者所有使用者的資料分塊拿走以後放入本地的靜態hashmap(快取),沒有發生變化就從本地載入,操作資料庫發生變化後,通知zookeeper的znode節點 每個計算節點重新從資料庫中載入一次。
3.在平行計算中時間同步也是一個需要注意的地方,如果每臺機器上的時間不一致會導致潛在的隱患,可以找些工具通過時間伺服器同步每臺機器上的當前時間和時區。
4.使用zookeeper對計算節點的狀態管理只是zookeeper實現的一部分,zookeeper還可以對外提供分組,配置管理,名稱空間等服務等,這裡只是做了一個拋磚引玉的作用。

對於zookeeper的可靠性和效能而言,有足夠的機器那麼穩定性就會越高,但是效能會降低,因為ZooKeeper在執行時全部的資料都會載入到記憶體中,叢集中每一臺伺服器都包含全量的資料,每個節點實時保持資料的同步。因此整個叢集中Follower數量越多,整個叢集寫入的效能越差。後來zookeeper Server為了避免這個問題,可以將ZooKeeper叢集中部分伺服器指定為Observer。