引言html
這個文檔是爲了想利用ZooKeeper的協調服務來建立分佈式應用的開發者提供的指南。它包括概念和實踐的信息。node
這個文檔的一開始的的四部分呈現了不一樣ZooKeeper高級概念的的討論。理解Zookeeper是怎麼工做的和如何使用它同等重要。它不包含源碼,可是它確實假設你熟悉分佈式計算的問題。在這第一組的部分是:算法
下面的四部分提供了實踐編程信息。他們是:數據庫
本書最後的附錄包含其它有用的ZooKeeper相關的信息express
本文檔的大部分信息能夠做爲獨立的參考材料。然而,在開始你第一個ZooKeeper應用以前,你應該至少閱讀ZooKeeper數據模型和ZooKeeper基本操做的章節。同時,簡單的編程例子對於理解ZooKeeper客戶端應用的基本結構是不幫助的。apache
ZooKeeper數據模型編程
ZooKeeper有一個層次結構的命名空間,就像是一個分佈的文件系統。惟一的不一樣是每個節點能夠有一個和它關聯的數據,孩子節點也是。它好像是有一個文件系統容許文件是一個目錄。節點的路徑老是表達爲一個典型的絕對的斜線分隔的路徑:沒有相對路徑。任何nuicode字符能夠被用在路徑中同時受如下的約束:緩存
ZNodes
每個ZooKeeper樹中的每個節點被稱爲znode。Znodes維護了一個包括數據改變,acl改變的版本號的數據結構。這個數據結構一樣也有時間戳。版本號和時間戳,容許ZooKeeper校驗緩存和協調更新。每次znode的數據改變,版本號相應地增長。例如,安全
每當客戶端檢索數據,它一樣也收到數據的版本號。當客戶端執行一個更新或刪除,它必須提供正在改變的znode的版本號。若是它提供了版本號和實際的版本號不匹配,更新將會失敗。(這個行爲能夠被覆蓋。更多信息請參考...)服務器
注意
在分佈式應用的工程中,單詞node被能夠稱爲一個真實的主機,一個服務器,一個集羣中的成員,一個客戶進程,等等。在ZooKeeper的文檔中,znodes指的是數據節點。Servers指的是組成ZooKeeper服務的機器;quorum peers指的是組成集羣的servers;
client指的是任何使用ZooKeeper服務的主機或進程。
Znodes是開發者主要訪問的實體。他們有一些在這裏值得提起的特性。
Watches
客戶端能夠在znodes上設置監視器(watches)。這個znode的改變將觸發這個監視器而後清除這個監視器。當一個監視器被觸發,ZooKeeper給客戶端發送一個通知。更多關於監視器的信息能夠在ZooKeeper Watches部分找到。
Data Access
存儲在命名空間中的每個znode的數據被原子性地讀和寫。讀獲取全部跟這個znode關聯的全部數據,寫替換全部的數據。每個節點有一個訪問控制列表(ACL)限制誰能夠作和能夠作什麼 。
ZooKeeper不是設計用來做爲一個數據庫或大對象的存儲。相反地,它管理協調數據。這些數據能夠是配置,狀態信息等等。不一樣形式的協調數據有一個共同的特性就是它們相對來講數據量小:以千字節爲單位。ZooKeeper客戶端和服務端實現必需檢查確保znode的數據小於1M,可是數據平均來講應該小於這個值。對相對大的數據的操做將引發一些操做耗費比其它更多的時間而且將影響一些操做的延遲,由於須要更多的時間在網絡上移動數據和移動到存儲媒介上。若是須要存儲大數據,一般處理這種數據的模式是把它們存儲在容量存儲系統上。例如NFS或HDFS,而且在ZooKeeper上存儲指針。
Ephemeral Nodes
ZooKeeper也有短暫節點的概念。這些節點只要建立它的會話是活躍的就會一直存在。當會話結束的時候,這個節點就會被刪除。由於這樣的行爲特性,短暫的節點不容許有孩子節點。
Sequence Nodes - 惟一命名
當建立一個znode你也能夠要求ZooKeeper追加一個單調遞增的計數器在路徑的結尾。這個計數器對父節點來講是惟一的。這個計數器的格式是%010d -- 也就是說10位數和0(數字0)襯墊(不足10位補0)(計算器被格式化爲這種形式是爲了簡化存儲)。也就是"<path>0000000001"。參考Queue Recipe獲取這個特性的例子。注意:被用來存儲下一個序列數字的計算器是一個被父節點維護的有符號的(signed)int,計算器將會溢出當增長超過2147483647(產生一個名字」<path>-2147483647「)。
Time in ZooKeeper
ZooKeeper以多種方式記錄時間:
每次改變ZooKeeper狀態將會收到一個zxid形式的標記(ZooKeeper的事務Id)。這暴露了ZooKeeper的全部改變的總序列。每個改變將有一個惟一和zxid而且若是zxid1比zxid2小那麼zxid1在zxid2以前發生(happend before zxid2).
一個節點每改變一次將引發 這個節點的版本號增長一次。這三個版本號是version(znode的數據改變的次數),cversion(znode字節點的改變的次數),和aversion(znode節點的ACL改變的次數)。
當使用多服務器的ZooKeeper,服務器之間使用ticks來定義好比狀態上傳,會話超時,節點以前是鏈接超時等等的時間。tick時間只經過最小的會話超時時間來暴露(2倍的tick時間);若是一個客戶端請求會話超時小於最小的會話超時時間,那麼服務端將會告訴客戶端真實的會話超時時間爲最小的會話超時時間。
ZooKeeper不使用真實的時間或時鐘時間,除了把時間戳在znode建立和修改的時候加入到數據結構。
ZooKeeper數據結構
ZooKeeper中的每個znode的數據結構是由下面的字段組成的:
建立znode時的zxid
最後修改znode的zxid
從znode建立以來的毫秒時間
從znode最後修改以來的毫秒時間
znode數據改變的次數
znode孩子節點改變的次數
znode的ACL改變的次數
若是是一個短暫節點,這個值就是znode的擁有者的會話id。若是不是短暫節點,它的值爲0。
znode的數據字段的長度
znode孩子節點的數量
ZooKeeper Sessions
一個ZooKeeper客戶端經過使用一個語言綁定建立一個握手來創建和ZooKeeper服務的會話。一旦創建,處理器以CONNECTING狀態開始而且客戶端庫試圖鏈接組成ZooKeeper服務的其中一個服務端,這時它就變成CONNECTED狀態。在正常操做下將會處於這二者之中的狀態。若是發生不可恢復的錯誤,例如會話過時或受權失敗,若是若是應用顯式地關閉了此次握手。此次握手將會變成CLOSED狀態。下面的圖表展現了ZooKeeper客戶端可能出現的狀態轉換。
爲了建立客戶端會話應用代碼必需提供一個包括以逗號分割主機:端口(host:port)列表對組成的鏈接字符串,每個對應一個ZooKeeper服務端(例如:"127.0.0.1:4545"或者」127.0.0.1:3000,127.0.0.1:3001,127.0.0.1:3002「)。ZooKeeper客戶端庫將挑選一個任一的服務端而且試圖進行鏈接。若是這個鏈接失敗,或者若是由於任何緣由客戶端斷開鏈接,客戶端就自動地嘗試鏈接列表中的下一個服務端,直到(從新)創建一個鏈接。
3.2.0新加 一個可選的"chroot"後綴能夠加入到鏈接字符串。這將會運行客戶端命令並相對於這個root解釋全部的路徑(和nuix的chroot命令類似)。若是使用例子看起來像這樣:"127.0.0.1:4545/app/a"或者"127.0.0.1:3000,127.0.0.1:3001,127.0.0.1:3002/app/a"。這裏客戶端將會以"/app/a"爲根目錄而且全部的路徑將會是相對於這個根目錄 - 例如 getting/setting/等等"/foo/bar"結果將是在」/app/a/foo/bar"上操做(從服務端的角度來看)。這個特色在一個特定ZooKeeper服務的每個用戶是不一樣根目錄的多用戶環境是很是有用的。這使重用變得更簡單由於每個用戶能夠改變他的/她的應用使它好像在"/"的根目錄下,同時真實的路徑(也就是 /app/a)能夠被肯定在部署的時候。
當客戶端獲得一個到服務端句柄,ZooKeeper建立一個ZooKeeper會話,以64位的數字表示,分配給客戶端。若是客戶端鏈接到不一樣的ZooKeeper服務端,它將會發送會話id做爲鏈接握手中的一部分。做爲一個安全措施,服務端爲會話Id建立一個密碼任何ZooKeeper服務端能夠用來校驗。這個密碼和會話id被髮送到客戶端當鏈接創建的時候。當和新的服務端從新創建鏈接的時候客戶端發送這個密碼和會話id到服務端。
ZooKeeper客戶端庫建立ZooKeeper會話的參數中的一個是以毫秒錶示的會話超時時間。客戶端發送一個請求超時時間,服務端在超時時間內回覆客戶端。目前的實現要求這個超時時間最小是2位的tickTime(在服務端的配置文件裏設置),最多20倍的tickTime.ZooKeeper客戶端API容許訪問這個超時時間。當一個客戶端(會話)被ZK服務集羣隔離開來,它將開始搜索在會話創建裏的服務器列表。最終,當客戶端和任一服務端的鏈接從新創建,這個會話將要麼再次轉變爲「connected」狀態(若是在會話超時時間內創建鏈接)要麼轉變爲"過時"狀態(若是在會話超時後創建鏈接)。不建議爲斷開創建一個新的會話對象(一個新的ZooKeeper.class或zookeeper句柄)。ZK客戶端將爲你處理重連。特別地咱們把啓發式算法內建到客戶端庫來處理像"羊羣效應"(herd effect),等等,只有當你被通知了會話到期了才新建一個會話(強制性的)。
會話到期時間被ZooKeeper集羣自己管理,而不是客戶端。當客戶端跟ZK集羣創建一個會話,它提供一個"過時時間"值。這個值被集羣決定客戶端會話何時過時。過時發生當集羣不能聽到客戶端的消息時在指定的會話超時週期內(也就是沒有心跳)。當會話過時集羣將會刪除全部的屬於這個會話的短暫節點並通知全部的連接的客戶端這一改變(任何監視這些節點的客戶端)。這時會話過時的會話的客戶端仍然和集羣處於斷開狀況。它將不會被通知會話過時只到/除非它和集羣從新創建連接。客戶端一直處於斷開狀態只到它和集羣從新創建連接。這時過時會話的監視器將會收到"會話過時"通知。
對於一個會話過時的狀態轉換能夠被過時會話的監視器看到的例子:
另外一個ZooKeeper的會話創建和參數是默認的監視器。監視器被通知當在客戶端發生任何改變。例如若是客戶端失去了服務端的鏈接它將會被通知,或者客戶端的會話過時,等等。這個監視器應該考慮初始狀態到失去鏈接的狀態(也就是說在任何狀態改變前事務被客戶端庫送到觀察者)。在一個新鏈接的狀況下,第一個送給觀察者的事件一般是會話創建事件。
會話經過客戶端發送請求保持存活。若是會話空閒一段超時會話的時間,客戶端將發送一個PING請求來保持會話是活着的。這個PING請求不只使ZooKeeper服務端知道客戶端是仍然存活着,它也使客戶端檢驗到ZooKeeper服務端的鏈接仍然活躍。PING的時機是至關保守以使確保合理的時間來檢測一個死去的鏈接和從新鏈接一個新的服務端。
一旦到服務端的鏈接成功地創建(connected)這裏有最基本的客戶端庫產生鏈接的兩個例子,當或者同步或異步的操做被執行而且下面的其中一個持有:
3.2.0新加 -- SessionMovedException.有一個內部的異常叫作SessionMovedException,一般不被客戶端看到。這個異常發生由於在一個鏈接中收到一個請求,這個會話已經被鏈接到一個不一樣的服務端。這個錯誤的一般緣由是一個服務端發送一個請求到服務端,可是網絡包有延遲,全部客戶端超時而且鏈接到一個新的服務端。當延遲的數據包到達了第一個服務端,老的服務端檢測到這個會話已經移動 了,而且關閉客戶端鏈接。客戶端一般不會看到這個錯誤由於他們不會從這個老的鏈接中讀數據(老的鏈接一般已經關閉)。這個條件能夠被看到的狀況是當兩個客戶端試圖從新創建相同鏈接用一個保存的會話id和密碼。其中一個客戶端將從新創建鏈接而另外一個將被斷開鏈接(致使試圖從新鏈接它的會話的對無期限地)
Updating the list of servers 咱們容許一個客戶端經過一個新的逗號分隔的host:port列表對來更新鏈接字符串,這每個對應一個ZooKeeper服務端。這個功能調用 一個機率的負載均衡邏輯,這個邏輯可能引發客戶端和它的當前主機斷開鏈接,以便達到新列表的每個服務端都有均一鏈接數。若是當前鏈接的主機不在新的列表裏面,那麼此次調用將老是引發這個鏈接被丟棄。不然,這個決定是基因而否服務端的數量是增長了仍是減少了和增長減少了多少。
例如,若是選擇的鏈接字符串包括3個主機而且如今 的列表包含3個主機和2個新主機。3個主機中的每個主機的40%將轉移到新主機中的一臺爲了平衡壓力。這個邏輯將致使這個客戶端有40%的機率丟掉當前鏈接的主機,而且在這個例子當中這個客戶端鏈接到2個新主機中的一個,隨機選擇。
另外一個例子 -- 假如咱們有5個主機,如今更新這個列表來刪除其中兩個主機,剩下的3個主機的鏈接仍然保持鏈接,而後全部鏈接到刪除的兩個主機的鏈接將須要移動到剩下3臺中的一臺,隨機選擇。若是鏈接被丟掉,客戶端移動到一個特殊的模式,它利用機率算法來選擇一個新的主機,而不只僅是輪詢。
在第一個例子中,第一個客戶端有40%的機率決定斷掉鏈接,若是這個決定肯定,它將試圖隨機鏈接一個新主機,而且只有它不能鏈接到任何一個新主機的時候,它將試圖鏈接老的主機。在找到一個服務端以後,或者嘗試了全部新列表中的服務端以後而且鏈接失敗,客戶端返回普通操做模式,它從鏈接字符串選擇任一一個服務端而且嘗試鏈接它。若是失敗,它將輪訓地嘗試不一樣的主機。(參考上面開始選擇服務端的邏輯)
全部ZooKeeper中的讀操做 - getData(),getChildren(),和exist() - 有一個設置監視器選項。這裏是ZooKeeper的監視器的定義:一個監視器事件是一次性觸發,發送給設置這個監視器的客戶端,這個事件當設置監視器的數據發生改變的時候發生。監視器的定義有三個關鍵點須要考慮:
一個監聽事件將會被髮給客戶端在數據已經改變的時候。例如,若是一個客戶端作了一個getData("/znode1", true)操做,而後/znode1的數據被改變或刪除,客戶端將等到一個/znode1的監聽事件。若是/znode1再次改變,將沒有監聽事件被髮生,除非客戶端作了另外一個讀操做而且設置 一個新的監視器。
這意爲着一個事件正在發送給客戶端的路上,可是可能尚未到達客戶端在成功返回以前改變操做到達客戶端以前。監視器被異步地發送給監聽聽。ZooKeper提供了一個順序保證:一個客戶端將不會看到它設置監視器的數據的改變直接它看到了監視事件。網絡延遲或其它因素可能致使不一樣看到監視器而且返回代碼在不一樣的時候點。關鍵點是不一樣的客戶端看到的任務東西都是有順序的。
這是指一個節點能夠以不一樣的方式改變。它有助於把ZooKeeper想像成一個維護兩個監視器的列表:數據監視器和孩子監視器。getData()和exists()設置數據監視器。getChildren()設置孩子監視器。另外,它可能有幫助的根據數據返回的類型想像 正在設置的監視器。getData()和exixts()返回關於這個節點的信息,然而getChildred()返回一個孩子的列表。所以,setData()將觸發給znode設置的數據監視器(假設成功設置)。一個成功的create()將觸發正在被建立的這個znode的數據監視器,父節點的孩子監視器。一個成功的delete()將觸發正在被刪除的znode的數據監視器和孩子監視器(由於沒有了孩子節點)同時也觸發這個被刪除節點的父節點的孩子觸發器。
監視器被維護在客戶端鏈接的ZooKeeper服務端的本地。這使監視器設置,維護,和分發都很輕量。當一個客戶端鏈接到一個新的服務端,監視器將會觸發任何會話事件。監視器將不會被收到當正在和服務端處於斷開狀態。當一個客戶端從新鏈接,先前註冊的監視器將被從新註冊若是須要被觸發。一般這些透明地發生。有一種狀況一個監視器可能丟失:尚未建立的znode的存在性的監視器將被丟失若是znode被建立而且刪除在處於斷開鏈接的時候。
咱們有三個讀取ZooKeeper狀態的的調用能夠設置監視器:exists(),getData(),和getChildren().下面的列表詳細說明了一個監視器能夠觸發的事件和能夠觸發的調用:
調用exists()時候觸發。
調用exists(),getData(),和getChildren()的時候觸發。
調用exists()和getData()的時候觸發
調用getChildren的時候觸發
咱們可能調用removeWatches來刪除註冊到一個znode的監視器。同時,ZooKeeper客戶端能夠在本地刪除監視器即便沒有服務器跟它鏈接經過設置本地標誌爲true.下面的列表詳細描述了將被觸發的事件在成功刪除監視器以後。
調用getChildren增長的監視器
調用exists或getData增長的監視器
關於監視器,ZooKeeper維護三個擔保
ZooKeeper使用ACLs來控制對znodes的訪問(ZooKeeper的數據樹的數據節點)。ACL的實現和UNIX文件訪問權限很是類似:它用權限位來容許/不容許對應節點的不一樣的操做和權限位應用的範圍。和標準的NUIX權限不一樣的是,ZooKeeper節點沒有對用戶,組,傲世界(其它的)的三個標準的範圍限制(文件的擁有者)。ZooKeeper沒有znode擁有者這種概念。相反地,ACL指定了一組id和這些id關聯的權限。
也注意ACL只適用一些特定的znode.特別地它沒有應用到孩子。例如,若是/app 只對ip:172.16.16.1可讀而且/app/status是全局可讀的,任何人將能夠讀取/app/status;ACL是不可遞歸的。
ZooKeeper支持可插拔的認證方案。Ids被以cheme:id的形式指定,這裏scheme是id對象的認證方案。例如, ip:172.16.16.1是主機172.16.16.1的id.
當客戶端鏈接到一個ZooKeeperu而且認證了它本身,ZooKeeper把客戶端鏈接關聯到這個客戶端對應的id上。這個ids根據znode的ACLs被檢查當客戶端試圖訪問一個節點的時候。ACLs被以成對的(scheme:expression, perms)組成。expression的格式對scheme來講是特定的。例如,(ip:19.22.0.0/16, READ)對任何ip地址以19.22開頭的客戶端有讀權限。
ZooKeeper支持以下的權限:
對於細粒度的訪問控制CREATE和DELETE權限已經被WRITE權限給打破。下面是CREATE和DELETE的例子:
你但願A能夠設置ZooKeeper節點的值,可是不能建立或刪除子節點。
沒有DELETE的CREATE:客戶端建立請求經過在父節點建立ZooKeeper節點。你想全部的客戶端能夠增長,可是隻有請求進程能夠刪除。(這就好像文件的APPEND權限)
同時,ADMIN權限在這裏是由於ZooKeeper沒有一個文件全部者的概念。在某種意義上ADMIN權限指明瞭擁有者。ZooKeeper不支持LOOKUP權限(在目錄上的執行權限位容許你LOOKUP即便你不能羅列這個目錄)。每個人顯式地擁有LOOKUP權限。這容許你一個節點,可是不會有更多。(問題是,若是你想在不存在的節點上調用zoo_exists(),沒有任何權限檢查)
內置的ACL方案
ZooKeeper有如下內置的方案:
world有一個單獨的id,anyone,這表明任何人。
auth 不使用任何id,表示任何受權的用戶
digest 使用一個 username:password字符串來生成 一個MD5哈希。而後被用來做爲ACL ID標示。受權經過發送一個明文username:password來完成。當在ACL中使用這個表達式將會是username:base64 encoded SHA1 password digest
ip 用客戶端的主機ip做爲ACL ID標示。ACl表達式addr/bits,這裏的addr的前bits位和客戶端主機ip的前bits位來匹配。
-----------------------------------------------------這裏有一段關於c語言的部分被省略了--------------------------------------------------------------------
ZooKeeper運行不一樣的認證方案的不一樣的環境中,因此它有一個徹底可插拔的認證框架。甚至內置的認證方案也是用的這個可插拔的認證框架。
爲了理解這個認證框架是怎麼工做的,首先你必須理解兩個主要的認證操做。框架首先必需認證客戶端。這個一般一旦客戶端鏈接服務端的時候被完成而且包含從客戶端發送的校驗信息而且把這些和鏈接關聯起來。第二個被框架處理的操做是在ACL中找一個對應這個客戶端的條目。ACL條目是<idspec, permissions> 對。idspec多是一個簡單的string和匹配這個鏈接關聯的認證信息或者它多是一對這個信息計算的表達式。這取決於認證插件作這個匹配的具體實現。
這裏是認證插件必須實現的接口:
public interface AuthenticationProvider { String getScheme(); KeeperException.Code handleAuthentication(ServerCnxn cnxn, byte authData[]); boolean isValid(String id); boolean matches(String id, String aclExpr); boolean isAuthenticated(); }
第一個方法getScheme返回標示這個插件的字符串。由於 咱們直接多種認證的方法,一個認證證書或idspec將老是以scheme爲前綴。ZooKeeper服務端使用經過認證插件返回的scheme來決定scheme應用到那一個id。
handleAuthentication被調用當一個客戶端發送被關聯到這個鏈接的認證信息時。客戶端指定這個信息對應的scheme.ZooKeeper服務端傳遞這個信息給認證插件。插件 getScheme匹配被客戶端傳過來的scheme。handleAuthentication的實現者一般將返回一個錯誤若是它肯定信息是壞的,或者它將把這個信息和這個鏈接關聯起來使用cnxn.getAuthInfo().add(new Id(getScheme(), data))
認證插件被參與到設置和使用ACLs,當一個ACL被設置給一個znode,ZooKeeper服務端將傳遞條目的id部分給 isValid(String id) 方法。它取決於插件來驗證id是不是一個正確的形式。例如,ip:172.16.0.0/16 是一個合法的id,可是ip:host.com不是。若是新ACL包含一個"auth"條目,isAuthenticated 被用來查看是否跟這個鏈接關聯的這個scheme的認證信息應該被加到這個ACL。一些schemes不該該包含在auth。例如,客戶端的IP地址不被認爲應該被做爲id加入到ACL若是auth被指定。
ZooKeeper在檢查ACL的時候調用matches(String id, String aclExpr)。它須要這個客戶端的認證信息和相關的ACL條目匹配。爲了找一個應用這個客戶端了條目,ZooKeeper服務端將找到每個條目的scheme而且若是有這個scheme的認證信息,matches(String id, String aclExpr)將被調用,而後id賦值給先前被handleAuthentication和aclExpr設置ACL條件的id被加入到鏈接的認證信息。認證插件使用它本身的邏輯而且匹配scheme來決定是否id被包含在aclExpr。
這裏有兩個內置的認證插件:ip和digest。其它的插件可使用系統屬性被加入。在啓動時ZooKeeper服務端將尋找以"zookeeper.authProvider."開頭的系統屬性而且解析這些屬性的值爲認證插件的類名。這個屬性可使用 -Dzookeeeper.authProvider.X=com.f.MyAuth或者像下面同樣在服務端的配置文件裏增長一個條目被設置:
authProvider.1=com.f.MyAuth authProvider.2=com.f.MyAuth2
應該注意來保證屬性的後綴是惟一的。若是是重複的例如 -Dzookeeeper.authProvider.X=com.f.MyAuth -Dzookeeper.authProvider.X=com.f.MyAuth2。只有一個將被使用。同時全部的服務端必須有相同的插件定義,不然客戶端使用插件提供的認證方案將會有問題。
ZooKeeper是一個高性能,可擴展的服務。讀和寫操做都被設計得很快。這的緣由是在讀取的狀況下,ZooKeeper能夠服務老的數據,反過來也是由於ZooKeeper的一致性保證:
順序的一致性
從客戶端來的更新將被按照它們發送的順序應用。
原子性
更新要麼成功要麼失敗 -- 沒有部分結果。
單系統鏡像
客戶端將會看到服務端相同的視圖無論它鏈接的是那一個服務端
可靠性
一旦更新被應用,它將會被留存到直到一個客戶端覆蓋這個更新。這個擔保有兩個結果:
時效性
系統的客戶端視圖在必定的時限內(幾十秒的順序)保證是最新的。要麼系統改變將被客戶端看到,要麼客戶端將檢測到服務端過時。
利用這些一致性保證,很是容易構建高級別的功能,例如領導者選舉,屏障,隊列和讀寫可撤銷鎖在ZooKeeper客戶端(對ZooKeeper來講不須要更多)。更詳細的信息請參考Recipes and Solutions。
注意
有時候開發者錯誤地認爲另外一個保證ZooKeeper沒有實現。它就是:
同時一致的跨客戶端視圖
ZooKeeper沒有及時地在每個實例上作保證,兩個不一樣的客戶端將有ZooKeeper數據一致的視圖。由於像網絡延遲之樣的因素,一個客戶端可能在另外一個客戶端獲取這個改變以前執行一個更新。考慮有兩個客戶端A和B的場景。若是客戶端A設置節點/a的值從0到1,而後告訴客戶端B讀取/a,客戶端B可能讀到老的數據0,取決於它鏈接是那一個服務端。若是客戶端A和B讀到相同的值很是重要,客戶端B應該調用sync()方法在它執行讀操做以前。
因此,ZooKeeper自己不保證改變在全部的服務端兩步發生,可是ZooKeeper原語可能被用來構造更高級的功能來提供有用的客戶端同步。(更多信息請參考ZooKeeper Recipes)。
ZooKeeper客戶端庫由兩種語言:Java和C.下面的部分描述這些內容。
ZooKeeper的Java bingding有兩個包:org.apache.zookeeper 和 org.apache.zookeeper.data。組成ZooKeeper的剩下的其它包被用來在內部使用或是服務端實現的一部分。org.apache.zookeeper.data包被生成的classes組成,這些類被簡單地用做容器。
被ZooKeeper Java客戶端使用的最主要類是ZooKeeper類。它的兩個構造器只是在可選的session id和password上不一樣。ZooKeeper支持在整個進程實例中會話恢復。Java程序能夠保存它的session id和password到一個徹底的存儲設備上,重啓,而且恢復這個被先前程序的實現使用的會話。
當一個ZooKeeper對象被建立,兩個線程也被建立:一個IO線程和一個事件線程。全部IO操做都在IO線程(NIO)上發生。全部事件調用發生在事件線程上。例如到ZooKeeper服務端的重連的會話維護和維護心跳是在IO線程上完成。同步方法的回覆也是在IO線程中處理。全部異步方法的回覆和監聽事件在事件線程中處理。這種設計有一些事情須要注意:
最後,與關閉相關的法則是直接的:一旦一個ZooKeeper對象被關閉或者收到一個導致的事件(SESSION_EXPIRED 和 AUTH_FAILED),ZooKeeper對象變得無效。在關閉時,兩個線程關閉而且任何對ZooKeeper的進一步的訪問是未定義的行爲而且應該被避免。
-------------------------------------有關C的內容被忽略---------------------------------------------
本節調查一個開發人員能夠對ZooKeeper服務端執行的全部操做。這相比前面的概念章節是比較低級別的信息,可是比ZooKeeper API手冊高級一些。它包含這些主題:
Java和C客戶端綁定均可能報告錯誤。Java客戶端綁定經過拋出KeeperExecption來報告錯誤,調用異常的code()方法將返回指定的錯誤代碼。C客戶端綁定返回一個ZOO_ERRORS枚舉的錯誤代碼。API回調錶示兩種語言綁定的返回代碼。更多信息請參考API文檔關於可能的錯誤和它們的意思。
如今你瞭解了ZooKeeper。它使你的應用很快,簡單,可是等等。。。有一點問題。這裏有一些ZooKeeper用戶可能要掉進去的陷阱: