【Zookeeper系列】Zookeeper簡單介紹(轉)

原文連接:https://www.cnblogs.com/sunddenly/p/4033574.htmlhtml

1、分佈式協調技術

在給你們介紹ZooKeeper以前先來給你們介紹一種技術——分佈式協調技術。那麼什麼是分佈式協調技術?那麼我來告訴你們,其實分佈式協調技術主要用來解決分佈式環境當中多個進程之間的同步控制,讓他們有序的去訪問某種臨界資源,防止形成"髒數據"的後果。這時,有人可能會說這個簡單,寫一個調度算法就輕鬆解決了。說這句話的人,可能對分佈式系統不是很瞭解,因此纔會出現這種誤解。若是這些進程所有是跑在一臺機上的話,相對來講確實就好辦了,問題就在於他是在一個分佈式的環境下,這時問題又來了,那什麼是分佈式呢?這個一兩句話我也說不清楚,但我給你們畫了一張圖但願能幫助你們理解這方面的內容,若是以爲不對儘可拍磚,來我們看一下這張圖,如圖1.1所示。node

圖 1.1 分佈式系統圖算法

給你們分析一下這張圖,在這圖中有三臺機器,每臺機器各跑一個應用程序。而後咱們將這三臺機器經過網絡將其鏈接起來,構成一個系統來爲用戶提供服務,對用戶來講這個系統的架構是透明的,他感受不到我這個系統是一個什麼樣的架構。那麼咱們就能夠把這種系統稱做一個分佈式系統數據庫

那咱們接下來再分析一下,在這個分佈式系統中如何對進程進行調度,我假設在第一臺機器上掛載了一個資源,而後這三個物理分佈的進程都要競爭這個資源,但咱們又不但願他們同時進行訪問,這時候咱們就須要一個協調器,來讓他們有序的來訪問這個資源。這個協調器就是咱們常常提到的那個,好比說"進程-1"在使用該資源的時候,會先去得到鎖,"進程1"得到鎖之後會對該資源保持獨佔,這樣其餘進程就沒法訪問該資源,"進程1"用完該資源之後就將鎖釋放掉,讓其餘進程來得到鎖,那麼經過這個鎖機制,咱們就能保證了分佈式系統中多個進程可以有序的訪問該臨界資源。那麼咱們把這個分佈式環境下的這個鎖叫做分佈式鎖。這個分佈式鎖也就是咱們分佈式協調技術實現的核心內容,那麼如何實現這個分佈式呢,那就是咱們後面要講的內容。服務器

2、分佈式鎖的實現

好咱們知道,爲了防止分佈式系統中的多個進程之間相互干擾,咱們須要一種分佈式協調技術來對這些進程進行調度。而這個分佈式協調技術的核心就是來實現這個分布式鎖。那麼這個鎖怎麼實現呢?這實現起來確實相對來講比較困難的。網絡

1.1 面臨的問題

在看了圖1.1所示的分佈式環境以後,有人可能會感受這不是很難。無非是將原來在同一臺機器上對進程調度的原語,經過網絡實如今分佈式環境中。是的,表面上是能夠這麼說。可是問題就在網絡這,在分佈式系統中,全部在同一臺機器上的假設都不存在:由於網絡是不可靠的。數據結構

好比,在同一臺機器上,你對一個服務的調用若是成功,那就是成功,若是調用失敗,好比拋出異常那就是調用失敗。可是在分佈式環境中,因爲網絡的不可靠,你對一個服務的調用失敗了並不表示必定是失敗的,多是執行成功了,可是響應返回的時候失敗了。還有,A和B都去調用C服務,在時間上 A還先調用一些,B後調用,那麼最後的結果是否是必定A的請求就先於B到達呢? 這些在同一臺機器上的種種假設,咱們都要從新思考,咱們還要思考這些問題給咱們的設計和編碼帶來了哪些影響。還有,在分佈式環境中爲了提高可靠性,咱們每每會部署多套服務,可是如何在多套服務中達到一致性,這在同一臺機器上多個進程之間的同步相對來講比較容易辦到,但在分佈式環境中確實一個大難題。架構

因此分佈式協調遠比在同一臺機器上對多個進程的調度要可貴多,並且若是爲每個分佈式應用都開發一個獨立的協調程序。一方面,協調程序的反覆編寫浪費,且難以造成通用、伸縮性好的協調器。另外一方面,協調程序開銷比較大,會影響系統原有的性能。因此,急需一種高可靠、高可用的通用協調機制來用以協調分佈式應用。異步

1.2 分佈式鎖的實現者

目前,在分佈式協調技術方面作得比較好的就是Google的Chubby還有Apache的ZooKeeper他們都是分佈式鎖的實現者。有人會問既然有了Chubby爲何還要弄一個ZooKeeper,難道Chubby作得不夠好嗎?不是這樣的,主要是Chbby是非開源的,Google自家用。後來雅虎模仿Chubby開發出了ZooKeeper,也實現了相似的分佈式鎖的功能,而且將ZooKeeper做爲一種開源的程序捐獻給了Apache,那麼這樣就可使用ZooKeeper所提供鎖服務。並且在分佈式領域久經考驗,它的可靠性,可用性都是通過理論和實踐的驗證的。因此咱們在構建一些分佈式系統的時候,就能夠以這類系統爲起點來構建咱們的系統,這將節省很多成本,並且bug也 將更少。分佈式

3、ZooKeeper概述

ZooKeeper是一種爲分佈式應用所設計的高可用、高性能且一致的開源協調服務,它提供了一項基本服務:分佈式鎖服務。因爲ZooKeeper的開源特性,後來咱們的開發者在分佈式鎖的基礎上,摸索了出了其餘的使用方法:配置維護、組服務、分佈式消息隊列分佈式通知/協調等。

注意:ZooKeeper性能上的特色決定了它可以用在大型的、分佈式的系統當中。從可靠性方面來講,它並不會由於一個節點的錯誤而崩潰。除此以外,它嚴格的序列訪問控制意味着複雜的控制原語能夠應用在客戶端上。ZooKeeper在一致性、可用性、容錯性的保證,也是ZooKeeper的成功之處,它得到的一切成功都與它採用的協議——Zab協議是密不可分的,這些內容將會在後面介紹。

前面提到了那麼多的服務,好比分佈式鎖、配置維護、組服務等,那它們是如何實現的呢,我相信這纔是你們關心的東西。ZooKeeper在實現這些服務時,首先它設計一種新的數據結構——Znode,而後在該數據結構的基礎上定義了一些原語,也就是一些關於該數據結構的一些操做。有了這些數據結構和原語還不夠,由於咱們的ZooKeeper是工做在一個分佈式的環境下,咱們的服務是經過消息以網絡的形式發送給咱們的分佈式應用程序,因此還須要一個通知機制——Watcher機制。那麼總結一下,ZooKeeper所提供的服務主要是經過:數據結構+原語+watcher機制,三個部分來實現的。那麼我就從這三個方面,給你們介紹一下ZooKeeper。

4、ZooKeeper數據模型

4.1 ZooKeeper數據模型Znode

ZooKeeper擁有一個層次的命名空間,這個和標準的文件系統很是類似,以下圖3.1 所示。

圖4.1 ZooKeeper數據模型與文件系統目錄樹

 

從圖中咱們能夠看出ZooKeeper的數據模型,在結構上和標準文件系統的很是類似,都是採用這種樹形層次結構,ZooKeeper樹中的每一個節點被稱爲—Znode。和文件系統的目錄樹同樣,ZooKeeper樹中的每一個節點能夠擁有子節點。但也有不一樣之處:

(1) 引用方式

Zonde經過路徑引用,如同Unix中的文件路徑。路徑必須是絕對的,所以他們必須由斜槓字符來開頭。除此之外,他們必須是惟一的,也就是說每個路徑只有一個表示,所以這些路徑不能改變。在ZooKeeper中,路徑由Unicode字符串組成,而且有一些限制。字符串"/zookeeper"用以保存管理信息,好比關鍵配額信息。

(2) Znode結構

ZooKeeper命名空間中的Znode,兼具文件和目錄兩種特色。既像文件同樣維護着數據、元信息、ACL、時間戳等數據結構,又像目錄同樣能夠做爲路徑標識的一部分。圖中的每一個節點稱爲一個Znode。 每一個Znode由3部分組成:

 stat:此爲狀態信息, 描述該Znode的版本, 權限等信息

 data:與該Znode關聯的數據

 children:該Znode下的子節點

ZooKeeper雖然能夠關聯一些數據,但並無被設計爲常規的數據庫或者大數據存儲,相反的是,它用來管理調度數據,好比分佈式應用中的配置文件信息、狀態信息、聚集位置等等。這些數據的共同特性就是它們都是很小的數據,一般以KB爲大小單位。ZooKeeper的服務器和客戶端都被設計爲嚴格檢查並限制每一個Znode的數據大小至多1M,但常規使用中應該遠小於此值。

(3) 數據訪問

ZooKeeper中的每一個節點存儲的數據要被原子性的操做。也就是說讀操做將獲取與節點相關的全部數據,寫操做也將替換掉節點的全部數據。另外,每個節點都擁有本身的ACL(訪問控制列表),這個列表規定了用戶的權限,即限定了特定用戶對目標節點能夠執行的操做。

(4) 節點類型

ZooKeeper中的節點有兩種,分別爲臨時節點永久節點。節點的類型在建立時即被肯定,而且不能改變。

① 臨時節點:該節點的生命週期依賴於建立它們的會話。一旦會話(Session)結束,臨時節點將被自動刪除,固然能夠也能夠手動刪除。雖然每一個臨時的Znode都會綁定到一個客戶端會話,但他們對全部的客戶端仍是可見的。另外,ZooKeeper的臨時節點不容許擁有子節點。

② 永久節點:該節點的生命週期不依賴於會話,而且只有在客戶端顯示執行刪除操做的時候,他們才能被刪除。

(5) 順序節點

當建立Znode的時候,用戶能夠請求在ZooKeeper的路徑結尾添加一個遞增的計數。這個計數對於此節點的父節點來講是惟一的,它的格式爲"%10d"(10位數字,沒有數值的數位用0補充,例如"0000000001")。當計數值大於232-1時,計數器將溢出。

(6) 觀察

客戶端能夠在節點上設置watch,咱們稱之爲監視器。當節點狀態發生改變時(Znode的增、刪、改)將會觸發watch所對應的操做。當watch被觸發時,ZooKeeper將會向客戶端發送且僅發送一條通知,由於watch只能被觸發一次,這樣能夠減小網絡流量。

4.2 ZooKeeper中的時間

ZooKeeper有多種記錄時間的形式,其中包含如下幾個主要屬性:

(1) Zxid

導致ZooKeeper節點狀態改變的每個操做都將使節點接收到一個Zxid格式的時間戳,而且這個時間戳全局有序。也就是說,也就是說,每一個對節點的改變都將產生一個惟一的Zxid。若是Zxid1的值小於Zxid2的值,那麼Zxid1所對應的事件發生在Zxid2所對應的事件以前。實際上,ZooKeeper的每一個節點維護者三個Zxid值,爲別爲:cZxid、mZxid、pZxid。

 cZxid: 是節點的建立時間所對應的Zxid格式時間戳。
② mZxid:是節點的修改時間所對應的Zxid格式時間戳。

實現中Zxid是一個64爲的數字,它高32位是epoch用來標識leader關係是否改變,每次一個leader被選出來,它都會有一個 新的epoch。低32位是個遞增計數。 (2) 版本號

對節點的每個操做都將導致這個節點的版本號增長。每一個節點維護着三個版本號,他們分別爲:

① version:節點數據版本號
② cversion:子節點版本號
③ aversion:節點所擁有的ACL版本號

4.3 ZooKeeper節點屬性

經過前面的介紹,咱們能夠了解到,一個節點自身擁有表示其狀態的許多重要屬性,以下圖所示。

圖 4.2 Znode節點屬性結構

5、ZooKeeper服務中操做

在ZooKeeper中有9個基本操做,以下圖所示:

圖 5.1 ZooKeeper類方法描述

更新ZooKeeper操做是有限制的。delete或setData必須明確要更新的Znode的版本號,咱們能夠調用exists找到。若是版本號不匹配,更新將會失敗。

更新ZooKeeper操做是非阻塞式的。所以客戶端若是失去了一個更新(因爲另外一個進程在同時更新這個Znode),他能夠在不阻塞其餘進程執行的狀況下,選擇從新嘗試或進行其餘操做。

儘管ZooKeeper能夠被看作是一個文件系統,可是處於便利,摒棄了一些文件系統地操做原語。由於文件很是的小而且使總體讀寫的,因此不須要打開、關閉或是尋地的操做。

6、Watch觸發器

(1) watch概述

ZooKeeper能夠爲全部的讀操做設置watch,這些讀操做包括:exists()、getChildren()及getData()。watch事件是一次性的觸發器,當watch的對象狀態發生改變時,將會觸發此對象上watch所對應的事件。watch事件將被異步地發送給客戶端,而且ZooKeeper爲watch機制提供了有序的一致性保證。理論上,客戶端接收watch事件的時間要快於其看到watch對象狀態變化的時間。

(2) watch類型

ZooKeeper所管理的watch能夠分爲兩類:

 數據watch(data  watches):getDataexists負責設置數據watch
② 孩子watch(child watches):getChildren負責設置孩子watch

咱們能夠經過操做返回的數據來設置不一樣的watch:

① getData和exists:返回關於節點的數據信息
② getChildren:返回孩子列表

所以

① 一個成功的setData操做將觸發Znode的數據watch

 一個成功的create操做將觸發Znode的數據watch以及孩子watch

③ 一個成功的delete操做將觸發Znode的數據watch以及孩子watch

(3) watch註冊與處觸發

圖 6.1 watch設置操做及相應的觸發器如圖下圖所示:

① exists操做上的watch,在被監視的Znode建立刪除數據更新時被觸發。
 getData操做上的watch,在被監視的Znode刪除數據更新時被觸發。在被建立時不能被觸發,由於只有Znode必定存在,getData操做纔會成功。
 getChildren操做上的watch,在被監視的Znode的子節點建立刪除,或是這個Znode自身被刪除時被觸發。能夠經過查看watch事件類型來區分是Znode,仍是他的子節點被刪除:NodeDelete表示Znode被刪除,NodeDeletedChanged表示子節點被刪除。

Watch由客戶端所鏈接的ZooKeeper服務器在本地維護,所以watch能夠很是容易地設置、管理和分派。當客戶端鏈接到一個新的服務器時,任何的會話事件都將可能觸發watch。另外,當從服務器斷開鏈接的時候,watch將不會被接收。可是,當一個客戶端從新創建鏈接的時候,任何先前註冊過的watch都會被從新註冊。

(4) 須要注意的幾點

Zookeeper的watch實際上要處理兩類事件:

① 鏈接狀態事件(type=None, path=null)

這類事件不須要註冊,也不須要咱們連續觸發,咱們只要處理就好了。

② 節點事件

節點的創建,刪除,數據的修改。它是one time trigger,咱們須要不停的註冊觸發,還可能發生事件丟失的狀況。

