Zookeeper系列(一)

一、ZooKeeper的背景

1.1 認識ZooKeeper

ZooKeeper---譯名爲“動物園管理員”。動物園裏當然有好多的動物,遊客可以根據動物園提供的嚮導圖到不同的場館觀賞各種類型的動物,而不是像走在原始叢林裏,心驚膽顫的被動 物所觀賞。爲了讓各種不同的動物呆在它們應該呆的地方,而不是相互串門,或是相互廝殺,就需要動物園管理員按照動物的各種習性加以分類和管理,這樣我們才能更加放心安全的觀賞動物。

回到企業級應用系統中,隨着信息化水平的不斷提高,企業級系統變得越來越龐大臃腫,性能急劇下降,客戶抱怨頻頻。拆分系統是目前我們可選擇的解決系統可伸縮性和性能問題的唯一行之有效的方法。但是拆分系統同時也帶來了系統的複雜性——各子系統不是孤立存在的,它們彼此之間需要協作和交互,這就是我們常說的分佈式系統0。各個子系統就好比動物園裏的動物,爲了使各個子系統能正常爲用戶提供統一的服務,必須需要一種機制來進行協調——這就是ZooKeeper(動物園管理員)。

1.2 爲什麼使用ZooKeeper

我們知道要寫一個分佈式應用是非常困難的,主要原因就是局部故障。一個消息通過網絡在兩個節點之間傳遞時,網絡如果發生故障,發送方並不知道接收方是否接收到了這個消息。他可能在網絡故障遷就收到了此消息,也坑沒有收到,又或者可能接收方的進程死了。發送方瞭解情況的唯一方法就是再次連接發送方,並向他進行詢問。這就是局部故障:根本不知道操作是否失敗。因此,大部分分佈式應用需要一個主控、協調控制器來管理物理分佈的子進程。目前,大部分應用需要開發私有的協調程序,缺乏一個通用的機制。協調程序的反覆編寫浪費,且難以形成通用、伸縮性好的協調器。協調服務非常容易出錯,並很難從故障中恢復。例如:協調服務很容易處於競態1甚至死鎖2。Zookeeper的設計目的,是爲了減輕分佈式應用程序所承擔的協調任務。

Zookeeper並不能阻止局部故障的發生,因爲它們的本質是分佈式系統。他當然也不會隱藏局部故障。ZooKeeper的目的就是提供一些工具集,用來建立安全處理局部故障的分佈式應用。

ZooKeeper是一個分佈式小文件系統,並且被設計爲高可用性。通過選舉算法和集羣複製可以避免單點故障3,由於是文件系統,所以即使所有的ZooKeeper節點全部掛掉,數據也不會丟失,重啓服務器之後,數據即可恢復。另外ZooKeeper的節點更新是原子的,也就是說更新不是成功就是失敗。通過版本號,ZooKeeper實現了更新的樂觀鎖4,當版本號不相符時,則表示待更新的節點已經被其他客戶端提前更新了,而當前的整個更新操作將全部失敗。當然所有的一切ZooKeeper已經爲開發者提供了保障,我們需要做的只是調用API。與此同時,隨着分佈式應用的的不斷深入,需要對集羣管理逐步透明化監控集羣和作業狀態,可以充分利ZK的獨有特性。

1.3 ZooKeeper的應用 

ZooKeeper本質上是一個分佈式的小文件存儲系統。原本是Apache Hadoop的一個組件,現在被拆分爲一個Hadoop的獨立子項目,在HBase(Hadoop的另外一個被拆分出來的子項目,用於分佈式環境下的超大數據量的DBMS)中也用到了ZooKeeper集羣。 

Hadoop,使用Zookeeper的事件處理確保整個集羣只有一個NameNode,存儲配置信息等.
  HBase,使用Zookeeper的事件處理確保整個集羣只有一個HMaster,察覺HRegionServer聯機和宕(dàng)機,存儲訪問控制列表等。

有人會懷疑ZooKeeper的執行能力,在ZooKeeper誕生的地方——Yahoo!他被用作雅虎消息代理的協調和故障恢復服務。雅虎消息代理是一個高度可擴展的發佈-訂閱系統,他管理着成千上萬臺聯及程序和信息控制系統。它的吞吐量標準已經達到大約每秒10000基於寫操作的工作量。對於讀操作的工作量來說,它的吞吐量標準還要高几倍。

二、ZooKeeper的介紹

2.1 ZooKeeper的概述

Zookeeper 是爲分佈式應用程序提供高性能協調服務的工具集合,也是Google的Chubby一個開源的實現,是Hadoop 的分佈式協調服務。它包含一個簡單的原語集5,分佈式應用程序可以基於它實現配置維護、命名服務、分佈式同步、組服務等。Zookeeper可以用來保證數據在ZK集羣之間的數據的事務性一致6。其中ZooKeeper提供通用的分佈式鎖服務7,用以協調分佈式應用。

Zookeeper作爲Hadoop項目中的一個子項目,是 Hadoop集羣管理的一個必不可少的模塊,它主要用來解決分佈式應用中經常遇到的數據管理問題,如集羣管理、統一命名服務、分佈式配置管理、分佈式消息隊列、分佈式鎖、分佈式協調等。在Hadoop中,它管理Hadoop集羣中的NameNode,還有在Hbase中Master Election、Server 之間狀態同狀步等。
  Zoopkeeper 提供了一套很好的分佈式集羣管理的機制,就是它這種基於層次型的目錄樹的數據結構,並對樹中的節點進行有效管理,從而可以設計出多種多樣的分佈式的數據管理模型。

2.2 ZooKeeper的設計目標

衆所周知,分佈式環境下的程序和活動爲了達到協調一致目的,通常具有某些共同的特點,例如,簡單性、有序性等。ZooKeeper不但在這些目標的實現上有自身特點,並且具有獨特優勢。下面我們將簡述ZooKeeper的設計目標。

(1)簡單化

ZooKeeper允許各分佈式進程通過一個共享的命名空間相互聯繫,該命名空間類似於一個標準的層次型的文件系統:由若干註冊了的數據節點構成(用Zookeeper的術語叫znode),這些節點類似於文件和目錄。典型的文件系統是基於存儲設備的,文傳統的文件系統主要用於存儲功能,然而ZooKepper的數據是保存在內存中的。也就是說,可以獲得高吞吐和低延遲。ZooKeeper的實現非常重視高性能、高可靠,以及嚴格的有序訪問。

高性能保證了ZooKeeper可以用於大型的分佈式系統,高可靠保證了ZooKeeper不會發生單點故障,嚴格的順序訪問保證了客戶端可以獲得複雜的同步操作原語。

(2)健壯性

就像ZooKeeper需要協調的分佈式系統一樣,它本身就是具有冗餘結構,它構建在一系列主機之上,叫做一個”ensemble”。
  構成ZooKeeper服務的各服務器之間必須相互知道,它們維護着一個狀態信息的內存映像8,以及在持久化存儲中維護着事務日誌和快照9。只要大部分服務器正常工作,ZooKeeper服務就能正常工作。
  客戶端連接到一臺ZooKeeper服務器。客戶端維護這個TCP連接,通過這個連接,客戶端可以發送請求、得到應答,得到監視事件以及發送心跳。如果這個連接斷了,客戶端可以連接到另一個ZooKeeper服務器。

(3)有序性

ZooKeeper給每次更新附加一個數字標籤,表明ZooKeeper中的事務順序,後續操作可以利用這個順序來完成更高層次的抽象功能,例如同步原語7。

(4)速度優勢

ZooKeeper特別適合於以讀爲主要負荷的場合。ZooKeeper可以運行在數千臺機器上,如果大部分操作爲讀,例如讀寫比例爲10:1,ZooKeeper的效率會很高。

2.3 ZooKeeper的集羣

ZK集羣如下圖2.1所示。這是實際應用的一個場景,該ZooKeeper集羣當中一共有5臺服務器,有兩種角色Leader和Follwer,5臺服務器連通在一起,客戶端有分別連在不同的ZK服務器上。如果當數據通過客戶端1,在左邊第一臺Follower服務器上做了一次數據變更,他會把這個數據的變化同步到其他所有的服務器,同步結束之後,那麼其他的客戶端都會獲得這個數據的變化。

圖 2.1

注意:

通常Zookeeper由2n+1臺servers組成,每個server都知道彼此的存在。每個server都維護的內存狀態鏡像以及持久化存儲的事務日誌和快照。爲了保證Leader選舉能過得到多數的支持,所以ZooKeeper集羣的數量一般爲奇數。對於2n+1臺server,只要有n+1臺(大多數)server可用,整個系統保持可用。

2.3.1 集羣中的角色

在ZooKeeper集羣當中,集羣中的服務器角色有兩種Leader和Learner,Learner角色又分爲Observer和Follower,具體功能如下:

1.領導者(leader),負責進行投票的發起和決議,更新系統狀態

2.學習者(learner),包括跟隨者(follower)和觀察者(observer),

3.follower用於接受客戶端請求並向客戶端返回結果,在選主過程中參與投票

4.Observer可以接受客戶端請求,將寫請求轉發給leader,但observer不參加投票過程,只同步leader的狀態,observer的目的是爲了擴展系統,提高讀取速度。

5. 客戶端(client),請求發起方

ZooKeeper的組件圖中給出了ZooKeeper服務的高層次的組件。除了請求處理器(requestprocessor)外,構成ZooKeeper服務的每個服務器都有一個備份。複製的數據庫(replicateddatabase)是一個內存數據庫,包含整個數據樹。爲了可恢復,更新會被log到磁盤,並且在更新這個內存數據庫之前,先序列化到磁盤。

每個ZooKeeper都爲客戶端提供服務。客戶端只連接到一個服務器,並提交請求。讀請求直接由本地的複製數據庫提供數據。對服務狀態進行修改的請求、寫請求通過一個約定的協議進行通訊。

作爲這個協議的一部分,所有的寫請求都被傳送到一個叫“首領(leader)”的服務器,而其他的服務器,叫做“(隨從)followers”,follower從leader接收信息修改的提議,並同意進行。當leader發生故障時,協議的信息層(messaginglayer)關注leader的替換,並同步到所有的follower。

ZooKeeper採用一個自定義的信息原子操作協議,由於信息層的操作是原子性的,ZooKeeper能保證本地的複製數據庫不會產生不一致。當leader接收到一個寫請求,它計算出寫之後系統的狀態,把它變成一個事務。

2.3.2 Zookeeper的讀寫機制和保證及特點

(1)ZooKeeper的讀寫機制

Zookeeper是一個由多個server組成的集羣

一個leader,多個follower

每個server保存一份數據副本

全局數據一致

分佈式讀寫

更新請求轉發,由leader實施

(2)ZooKeeper的保證

ZooKeeper運行非常快而且簡單。雖然它的目標是構建更加複雜服務(例如同步)的基礎,但它提供了一些保證,如下:

1.順序一致性:來自於客戶端的更新,根據發送的先後被順序實施。

2.唯一的系統映像:儘管客戶端連接到不同的服務器,但它們看到的一個唯一(一致性)的系統服務,client無論連接到哪個server,數據視圖都是一致的。

3.可靠性:一旦實施了一個更新,就會一直保持那種狀態,直到客戶端再次更新它,同時數據更新原子性,一次數據更新要麼成功,要麼失敗。

4.及時性:在一個確定的時間內,客戶端看到的系統狀態是最新的。

(3)ZooKeeper特點

最終一致性:client不論連接到哪個Server,展示給它都是同一個視圖,這是zookeeper最重要的性能。

可靠性:具有簡單、健壯、良好的性能,如果消息m被一臺服務器接受,那麼它將被所有的服務器接受。

實時性:Zookeeper保證客戶端將在一個時間間隔範圍內獲得服務器的更新信息,或者服務器失效的信息。 但由於網絡延時等原因,Zookeeper不能保證兩個客戶端能同時得到剛更新的數據,如果需要最新數據,應該在讀數據之前調用sync()接口。

等待無關(wait-free):慢的或者失效的client,不得干預快速的client的請求,使得每個client都能有效的等待。

原子性:更新只能成功或者失敗,沒有中間狀態。

順序性:包括全局有序和偏序兩種:

全局有序:是指如果在一臺服務器上消息a在消息b前發佈,則在所有Server上消息a都將在消息b前被髮布;

偏序:是指如果一個消息b在消息a後被同一個發送者發佈,a必將排在b前面

三、ZooKeeper服務

3.1 ZooKeeper數據模型

ZooKeeper擁有一個層次的命名空間,這個和分佈式的文件系統非常相似。不同的是ZooKeeper命名空間中的Znode,兼具文件和目錄兩種特點。既像文件一樣維護着數據、元信息、ACL、時間戳等數據結構,又像目錄一樣可以作爲路徑標識的一部分,並可以具有子znode。用戶對znode具有增、刪、改、查等操作(權限允許的情況下)。

znode具有原子性操作,每個znode的數據將被原子性地讀寫,讀操作會讀取與znode相關的所有數據,寫操作會一次性替換所有數據。zookeeper並沒有被設計爲常規的數據庫或者大數據存儲,相反的是,它用來管理調度數據,比如分佈式應用中的配置文件信息、狀態信息、彙集位置等等。這些數據的共同特性就是它們都是很小的數據,通常以KB爲大小單位。zooKeeper的服務器和客戶端都被設計爲嚴格檢查並限制每個znode的數據大小至多1M,當時常規使用中應該遠小於此值。

Zonde由路徑標註,ZooKeeper中被表示成有反斜槓分割的Unicode字符串,如同Unix中的文件路徑。路徑必須是絕對的,因此他們必須由反斜槓來字符開頭。除此以外,他們必須是唯一的,也就是說每一個路徑只有一個表示,因此這些路徑不能改變。ZooKeeper的數據結構, 與普通的文件系統極爲類似. 見下圖:

圖中的每個節點稱爲一個znode. 每個znode由3部分組成:

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

2.data:與該znode關聯的數據.

3.children:該znode下的子節點.

3.1.1 ZooKeeper節點Znode

ZooKeeper目錄樹中每一個節點對應一個Znode。每個Znode維護着一個屬性結構,它包含着版本號(dataVersion),時間戳(ctime,mtime)等狀態信息。ZooKeeper正是使用節點的這些特性來實現它的某些特定功能。每當Znode的數據改變時,他相應的版本號將會增加。每當客戶端檢索數據時,它將同時檢索數據的版本號。並且如果一個客戶端執行了某個節點的更新或刪除操作,他也必須提供要被操作的數據版本號。如果所提供的數據版本號與實際不匹配,那麼這個操作將會失敗。

Znode是客戶端訪問ZooKeeper的主要實體,它包含以下幾個特徵:

(1)Watches

客戶端可以在節點上設置watch(我們稱之爲監視器)。當節點狀態發生改變時(數據的增、刪、改)將會觸發watch所對應的操作。當watch被觸發時,ZooKeeper將會向客戶端發送且僅發送一條通知,因爲watch只能被觸發一次。

(2)數據訪問

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

(3)節點類型

ZooKeeper中的節點有兩種,分別爲臨時節點和永久節點。節點的類型在創建時即被確定,並且不能改變。
  ZooKeeper的臨時節點:該節點的生命週期依賴於創建它們的會話。一旦會話結束,臨時節點將被自動刪除,當然可以也可以手動刪除。另外,需要注意是,ZooKeeper的臨時節點不允許擁有子節點。
  ZooKeeper的永久節點:該節點的生命週期不依賴於會話,並且只有在客戶端顯示執行刪除操作的時候,他們才能被刪除。

(4)順序節點(唯一性的保證)

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

org.apache.zookeeper.CreateMode中定義了四種節點類型,分別對應:

PERSISTENT:永久節點

EPHEMERAL:臨時節點

PERSISTENT_SEQUENTIAL:永久節點、序列化

EPHEMERAL_SEQUENTIAL:臨時節點、序列化

3.1.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版本號

3.1.3 節點的屬性結構

通過前面的介紹,我們可以瞭解到,一個節點自身擁有表示其狀態的許多重要屬性,如下圖所示。

3.1.4 Zonde總結

(1)znode中的數據可以有多個版本,在查詢該znode數據時就需要帶上版本信息。如:set path version / delete path version

(2)znode可以是臨時znode,由create -e 生成的節點,一旦創建這個znode的client與server斷開連接,該znode將被自動刪除。

client和server之間通過heartbeat來確認連接正常,這種狀態稱之爲session,斷開連接後session失效。

(3)臨時znode不能有子znode。

(4)znode可以自動編號,由create -s 生成的節點,例如在 create -s /app/node 已存在時,將會生成 /app/node00***001節點。

(5)znode可以被監控,該目錄下某些信息的修改,例如節點數據、子節點變化等,可以主動通知監控註冊的client。事實上,通過這個特性,可以完成許多重要應用,例如配置管理、信息同步、分佈式鎖等等。

3.2 ZooKeeper服務中的操作

在ZooKeeper中有9個基本操作,如下圖所示:

更新ZooKeeper操作是有限制的。delete或setData必須明確要更新的Znode的版本號,我們可以調用exists找到。如果版本號不匹配,更新將會失敗。

更新ZooKeeper操作是非阻塞式的。因此客戶端如果失去了一個更新(由於另一個進程在同時更新這個Znode),他可以在不阻塞其他進程執行的情況下,選擇重新嘗試或進行其他操作。

儘管ZooKeeper可以被看做是一個文件系統,但是處於便利,摒棄了一些文件系統地操作原語。因爲文件非常的小並且使整體讀寫的,所以不需要打開、關閉或是尋地的操作。

3.2.1 watch觸發器

讀操作exists、getChildren和getData都被設置了watch,並且這些watch都由寫操作來觸發:create、delete和setData。ACL操作並不參與到watch中。當watch被觸發時,watch事件被生成,他的類型由watch和觸發他的操作共同決定。ZooKeeper所管理的watch可以分爲兩類:

1.數據watch(data watches):getData和exists負責設置數據watch;

2.孩子watch(child watches):getChildren負責設置孩子watch;

我們可以通過操作返回的數據來設置不同的watch:

1.getData和exists:返回關於節點的數據信息

2.getChildren:返回孩子列表

因此,一個成功的setData操作將觸發Znode的數據watch。

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

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

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

exists操作上的watch,在被監視的Znode創建、刪除或數據更新時被觸發。

getData操作上的watch,在被監視的Znode刪除或數據更新時被觸發。在被創建時不能被觸發,因爲只有Znode一定存在,getData操作纔會成功。

getChildren操作上的watch,在被監視的Znode的子節點創建或刪除,或是這個Znode自身被刪除時被觸發。可以通過查看watch事件類型來區分是Znode還是他的子節點被刪除:NodeDelete表示Znode被刪除,NodeDeletedChanged表示子節點被刪除。

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

watch事件包括了事件所涉及的Znode的路徑,因此對於NodeCreated和NodeDeleted事件來說,根據路徑就可以簡單區分出是哪個Znode被創建或是被刪除了。爲了查詢在NodeChildrenChanged事件後哪個子節點被改變了,需要再次調用getChildren來獲得新的children列表。同樣的,爲了查詢NodeDeletedChanged事件後產生的新數據,需要調用getData。在兩種情況下,Znode可能在獲取watch事件或執行讀操作這兩種狀態下切換,在寫應用程序時,必須記住這一點。

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

1. 連接狀態事件(type=None, path=null)

這類事件不需要註冊,也不需要我們連續觸發,我們只要處理就行了。

2. 節點事件

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

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

(2)節點事件的觸發,通過函數exists,getData或getChildren來處理

這類函數,有雙重作用:

1. 註冊觸發事件

2. 函數本身的功能

函數的本身的功能又可以用異步的回調函數來實現,重載processResult()過程中處理函數本身的的功能。

函數還可以指定自己的watch,所以每個函數都有4個版本。根據自己的需要來選擇不同的函數,不同的版本。

3.3 ZooKeeper訪問控制列表ACL

ZooKeeper使用ACL來對Znode進行訪問控制。ACL的實現和Unix文件訪問許可非常相似:它使用許可位來對一個節點的不同操作進行允許或禁止的權限控制。但是,和標準的Unix許可不同的是,Zookeeper對於用戶類別的區分,不止侷限於所有者(owner)、組 (group)、所有人(world)三個級別。Zookeeper中,數據節點沒有“所有者”的概念。訪問者利用id標識自己的身份,並獲得與之相應的 不同的訪問權限。

注意:

傳統的文件系統中,ACL分爲兩個維度,一個是屬組,一個是權限,子目錄/文件默認繼承父目錄的ACL。而在Zookeeper中一個ACL和一個ZooKeeper節點相對應。並且,父節點的ACL與子節點的ACL是相互獨立的。也就是說,ACL不能被子節點所繼承,父節點所擁有的權限與子節點所用的權限都沒有任何關係。

Zookeeper支持可配置的認證機制。它利用一個三元組來定義客戶端的訪問權限:(scheme:expression, perms) 。其中:

1.scheme:定義了expression的含義。

如:(host:host1.corp.com,READ),標識了一個名爲host1.corp.com的主機,有該數據節點的讀權限。

2.Perms:標識了操作權限。

如:(ip:19.22.0.0/16, READ),表示IP地址以19.22開頭的主機,有該數據節點的讀權限。

Zookeeper的ACL也可以從三個維度來理解:一是,scheme; 二是,user; 三是,permission,通常表示爲scheme:id:permissions,如下圖所示。

1.world : id格式:anyone。

如:world:anyone代表任何人,zookeeper中對所有人有權限的結點就是屬於world:anyone的。

2.auth : 它不需要id。

注:只要是通過authentication的user都有權限,zookeeper支持通過kerberos來進行認證, 也支持username/password形式的認證。

3.digest: id格式:username:BASE64(SHA1(password))。

它需要先通過username:password形式的authentication。

4.ip: id格式:客戶機的IP地址。

設置的時候可以設置一個ip段。如:ip:192.168.1.0/16, 表示匹配前16個bit的IP段

5.super: 超級用戶模式。

在這種scheme情況下,對應的id擁有超級權限,可以做任何事情

ZooKeeper權限定義如下圖所示:

ZooKeeper內置的ACL模式如下圖所示:

當會話建立的時候,客戶端將會進行自我驗證。另外,ZooKeeper Java API支持三種標準的用戶權限,它們分別爲:

1.ZOO_PEN_ACL_UNSAFE:對於所有的ACL來說都是完全開放的,任何應用程序可以在節點上執行任何操作,比如創建、列出並刪除子節點。

2.ZOO_READ_ACL_UNSAFE:對於任意的應用程序來說,僅僅具有讀權限。

3.ZOO_CREATOR_ALL_ACL:授予節點創建者所有權限。需要注意的是,設置此權限之前,創建者必須已經通了服務器的認證。

下面演示一個通過digest(用戶名密碼的方式)爲創建的節點設置ACL的例子,代碼如下:

import org.apache.zookeeper.*;
import org.apache.zookeeper.server.auth.DigestAuthenticationProvider;
import org.apache.zookeeper.data.*;
import java.util.*;

public class NewDigest {

    public static void main(String[] args) throws Exception {//new一個acl
        List acls = new ArrayList();   

     //添加第一個id,採用用戶名密碼形式
        Id id1 = new Id("digest",DigestAuthenticationProvider.generateDigest("admin:admin"));
        ACL acl1 = new ACL(ZooDefs.Perms.ALL, id1);
        acls.add(acl1);
        
     //添加第二個id,所有用戶可讀權限
        Id id2 = new Id("world", "anyone");
        ACL acl2 = new ACL(ZooDefs.Perms.READ, id2);
        acls.add(acl2);

        // zk用admin認證,創建/test ZNode。
     ZooKeeper zk = new ZooKeeper("host1:2181,host2:2181,host3:2181",2000, null);
     zk.addAuthInfo("digest", "admin:admin".getBytes());
     zk.create("/test", "data".getBytes(), acls, CreateMode.PERSISTENT);
   }
}

3.4 ZooKeeper的執行

ZooKeeper服務可以以兩種模式運行。在單機模式下,只有一個ZooKeeper服務器,便於用來測試。但是他沒有高可用性和恢復性的保障。在工業界,ZooKeeper以複合模式10運行在一組叫ensemble的集羣上。ZooKeeper通過複製來獲得高可用性,同時,只要ensemble中大部分機器運作,就可以提供服務。在2n+1個節點的ensemble中,可以承受n臺機器故障。

ZooKeeper的思想非常簡單:他所需要做的就是保證對Znode樹的每一次修改都複製到ensemble中的大部分機器上去。如果機器中的小部分出故障了,那麼至少有一臺機器將會恢復到最新狀態,其他的則保存這副本,直到最終達到最新狀態。Zookeeper採用Zab協議,它分爲兩個階段,並且可能被無限的重複。

(1)階段1:領導者選舉

在ensemble中的機器要參與一個選擇特殊成員的進程,這個成員叫領導者,其他機器腳跟隨者。在大部分的跟隨者與他們的領導者同步了狀態以後,這個階段纔算完成。

(2)階段2:原子廣播

所有的寫操作請求被傳送給領導者,並通過廣播將更新信息告訴跟隨者。當大部分跟隨者執行了修改之後,領導者就提交更新操作,客戶端將得到更新成功的迴應。未獲得一致性的協議被設計爲原子的,因此無論修改失敗與否,他都分兩階段提交。

如果領導者出故障了,城下的機器將會再次進行領導者選舉,並在新領導被選出前繼續執行任務。如果在不久後老的領導者恢復了,那麼它將以跟隨者的身份繼續運行。領導者選舉非常快,由發佈的結果所知,大約是200毫秒,因此在選舉是性能不會明顯減慢。
所有在ensemble中的機器在更新它們內存中的Znode樹之前會先將更新信息寫入磁盤。讀操作請求可由任何機器服務,同時,由於他們只涉及內存查找,因此非常快。

3.5 ZooKeeper一致性

在ensemble中的領導者和跟隨着非常靈活,跟隨者通過更新號來滯後領導者11,結果導致了只要大部分而不是所有的ensemble中的元素確認更新,就能被提交了。對於ZooKeeper來說,一個較好的智能模式是將客戶端連接到跟着領導者的ZooKeeper服務器上。客戶端可能被連接到領導者上,但他不能控制它,而且在如下情況時,甚至可能不知道。參見下圖:

每一個Znode樹的更新都會給定一個唯一的全局標識,叫zxid(表示ZooKeeper事務“ID”)。更新是被排序的,因此如果zxid的z1<z2,那麼z1就比z2先執行。對於ZooKeeper來說,這是分佈式系統中排序的唯一標準。

ZooKeeper是一種高性能、可擴展的服務。ZooKeeper的讀寫速度非常快,並且讀的速度要比寫快。另外,在進行讀操作的時候,ZooKeeper依然能夠爲舊的數據提供服務。這些都是由ZooKeeper所提供的一致性保證的,它具有如下特點:

(1)順序一致性

任何一個客戶端的更新都按他們發送的順序排序,也就意味着如果一個客戶端將Znode z的值更新爲值a,那麼在之後的操作中,他會將z更新爲b,在客戶端發現z帶有值b之後,就不會再看見帶有值a的z。

(2)原子性

更新不成功就失敗,這意味着如果更新失敗了,沒有客戶端會知道。☆☆

(3)單系統映像☆

無論客戶端連接的是哪臺服務器,他與系統看見的視圖一樣。這就意味着,如果一個客戶端在相同的會話時連接了一臺新的服務器,他將不會再看見比在之前服務器上看見的更老的系統狀態,當服務器系統出故障,同時客戶端嘗試連接ensemble中的其他機器時,故障服務器的後面那臺機器將不會接受連接,直到它連接到故障服務器。

(4)容錯性☆☆☆

一旦更新成功後,那麼在客戶端再次更新他之前,他就固定了,將不再被修改,這就會保證產生下面兩種結果:

如果客戶端成功的獲得了正確的返回代碼,那麼說明更新已經成功。如果不能夠獲得返回代碼(由於通信錯誤、超時等原因),那麼客戶端將不知道更新是否生效。

當故障恢復的時候,任何客戶端能夠看到的執行成功的更新操作將不會回滾。

(5)實時性☆☆

在任何客戶端的系統視圖上的的時間間隔是有限的,因此他在超過幾十秒的時間內部會過期。這就意味着,服務器不會讓客戶端看一些過時的數據,而是關閉,強制客戶端轉到一個更新的服務器上。

解釋一下:

由於性能原因,讀操作由ZooKeeper服務器的內存提供,而且不參與寫操作的全局排序。這一特性可能會導致來自使用ZooKeeper外部機制交流的客戶端與ZooKeeper狀態的不一致。舉例來說,客戶端A將Znode z的值a更新爲a',A讓B來讀z,B讀到z的值是a而不是a’。這與ZooKeeper的保證機制是相容的(不允許的情況較作“同步一致的交叉客戶端視 圖”)。爲了避免這種情況的發生,B在讀取z的值之前,應該先調用z上的sync。Sync操作強制B連接上的ZooKeeper服務器與leader保 持一致這樣,當B讀到z的值時,他將成爲A設置的值(或是之後的值)

容易混淆的是:

sync操作只能被異步調用12。這樣操作的原因是你不需要等待他的返回,因爲ZooKeeper保證了任何接下去的操作將會發生在sync在服務器上執行以後,即使操作是在sync完成前被調用的。

這些已執行的保證後,ZooKeeper更高級功能的設計與實現將會變得非常容易,例如:leader選舉、隊列,以及可撤銷鎖等機制的實現。

3.6 ZooKeeper會話

ZooKeeper客戶端與ensemble中的服務器列表配置一致,在啓動時,他嘗試與表中的一個服務器相連接。如果連接失敗了,他就嘗試表中的其他服務器,以此類推,知道他最終連接到其中一個,或者ZooKeeper的所有服務器都無法獲得時,連接失敗。

一旦與ZooKeeper服務器連接成功,服務器會創建與客戶端的一個新的對話。每個回話都有超時時段,這是應用程序在創建它時設定的。如果服務器沒有在超時時段內得到請求,他可能會中斷這個會話。一旦會話被中斷了,他可能不再被打開,而且任何與會話相連接的臨時節點都將丟失。

無論什麼時候會話持續空閒長達一定時間,都會由客戶端發送ping請求保持活躍(猶如心跳)。時間段要足夠小以監測服務器故障(由讀操作超時反應),並且能再回話超市時間段內重新連接到另一個服務器。

在ZooKeeper中有幾個time參數。tick time是ZooKeeper中的基本時間長度,爲ensemble裏的服務器所使用,用來定義對於交互運行的調度。其他設置以tick time的名義定義,或者至少由它來約束。

創建更復雜的臨時性狀態的應用程序應該支持更長的會話超時,因爲重新構建的代價會更昂貴。在一些情況下,我們可以讓應用程序在一定會話時間內能夠重啓,並且避免會話過期。(這可能更適合執行維護或是升級)每個會話都由服務器給定一個唯一的身份和密碼,而且如果是在建立連接時被傳遞給ZooKeeper的話,只要沒有過期它能夠恢復會話。

這些特性可以視爲一種可以避免會話過期的優化,但它並不能代替用來處理會話過期。會話過期可能出現在機器突然故障時,或是由於任何原因導致的應用程序安全關閉了,但在會話中斷前沒有重啓。

3.7 ZooKeeper實例狀態

Zookeeper對象的轉變是通過其生命週期中的不同狀態來實現。可以使用getState()方法在任何時候去查詢他的狀態:

public states getState()

Zookeeper狀態事務,如圖3.5所示

圖 3.5 Zookeeper狀態事務

  getState()方法的返回類型是states,states是枚舉類型代表Zookeeper對象可能所處的不同狀態,一個Zookeeper實例可能一次只處於一個狀態。一個新建的Zookeeper實例正在於Zookeeper服務器建立連接時,是處於CONNECTING狀態的。一旦連接建立好以後,他就變成了Connected狀態。

使用Zookeeper的客戶端可以通過註冊Watcher的方法來獲取狀態轉變的消息。一旦進入了CONNNECTED狀態,Watcher將獲得一個KeepState值爲SyncConnected的WatchedEvent。

注意Zookeeper的watcher有兩個職責:

<1>瞭解Zookeeper的狀態改變。傳遞給ZooKeeper對象構造函數的(默認)watcher,被用來監測狀態的改變。

<2>瞭解Zonde的改變。監測Zonde的改變既可以使用專門的實例設置到讀操作上,也可以使用讀操作的默認watcher。

Zookeeper實例可能失去或重新連接Zookeeper服務,在CONNECTED和CONNECTING狀態中切換。如果連接斷開,watcher得到一個Disconnected事件。學要注意的是,這些狀態的遷移是由Zookeeper實例自己發起的,如果連接斷開他將自動嘗試自動連接。

如果任何一個close()方法被調用,或是會話由Expired類型的KeepState提示過期時,ZooKeeper可能會轉變成第三種狀態CLOSED。一旦處於CLOSED狀態,Zookeeper對象將不再是活動的了(可以使用states的isActive()方法進行測試),而且不能被重用。客戶端必須建立一個新的Zookeeper實例才能重新連接到Zookeeper服務。

下期預告:ZooKeeper的集羣安裝和配置,敬請關注。本期內容及供大家參考,有什麼不對的地方,希望大家給予指點更正。如果您覺得,文章寫得還行,就擡起您的貴手,點一下推薦

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章