上面2類事件都在Watch中處理,也就是重載的process(Event event)

節點事件的觸發,經過函數exists,getData或getChildren來處理這類函數,有雙重做用:

① 註冊觸發事件

② 函數自己的功能

函數的自己的功能又能夠用異步的回調函數來實現,重載processResult()過程當中處理函數自己的的功能。

7、ZooKeeper應用舉例 

爲了方便你們理解ZooKeeper,在此就給你們舉個例子,看看ZooKeeper是如何實現的他的服務的,我以ZooKeeper提供的基本服務分佈式鎖爲例。

7.1 分佈式鎖應用場景

在分佈式鎖服務中,有一種最典型應用場景,就是經過對集羣進行Master選舉,來解決分佈式系統中的單點故障。什麼是分佈式系統中的單點故障:一般分佈式系統採用主從模式,就是一個主控機鏈接多個處理節點。主節點負責分發任務,從節點負責處理任務,當咱們的主節點發生故障時,那麼整個系統就都癱瘓了,那麼咱們把這種故障叫做單點故障。以下圖7.1和7.2所示:

圖 7.1 主從模式分佈式系統               圖7.2 單點故障

     

7.2 傳統解決方案

傳統方式是採用一個備用節點,這個備用節點按期給當前主節點發送ping包,主節點收到ping包之後向備用節點發送回復Ack,當備用節點收到回覆的時候就會認爲當前主節點還活着,讓他繼續提供服務。如圖7.3所示:

圖 7.3 傳統解決方案

當主節點掛了,這時候備用節點收不到回覆了,而後他就認爲主節點掛了接替他成爲主節點以下圖7.4所示:

圖 7.4傳統解決方案

可是這種方式就是有一個隱患,就是網絡問題,來看一網絡問題會形成什麼後果,以下圖7.5所示:

圖 7.5 網絡故障

也就是說咱們的主節點的並無掛,只是在回覆的時候網絡發生故障,這樣咱們的備用節點一樣收不到回覆,就會認爲主節點掛了,而後備用節點將他的Master實例啓動起來,這樣咱們的分佈式系統當中就有了兩個主節點也就是---雙Master,出現Master之後咱們的從節點就會將它所作的事一部分彙報給了主節點,一部分彙報給了從節點,這樣服務就全亂了。爲了防止出現這種狀況,咱們引入了ZooKeeper,它雖然不能避免網絡故障,但它可以保證每時每刻只有一個Master。我麼來看一下ZooKeeper是如何實現的。

7.3 ZooKeeper解決方案

(1) Master啓動

在引入了Zookeeper之後咱們啓動了兩個主節點,"主節點-A"和"主節點-B"他們啓動之後,都向ZooKeeper去註冊一個節點。咱們假設"主節點-A"鎖註冊地節點是"master-00001","主節點-B"註冊的節點是"master-00002",註冊完之後進行選舉,編號最小的節點將在選舉中獲勝得到鎖成爲主節點,也就是咱們的"主節點-A"將會得到鎖成爲主節點,而後"主節點-B"將被阻塞成爲一個備用節點。那麼,經過這種方式就完成了對兩個Master進程的調度。

圖7.6 ZooKeeper Master選舉

(2) Master故障

若是"主節點-A"掛了,這時候他所註冊的節點將被自動刪除,ZooKeeper會自動感知節點的變化,而後再次發出選舉,這時候"主節點-B"將在選舉中獲勝,替代"主節點-A"成爲主節點。

圖7.7 ZooKeeper Master選舉

(3) Master 恢復

圖7.8 ZooKeeper Master選舉

若是主節點恢復了,他會再次向ZooKeeper註冊一個節點,這時候他註冊的節點將會是"master-00003",ZooKeeper會感知節點的變化再次發動選舉,這時候"主節點-B"在選舉中會再次獲勝繼續擔任"主節點","主節點-A"會擔任備用節點。

相關文章
相關標籤/搜索