一鸟废、ZooKeeper的背景
1.1 認識ZooKeeper
ZooKeeper---譯名為“動物園管理員”。動物園里當然有好多的動物姑荷,游客可以根據(jù)動物園提供的向?qū)D到不同的場館觀賞各種類型的動物盒延,而不是像走在原始叢林里缩擂,心驚膽顫的被動 物所觀賞。為了讓各種不同的動物呆在它們應該呆的地方兰英,而不是相互串門撇叁,或是相互廝殺,就需要動物園管理員按照動物的各種習性加以分類和管理畦贸,這樣我們才能更加放心安全的觀賞動物陨闹。
回到企業(yè)級應用系統(tǒng)中,隨著信息化水平的不斷提高薄坏,企業(yè)級系統(tǒng)變得越來越龐大臃腫趋厉,性能急劇下降,客戶抱怨頻頻胶坠。拆分系統(tǒng)是目前我們可選擇的解決系統(tǒng)可伸縮性和性能問題的唯一行之有效的方法君账。但是拆分系統(tǒng)同時也帶來了系統(tǒng)的復雜性——各子系統(tǒng)不是孤立存在的,它們彼此之間需要協(xié)作和交互沈善,這就是我們常說的分布式系統(tǒng)0乡数。各個子系統(tǒng)就好比動物園里的動物,為了使各個子系統(tǒng)能正常為用戶提供統(tǒng)一的服務闻牡,必須需要一種機制來進行協(xié)調(diào)——這就是ZooKeeper(動物園管理員)净赴。
1.2 為什么使用ZooKeeper
我們知道要寫一個分布式應用是非常困難的,主要原因就是局部故障罩润。一個消息通過網(wǎng)絡在兩個節(jié)點之間傳遞時玖翅,網(wǎng)絡如果發(fā)生故障,發(fā)送方并不知道接收方是否接收到了這個消息割以。他可能在網(wǎng)絡故障遷就收到了此消息金度,也坑沒有收到,又或者可能接收方的進程死了严沥。發(fā)送方了解情況的唯一方法就是再次連接發(fā)送方猜极,并向他進行詢問。這就是局部故障:根本不知道操作是否失敗消玄。因此魔吐,大部分分布式應用需要一個主控、協(xié)調(diào)控制器來管理物理分布的子進程莱找。目前,大部分應用需要開發(fā)私有的協(xié)調(diào)程序嗜桌,缺乏一個通用的機制奥溺。協(xié)調(diào)程序的反復編寫浪費,且難以形成通用骨宠、伸縮性好的協(xié)調(diào)器浮定。協(xié)調(diào)服務非常容易出錯相满,并很難從故障中恢復。例如:協(xié)調(diào)服務很容易處于競態(tài)1甚至死鎖2桦卒。Zookeeper的設(shè)計目的立美,是為了減輕分布式應用程序所承擔的協(xié)調(diào)任務。
Zookeeper并不能阻止局部故障的發(fā)生方灾,因為它們的本質(zhì)是分布式系統(tǒng)建蹄。他當然也不會隱藏局部故障。ZooKeeper的目的就是提供一些工具集裕偿,用來建立安全處理局部故障的分布式應用洞慎。
ZooKeeper是一個分布式小文件系統(tǒng),并且被設(shè)計為高可用性嘿棘。通過選舉算法和集群復制可以避免單點故障3劲腿,由于是文件系統(tǒng),所以即使所有的ZooKeeper節(jié)點全部掛掉鸟妙,數(shù)據(jù)也不會丟失焦人,重啟服務器之后,數(shù)據(jù)即可恢復重父。另外ZooKeeper的節(jié)點更新是原子的花椭,也就是說更新不是成功就是失敗。通過版本號坪郭,ZooKeeper實現(xiàn)了更新的樂觀鎖4个从,當版本號不相符時,則表示待更新的節(jié)點已經(jīng)被其他客戶端提前更新了歪沃,而當前的整個更新操作將全部失敗嗦锐。當然所有的一切ZooKeeper已經(jīng)為開發(fā)者提供了保障,我們需要做的只是調(diào)用API沪曙。與此同時奕污,隨著分布式應用的的不斷深入,需要對集群管理逐步透明化監(jiān)控集群和作業(yè)狀態(tài)液走,可以充分利ZK的獨有特性碳默。
1.3 ZooKeeper的應用
ZooKeeper本質(zhì)上是一個分布式的小文件存儲系統(tǒng)。原本是Apache Hadoop的一個組件缘眶,現(xiàn)在被拆分為一個Hadoop的獨立子項目嘱根,在HBase(Hadoop的另外一個被拆分出來的子項目,用于分布式環(huán)境下的超大數(shù)據(jù)量的DBMS)中也用到了ZooKeeper集群巷懈。
Hadoop该抒,使用Zookeeper的事件處理確保整個集群只有一個NameNode,存儲配置信息等.
HBase顶燕,使用Zookeeper的事件處理確保整個集群只有一個HMaster凑保,察覺HRegionServer聯(lián)機和宕(dàng)機冈爹,存儲訪問控制列表等。
有人會懷疑ZooKeeper的執(zhí)行能力欧引,在ZooKeeper誕生的地方——Yahoo!他被用作雅虎消息代理的協(xié)調(diào)和故障恢復服務频伤。雅虎消息代理是一個高度可擴展的發(fā)布-訂閱系統(tǒng),他管理著成千上萬臺聯(lián)及程序和信息控制系統(tǒng)芝此。它的吞吐量標準已經(jīng)達到大約每秒10000基于寫操作的工作量憋肖。對于讀操作的工作量來說,它的吞吐量標準還要高幾倍癌蓖。
二瞬哼、ZooKeeper的介紹
2.1 ZooKeeper的概述
Zookeeper 是為分布式應用程序提供高性能協(xié)調(diào)服務的工具集合,也是Google的Chubby一個開源的實現(xiàn)租副,是Hadoop 的分布式協(xié)調(diào)服務坐慰。它包含一個簡單的原語集5,分布式應用程序可以基于它實現(xiàn)配置維護用僧、命名服務结胀、分布式同步、組服務等责循。Zookeeper可以用來保證數(shù)據(jù)在ZK集群之間的數(shù)據(jù)的事務性一致6糟港。其中ZooKeeper提供通用的分布式鎖服務7,用以協(xié)調(diào)分布式應用院仿。
Zookeeper作為Hadoop項目中的一個子項目,是 Hadoop集群管理的一個必不可少的模塊,它主要用來解決分布式應用中經(jīng)常遇到的數(shù)據(jù)管理問題秸抚,如集群管理、統(tǒng)一命名服務歹垫、分布式配置管理剥汤、分布式消息隊列、分布式鎖排惨、分布式協(xié)調(diào)等吭敢。在Hadoop中,它管理Hadoop集群中的NameNode暮芭,還有在Hbase中Master Election鹿驼、Server 之間狀態(tài)同狀步等。
Zoopkeeper 提供了一套很好的分布式集群管理的機制辕宏,就是它這種基于層次型的目錄樹的數(shù)據(jù)結(jié)構(gòu)畜晰,并對樹中的節(jié)點進行有效管理,從而可以設(shè)計出多種多樣的分布式的數(shù)據(jù)管理模型瑞筐。
2.2 ZooKeeper的設(shè)計目標
眾所周知舷蟀,分布式環(huán)境下的程序和活動為了達到協(xié)調(diào)一致目的,通常具有某些共同的特點,例如野宜,簡單性、有序性等魔策。ZooKeeper不但在這些目標的實現(xiàn)上有自身特點匈子,并且具有獨特優(yōu)勢。下面我們將簡述ZooKeeper的設(shè)計目標闯袒。
(1)簡單化
ZooKeeper允許各分布式進程通過一個共享的命名空間相互聯(lián)系虎敦,該命名空間類似于一個標準的層次型的文件系統(tǒng):由若干注冊了的數(shù)據(jù)節(jié)點構(gòu)成(用Zookeeper的術(shù)語叫znode),這些節(jié)點類似于文件和目錄政敢。典型的文件系統(tǒng)是基于存儲設(shè)備的其徙,文傳統(tǒng)的文件系統(tǒng)主要用于存儲功能,然而ZooKepper的數(shù)據(jù)是保存在內(nèi)存中的喷户。也就是說唾那,可以獲得高吞吐和低延遲。ZooKeeper的實現(xiàn)非常重視高性能褪尝、高可靠闹获,以及嚴格的有序訪問。
高性能保證了ZooKeeper可以用于大型的分布式系統(tǒng)河哑,高可靠保證了ZooKeeper不會發(fā)生單點故障避诽,嚴格的順序訪問保證了客戶端可以獲得復雜的同步操作原語。
(2)健壯性
就像ZooKeeper需要協(xié)調(diào)的分布式系統(tǒng)一樣璃谨,它本身就是具有冗余結(jié)構(gòu)沙庐,它構(gòu)建在一系列主機之上,叫做一個”ensemble”佳吞。
構(gòu)成ZooKeeper服務的各服務器之間必須相互知道拱雏,它們維護著一個狀態(tài)信息的內(nèi)存映像8,以及在持久化存儲中維護著事務日志和快照9容达。只要大部分服務器正常工作古涧,ZooKeeper服務就能正常工作。
客戶端連接到一臺ZooKeeper服務器花盐∠刍客戶端維護這個TCP連接,通過這個連接算芯,客戶端可以發(fā)送請求柒昏、得到應答,得到監(jiān)視事件以及發(fā)送心跳熙揍。如果這個連接斷了职祷,客戶端可以連接到另一個ZooKeeper服務器。
(3)有序性
ZooKeeper給每次更新附加一個數(shù)字標簽,表明ZooKeeper中的事務順序有梆,后續(xù)操作可以利用這個順序來完成更高層次的抽象功能是尖,例如同步原語7。
(4)速度優(yōu)勢
ZooKeeper特別適合于以讀為主要負荷的場合泥耀。ZooKeeper可以運行在數(shù)千臺機器上饺汹,如果大部分操作為讀,例如讀寫比例為10:1痰催,ZooKeeper的效率會很高兜辞。
2.3 ZooKeeper的集群
ZK集群如下圖2.1所示。這是實際應用的一個場景夸溶,該ZooKeeper集群當中一共有5臺服務器逸吵,有兩種角色Leader和Follwer,5臺服務器連通在一起缝裁,客戶端有分別連在不同的ZK服務器上扫皱。如果當數(shù)據(jù)通過客戶端1,在左邊第一臺Follower服務器上做了一次數(shù)據(jù)變更压语,他會把這個數(shù)據(jù)的變化同步到其他所有的服務器啸罢,同步結(jié)束之后,那么其他的客戶端都會獲得這個數(shù)據(jù)的變化胎食。
注意:
通常Zookeeper由2n+1臺servers組成扰才,每個server都知道彼此的存在。每個server都維護的內(nèi)存狀態(tài)鏡像以及持久化存儲的事務日志和快照厕怜。為了保證Leader選舉能過得到多數(shù)的支持衩匣,所以ZooKeeper集群的數(shù)量一般為奇數(shù)。對于2n+1臺server粥航,只要有n+1臺(大多數(shù))server可用琅捏,整個系統(tǒng)保持可用。
2.3.1 集群中的角色
在ZooKeeper集群當中递雀,集群中的服務器角色有兩種Leader和Learner柄延,Learner角色又分為Observer和Follower,具體功能如下:
1.領(lǐng)導者(leader)缀程,負責進行投票的發(fā)起和決議搜吧,更新系統(tǒng)狀態(tài)
2.學習者(learner),包括跟隨者(follower)和觀察者(observer)杨凑,
3.follower用于接受客戶端請求并向客戶端返回結(jié)果滤奈,在選主過程中參與投票
4.Observer可以接受客戶端請求,將寫請求轉(zhuǎn)發(fā)給leader撩满,但observer不參加投票過程蜒程,只同步leader的狀態(tài)绅你,observer的目的是為了擴展系統(tǒng),提高讀取速度昭躺。
- 客戶端(client)忌锯,請求發(fā)起方
ZooKeeper的組件圖中給出了ZooKeeper服務的高層次的組件。除了請求處理器(requestprocessor)外领炫,構(gòu)成ZooKeeper服務的每個服務器都有一個備份汉规。復制的數(shù)據(jù)庫(replicateddatabase)是一個內(nèi)存數(shù)據(jù)庫,包含整個數(shù)據(jù)樹驹吮。為了可恢復,更新會被log到磁盤晶伦,并且在更新這個內(nèi)存數(shù)據(jù)庫之前碟狞,先序列化到磁盤。
每個ZooKeeper都為客戶端提供服務婚陪∽逦郑客戶端只連接到一個服務器,并提交請求泌参。讀請求直接由本地的復制數(shù)據(jù)庫提供數(shù)據(jù)脆淹。對服務狀態(tài)進行修改的請求、寫請求通過一個約定的協(xié)議進行通訊沽一。
作為這個協(xié)議的一部分盖溺,所有的寫請求都被傳送到一個叫“首領(lǐng)(leader)”的服務器,而其他的服務器铣缠,叫做“(隨從)followers”烘嘱,follower從leader接收信息修改的提議,并同意進行蝗蛙。當leader發(fā)生故障時蝇庭,協(xié)議的信息層(messaginglayer)關(guān)注leader的替換,并同步到所有的follower捡硅。
ZooKeeper采用一個自定義的信息原子操作協(xié)議哮内,由于信息層的操作是原子性的,ZooKeeper能保證本地的復制數(shù)據(jù)庫不會產(chǎn)生不一致壮韭。當leader接收到一個寫請求北发,它計算出寫之后系統(tǒng)的狀態(tài),把它變成一個事務泰涂。
2.3.2 Zookeeper的讀寫機制和保證及特點
(1)ZooKeeper的讀寫機制
Zookeeper是一個由多個server組成的集群
一個leader鲫竞,多個follower
每個server保存一份數(shù)據(jù)副本
全局數(shù)據(jù)一致
分布式讀寫
更新請求轉(zhuǎn)發(fā),由leader實施
(2)ZooKeeper的保證
ZooKeeper運行非潮泼桑快而且簡單从绘。雖然它的目標是構(gòu)建更加復雜服務(例如同步)的基礎(chǔ),但它提供了一些保證,如下:
1.順序一致性:來自于客戶端的更新僵井,根據(jù)發(fā)送的先后被順序?qū)嵤?/p>
2.唯一的系統(tǒng)映像:盡管客戶端連接到不同的服務器陕截,但它們看到的一個唯一(一致性)的系統(tǒng)服務,client無論連接到哪個server批什,數(shù)據(jù)視圖都是一致的农曲。
3.可靠性:一旦實施了一個更新,就會一直保持那種狀態(tài)驻债,直到客戶端再次更新它乳规,同時數(shù)據(jù)更新原子性,一次數(shù)據(jù)更新要么成功合呐,要么失敗暮的。
4.及時性:在一個確定的時間內(nèi),客戶端看到的系統(tǒng)狀態(tài)是最新的淌实。
(3)ZooKeeper特點
最終一致性:client不論連接到哪個Server冻辩,展示給它都是同一個視圖,這是zookeeper最重要的性能拆祈。
可靠性:具有簡單恨闪、健壯、良好的性能放坏,如果消息m被一臺服務器接受咙咽,那么它將被所有的服務器接受。
實時性:Zookeeper保證客戶端將在一個時間間隔范圍內(nèi)獲得服務器的更新信息轻姿,或者服務器失效的信息犁珠。 但由于網(wǎng)絡延時等原因,Zookeeper不能保證兩個客戶端能同時得到剛更新的數(shù)據(jù)互亮,如果需要最新數(shù)據(jù)犁享,應該在讀數(shù)據(jù)之前調(diào)用sync()接口。
等待無關(guān)(wait-free):慢的或者失效的client豹休,不得干預快速的client的請求炊昆,使得每個client都能有效的等待。
原子性:更新只能成功或者失敗威根,沒有中間狀態(tài)凤巨。
順序性:包括全局有序和偏序兩種:
全局有序:是指如果在一臺服務器上消息a在消息b前發(fā)布,則在所有Server上消息a都將在消息b前被發(fā)布洛搀;
偏序:是指如果一個消息b在消息a后被同一個發(fā)送者發(fā)布敢茁,a必將排在b前面
三、ZooKeeper服務
3.1 ZooKeeper數(shù)據(jù)模型
ZooKeeper擁有一個層次的命名空間留美,這個和分布式的文件系統(tǒng)非常相似彰檬。不同的是ZooKeeper命名空間中的Znode伸刃,兼具文件和目錄兩種特點。既像文件一樣維護著數(shù)據(jù)逢倍、元信息捧颅、ACL、時間戳等數(shù)據(jù)結(jié)構(gòu)较雕,又像目錄一樣可以作為路徑標識的一部分碉哑,并可以具有子znode。用戶對znode具有增亮蒋、刪扣典、改、查等操作(權(quán)限允許的情況下)慎玖。
znode具有原子性操作激捏,每個znode的數(shù)據(jù)將被原子性地讀寫,讀操作會讀取與znode相關(guān)的所有數(shù)據(jù)凄吏,寫操作會一次性替換所有數(shù)據(jù)。zookeeper并沒有被設(shè)計為常規(guī)的數(shù)據(jù)庫或者大數(shù)據(jù)存儲闰蛔,相反的是痕钢,它用來管理調(diào)度數(shù)據(jù),比如分布式應用中的配置文件信息序六、狀態(tài)信息任连、匯集位置等等。這些數(shù)據(jù)的共同特性就是它們都是很小的數(shù)據(jù)例诀,通常以KB為大小單位随抠。zooKeeper的服務器和客戶端都被設(shè)計為嚴格檢查并限制每個znode的數(shù)據(jù)大小至多1M,當時常規(guī)使用中應該遠小于此值繁涂。
Zonde由路徑標注拱她,ZooKeeper中被表示成有反斜杠分割的Unicode字符串,如同Unix中的文件路徑扔罪。路徑必須是絕對的秉沼,因此他們必須由反斜杠來字符開頭。除此以外矿酵,他們必須是唯一的唬复,也就是說每一個路徑只有一個表示,因此這些路徑不能改變全肮。ZooKeeper的數(shù)據(jù)結(jié)構(gòu), 與普通的文件系統(tǒng)極為類似. 見下圖:
圖中的每個節(jié)點稱為一個znode. 每個znode由3部分組成:
1.stat:此為狀態(tài)信息, 描述該znode的版本, 權(quán)限等信息.
2.data:與該znode關(guān)聯(lián)的數(shù)據(jù).
3.children:該znode下的子節(jié)點.
3.1.1 ZooKeeper節(jié)點Znode
ZooKeeper目錄樹中每一個節(jié)點對應一個Znode敞咧。每個Znode維護著一個屬性結(jié)構(gòu),它包含著版本號(dataVersion)辜腺,時間戳(ctime,mtime)等狀態(tài)信息休建。ZooKeeper正是使用節(jié)點的這些特性來實現(xiàn)它的某些特定功能乍恐。每當Znode的數(shù)據(jù)改變時,他相應的版本號將會增加丰包。每當客戶端檢索數(shù)據(jù)時禁熏,它將同時檢索數(shù)據(jù)的版本號。并且如果一個客戶端執(zhí)行了某個節(jié)點的更新或刪除操作邑彪,他也必須提供要被操作的數(shù)據(jù)版本號瞧毙。如果所提供的數(shù)據(jù)版本號與實際不匹配,那么這個操作將會失敗寄症。
Znode是客戶端訪問ZooKeeper的主要實體宙彪,它包含以下幾個特征:
(1)Watches
客戶端可以在節(jié)點上設(shè)置watch(我們稱之為監(jiān)視器)。當節(jié)點狀態(tài)發(fā)生改變時(數(shù)據(jù)的增有巧、刪释漆、改)將會觸發(fā)watch所對應的操作。當watch被觸發(fā)時篮迎,ZooKeeper將會向客戶端發(fā)送且僅發(fā)送一條通知男图,因為watch只能被觸發(fā)一次。
(2)數(shù)據(jù)訪問
ZooKeeper中的每個節(jié)點存儲的數(shù)據(jù)要被原子性的操作甜橱。也就是說讀操作將獲取與節(jié)點相關(guān)的所有數(shù)據(jù)逊笆,寫操作也將替換掉節(jié)點的所有數(shù)據(jù)。另外岂傲,每一個節(jié)點都擁有自己的ACL(訪問控制列表)难裆,這個列表規(guī)定了用戶的權(quán)限,即限定了特定用戶對目標節(jié)點可以執(zhí)行的操作镊掖。
(3)節(jié)點類型
ZooKeeper中的節(jié)點有兩種乃戈,分別為臨時節(jié)點和永久節(jié)點。節(jié)點的類型在創(chuàng)建時即被確定亩进,并且不能改變症虑。
ZooKeeper的臨時節(jié)點:該節(jié)點的生命周期依賴于創(chuàng)建它們的會話。一旦會話結(jié)束归薛,臨時節(jié)點將被自動刪除侦讨,當然可以也可以手動刪除。另外苟翻,需要注意是韵卤,ZooKeeper的臨時節(jié)點不允許擁有子節(jié)點。
ZooKeeper的永久節(jié)點:該節(jié)點的生命周期不依賴于會話崇猫,并且只有在客戶端顯示執(zhí)行刪除操作的時候沈条,他們才能被刪除。
(4)順序節(jié)點(唯一性的保證)
當創(chuàng)建Znode的時候诅炉,用戶可以請求在ZooKeeper的路徑結(jié)尾添加一個遞增的計數(shù)蜡歹。這個計數(shù)對于此節(jié)點的父節(jié)點來說是唯一的屋厘,它的格式為“%10d”(10位數(shù)字,沒有數(shù)值的數(shù)位用0補充月而,例如“0000000001”)汗洒。當計數(shù)值大于232-1時,計數(shù)器將溢出父款。
org.apache.zookeeper.CreateMode
中定義了四種節(jié)點類型溢谤,分別對應:
PERSISTENT:永久節(jié)點
EPHEMERAL:臨時節(jié)點
PERSISTENT_SEQUENTIAL:永久節(jié)點鸿秆、序列化
EPHEMERAL_SEQUENTIAL:臨時節(jié)點禀苦、序列化
3.1.2 ZooKeeper中的時間
ZooKeeper有多種記錄時間的形式,其中包含以下幾個主要屬性:
(1)Zxid
致使ZooKeeper節(jié)點狀態(tài)改變的每一個操作都將使節(jié)點接收到一個zxid格式的時間戳乳幸,并且這個時間戳全局有序肝集。也就是說瞻坝,也就是說,每個對節(jié)點的改變都將產(chǎn)生一個唯一的zxid杏瞻。如果zxid1的值小于zxid2的值所刀,那么zxid1所對應的事件發(fā)生在zxid2所對應的事件之前。實際上捞挥,ZooKeeper的每個節(jié)點維護者三個zxid值勉痴,為別為:cZxid、mZxid树肃、pZxid。
cZxid: 是節(jié)點的創(chuàng)建時間所對應的Zxid格式時間戳瀑罗。
mZxid:是節(jié)點的修改時間所對應的Zxid格式時間戳胸嘴。
實現(xiàn)中zxid是一個64為的數(shù)字,它高32位是epoch用來標識leader關(guān)系是否改變斩祭,每次一個leader被選出來劣像,它都會有一個 新的epoch。低32位是個遞增計數(shù)摧玫。
(2)版本號
對節(jié)點的每一個操作都將致使這個節(jié)點的版本號增加耳奕。每個節(jié)點維護著三個版本號,他們分別為:
version 節(jié)點數(shù)據(jù)版本號
cversion 子節(jié)點版本號
aversion 節(jié)點所擁有的ACL版本號
3.1.3 節(jié)點的屬性結(jié)構(gòu)
通過前面的介紹诬像,我們可以了解到屋群,一個節(jié)點自身擁有表示其狀態(tài)的許多重要屬性,如下圖所示坏挠。
3.1.4 Zonde總結(jié)
(1)znode中的數(shù)據(jù)可以有多個版本芍躏,在查詢該znode數(shù)據(jù)時就需要帶上版本信息。如:set path version / delete path version
(2)znode可以是臨時znode降狠,由create -e 生成的節(jié)點对竣,一旦創(chuàng)建這個znode的client與server斷開連接庇楞,該znode將被自動刪除。
client和server之間通過heartbeat來確認連接正常否纬,這種狀態(tài)稱之為session吕晌,斷開連接后session失效。
(3)臨時znode不能有子znode临燃。
(4)znode可以自動編號睛驳,由create -s 生成的節(jié)點,例如在 create -s /app/node 已存在時谬俄,將會生成 /app/node00***001節(jié)點柏靶。
(5)znode可以被監(jiān)控,該目錄下某些信息的修改溃论,例如節(jié)點數(shù)據(jù)屎蜓、子節(jié)點變化等,可以主動通知監(jiān)控注冊的client钥勋。事實上炬转,通過這個特性,可以完成許多重要應用算灸,例如配置管理扼劈、信息同步、分布式鎖等等菲驴。
3.2 ZooKeeper服務中的操作
在ZooKeeper中有9個基本操作荐吵,如下圖所示:
更新ZooKeeper操作是有限制的。delete或setData必須明確要更新的Znode的版本號赊瞬,我們可以調(diào)用exists找到先煎。如果版本號不匹配,更新將會失敗巧涧。
更新ZooKeeper操作是非阻塞式的薯蝎。因此客戶端如果失去了一個更新(由于另一個進程在同時更新這個Znode),他可以在不阻塞其他進程執(zhí)行的情況下谤绳,選擇重新嘗試或進行其他操作占锯。
盡管ZooKeeper可以被看做是一個文件系統(tǒng),但是處于便利缩筛,摒棄了一些文件系統(tǒng)地操作原語消略。因為文件非常的小并且使整體讀寫的,所以不需要打開瞎抛、關(guān)閉或是尋地的操作疑俭。
3.2.1 watch觸發(fā)器
讀操作exists、getChildren和getData都被設(shè)置了watch,并且這些watch都由寫操作來觸發(fā):create钞艇、delete和setData啄寡。ACL操作并不參與到watch中。當watch被觸發(fā)時哩照,watch事件被生成挺物,他的類型由watch和觸發(fā)他的操作共同決定。ZooKeeper所管理的watch可以分為兩類:
1.數(shù)據(jù)watch(data watches):getData和exists負責設(shè)置數(shù)據(jù)watch飘弧;
2.孩子watch(child watches):getChildren負責設(shè)置孩子watch识藤;
我們可以通過操作返回的數(shù)據(jù)來設(shè)置不同的watch:
1.getData和exists:返回關(guān)于節(jié)點的數(shù)據(jù)信息
2.getChildren:返回孩子列表
因此,一個成功的setData操作將觸發(fā)Znode的數(shù)據(jù)watch次伶。
一個成功的create操作將觸發(fā)Znode的數(shù)據(jù)watch以及孩子watch痴昧。
一個成功的delete操作將觸發(fā)Znode的數(shù)據(jù)watch以及孩子watch。
watch由客戶端所連接的ZooKeeper服務器在本地維護冠王,因此watch可以非常容易地設(shè)置赶撰、管理和分派。當客戶端連接到一個新的服務器上時柱彻,任何的會話事件都將可能觸發(fā)watch豪娜。另外,當從服務器斷開連接的時候哟楷,watch將不會被接收瘤载。但是,當一個客戶端重新建立連接的時候卖擅,任何先前注冊過的watch都會被重新注冊鸣奔。
exists操作上的watch,在被監(jiān)視的Znode創(chuàng)建惩阶、刪除或數(shù)據(jù)更新時被觸發(fā)挎狸。
getData操作上的watch,在被監(jiān)視的Znode刪除或數(shù)據(jù)更新時被觸發(fā)琳猫。在被創(chuàng)建時不能被觸發(fā),因為只有Znode一定存在私痹,getData操作才會成功脐嫂。
getChildren操作上的watch,在被監(jiān)視的Znode的子節(jié)點創(chuàng)建或刪除紊遵,或是這個Znode自身被刪除時被觸發(fā)账千。可以通過查看watch事件類型來區(qū)分是Znode還是他的子節(jié)點被刪除:NodeDelete表示Znode被刪除暗膜,NodeDeletedChanged表示子節(jié)點被刪除匀奏。
watch設(shè)置操作及相應的觸發(fā)器如圖下圖所示:
watch事件包括了事件所涉及的Znode的路徑,因此對于NodeCreated和NodeDeleted事件來說学搜,根據(jù)路徑就可以簡單區(qū)分出是哪個Znode被創(chuàng)建或是被刪除了娃善。為了查詢在NodeChildrenChanged事件后哪個子節(jié)點被改變了论衍,需要再次調(diào)用getChildren來獲得新的children列表。同樣的聚磺,為了查詢NodeDeletedChanged事件后產(chǎn)生的新數(shù)據(jù)坯台,需要調(diào)用getData。在兩種情況下瘫寝,Znode可能在獲取watch事件或執(zhí)行讀操作這兩種狀態(tài)下切換蜒蕾,在寫應用程序時,必須記住這一點焕阿。
(1)Zookeeper的watch實際上要處理兩類事件:
- 連接狀態(tài)事件(type=None, path=null)
這類事件不需要注冊咪啡,也不需要我們連續(xù)觸發(fā),我們只要處理就行了暮屡。
- 節(jié)點事件
節(jié)點的建立撤摸,刪除,數(shù)據(jù)的修改栽惶。它是one time trigger愁溜,我們需要不停的注冊觸發(fā),還可能發(fā)生事件丟失的情況外厂。
上面2類事件都在Watch中處理冕象,也就是重載的process(Event event)
(2)節(jié)點事件的觸發(fā),通過函數(shù)exists,getData或getChildren來處理
這類函數(shù)汁蝶,有雙重作用:
注冊觸發(fā)事件
函數(shù)本身的功能
函數(shù)的本身的功能又可以用異步的回調(diào)函數(shù)來實現(xiàn),重載processResult()過程中處理函數(shù)本身的的功能渐扮。
函數(shù)還可以指定自己的watch,所以每個函數(shù)都有4個版本。根據(jù)自己的需要來選擇不同的函數(shù)掖棉,不同的版本墓律。
3.3 ZooKeeper訪問控制列表ACL
ZooKeeper使用ACL來對Znode進行訪問控制。ACL的實現(xiàn)和Unix文件訪問許可非常相似:它使用許可位來對一個節(jié)點的不同操作進行允許或禁止的權(quán)限控制幔亥。但是耻讽,和標準的Unix許可不同的是,Zookeeper對于用戶類別的區(qū)分帕棉,不止局限于所有者(owner)针肥、組 (group)、所有人(world)三個級別香伴。Zookeeper中慰枕,數(shù)據(jù)節(jié)點沒有“所有者”的概念。訪問者利用id標識自己的身份即纲,并獲得與之相應的 不同的訪問權(quán)限具帮。
注意:
傳統(tǒng)的文件系統(tǒng)中,ACL分為兩個維度,一個是屬組蜂厅,一個是權(quán)限匪凡,子目錄/文件默認繼承父目錄的ACL。而在Zookeeper中一個ACL和一個ZooKeeper節(jié)點相對應葛峻。并且锹雏,父節(jié)點的ACL與子節(jié)點的ACL是相互獨立的。也就是說术奖,ACL不能被子節(jié)點所繼承礁遵,父節(jié)點所擁有的權(quán)限與子節(jié)點所用的權(quán)限都沒有任何關(guān)系。
Zookeeper支持可配置的認證機制采记。它利用一個三元組來定義客戶端的訪問權(quán)限:(scheme:expression, perms) 佣耐。其中:
1.scheme:定義了expression的含義。
如:(host:host1.corp.com唧龄,READ),標識了一個名為host1.corp.com的主機,有該數(shù)據(jù)節(jié)點的讀權(quán)限兼砖。
2.Perms:標識了操作權(quán)限。
如:(ip:19.22.0.0/16, READ),表示IP地址以19.22開頭的主機,有該數(shù)據(jù)節(jié)點的讀權(quán)限既棺。
Zookeeper的ACL也可以從三個維度來理解:一是讽挟,scheme; 二是,user; 三是丸冕,permission耽梅,通常表示為scheme:id:permissions,如下圖所示胖烛。
1.world : id格式:anyone眼姐。
如:world:anyone代表任何人,zookeeper中對所有人有權(quán)限的結(jié)點就是屬于world:anyone的佩番。
2.auth : 它不需要id众旗。
注:只要是通過authentication的user都有權(quán)限,zookeeper支持通過kerberos來進行認證, 也支持username/password形式的認證趟畏。
3.digest: id格式:username:BASE64(SHA1(password))贡歧。
它需要先通過username:password形式的authentication。
4.ip: id格式:客戶機的IP地址赋秀。
設(shè)置的時候可以設(shè)置一個ip段利朵。如:ip:192.168.1.0/16, 表示匹配前16個bit的IP段
5.super: 超級用戶模式。
在這種scheme情況下沃琅,對應的id擁有超級權(quán)限哗咆,可以做任何事情
ZooKeeper權(quán)限定義如下圖所示:
ZooKeeper內(nèi)置的ACL模式如下圖所示:
當會話建立的時候蜘欲,客戶端將會進行自我驗證益眉。另外,ZooKeeper Java API支持三種標準的用戶權(quán)限,它們分別為:
1.ZOO_PEN_ACL_UNSAFE:對于所有的ACL來說都是完全開放的郭脂,任何應用程序可以在節(jié)點上執(zhí)行任何操作年碘,比如創(chuàng)建、列出并刪除子節(jié)點展鸡。
2.ZOO_READ_ACL_UNSAFE:對于任意的應用程序來說屿衅,僅僅具有讀權(quán)限。
3.ZOO_CREATOR_ALL_ACL:授予節(jié)點創(chuàng)建者所有權(quán)限莹弊。需要注意的是涤久,設(shè)置此權(quán)限之前,創(chuàng)建者必須已經(jīng)通了服務器的認證忍弛。
下面演示一個通過digest(用戶名密碼的方式)為創(chuàng)建的節(jié)點設(shè)置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细疚,所有用戶可讀權(quán)限
Id id2 = new Id("world", "anyone");
ACL acl2 = new ACL(ZooDefs.Perms.READ, id2);
acls.add(acl2);
// zk用admin認證蔗彤,創(chuàng)建/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的執(zhí)行
ZooKeeper服務可以以兩種模式運行疯兼。在單機模式下然遏,只有一個ZooKeeper服務器,便于用來測試吧彪。但是他沒有高可用性和恢復性的保障待侵。在工業(yè)界,ZooKeeper以復合模式10運行在一組叫ensemble的集群上来氧。ZooKeeper通過復制來獲得高可用性诫给,同時,只要ensemble中大部分機器運作啦扬,就可以提供服務中狂。在2n+1個節(jié)點的ensemble中,可以承受n臺機器故障扑毡。
ZooKeeper的思想非常簡單:他所需要做的就是保證對Znode樹的每一次修改都復制到ensemble中的大部分機器上去胃榕。如果機器中的小部分出故障了,那么至少有一臺機器將會恢復到最新狀態(tài)瞄摊,其他的則保存這副本勋又,直到最終達到最新狀態(tài)。Zookeeper采用Zab協(xié)議换帜,它分為兩個階段楔壤,并且可能被無限的重復。
(1)階段1:領(lǐng)導者選舉
在ensemble中的機器要參與一個選擇特殊成員的進程惯驼,這個成員叫領(lǐng)導者蹲嚣,其他機器腳跟隨者递瑰。在大部分的跟隨者與他們的領(lǐng)導者同步了狀態(tài)以后,這個階段才算完成隙畜。
(2)階段2:原子廣播
所有的寫操作請求被傳送給領(lǐng)導者抖部,并通過廣播將更新信息告訴跟隨者。當大部分跟隨者執(zhí)行了修改之后议惰,領(lǐng)導者就提交更新操作慎颗,客戶端將得到更新成功的回應。未獲得一致性的協(xié)議被設(shè)計為原子的言询,因此無論修改失敗與否俯萎,他都分兩階段提交。
如果領(lǐng)導者出故障了运杭,城下的機器將會再次進行領(lǐng)導者選舉讯屈,并在新領(lǐng)導被選出前繼續(xù)執(zhí)行任務。如果在不久后老的領(lǐng)導者恢復了县习,那么它將以跟隨者的身份繼續(xù)運行涮母。領(lǐng)導者選舉非常快躁愿,由發(fā)布的結(jié)果所知叛本,大約是200毫秒,因此在選舉是性能不會明顯減慢彤钟。所有在ensemble中的機器在更新它們內(nèi)存中的Znode樹之前會先將更新信息寫入磁盤来候。讀操作請求可由任何機器服務,同時逸雹,由于他們只涉及內(nèi)存查找营搅,因此非常快梆砸。
3.5 ZooKeeper一致性
在ensemble中的領(lǐng)導者和跟隨著非常靈活转质,跟隨者通過更新號來滯后領(lǐng)導者11,結(jié)果導致了只要大部分而不是所有的ensemble中的元素確認更新帖世,就能被提交了休蟹。對于ZooKeeper來說,一個較好的智能模式是將客戶端連接到跟著領(lǐng)導者的ZooKeeper服務器上日矫÷腹客戶端可能被連接到領(lǐng)導者上,但他不能控制它哪轿,而且在如下情況時盈魁,甚至可能不知道。參見下圖:
每一個Znode樹的更新都會給定一個唯一的全局標識窃诉,叫zxid(表示ZooKeeper事務“ID”)杨耙。更新是被排序的姓惑,因此如果zxid的z1小于z2,那么z1就比z2先執(zhí)行按脚。對于ZooKeeper來說,這是分布式系統(tǒng)中排序的唯一標準敦冬。
ZooKeeper是一種高性能辅搬、可擴展的服務。ZooKeeper的讀寫速度非巢焙担快堪遂,并且讀的速度要比寫快。另外萌庆,在進行讀操作的時候溶褪,ZooKeeper依然能夠為舊的數(shù)據(jù)提供服務。這些都是由ZooKeeper所提供的一致性保證的践险,它具有如下特點:
(1)順序一致性
任何一個客戶端的更新都按他們發(fā)送的順序排序猿妈,也就意味著如果一個客戶端將Znode z的值更新為值a,那么在之后的操作中巍虫,他會將z更新為b彭则,在客戶端發(fā)現(xiàn)z帶有值b之后,就不會再看見帶有值a的z占遥。
(2)原子性
更新不成功就失敗俯抖,這意味著如果更新失敗了,沒有客戶端會知道瓦胎》移迹☆☆
(3)單系統(tǒng)映像☆
無論客戶端連接的是哪臺服務器,他與系統(tǒng)看見的視圖一樣搔啊。這就意味著柬祠,如果一個客戶端在相同的會話時連接了一臺新的服務器,他將不會再看見比在之前服務器上看見的更老的系統(tǒng)狀態(tài)负芋,當服務器系統(tǒng)出故障瓶盛,同時客戶端嘗試連接ensemble中的其他機器時,故障服務器的后面那臺機器將不會接受連接示罗,直到它連接到故障服務器惩猫。
(4)容錯性☆☆☆
一旦更新成功后,那么在客戶端再次更新他之前蚜点,他就固定了轧房,將不再被修改,這就會保證產(chǎn)生下面兩種結(jié)果:
如果客戶端成功的獲得了正確的返回代碼绍绘,那么說明更新已經(jīng)成功奶镶。如果不能夠獲得返回代碼(由于通信錯誤迟赃、超時等原因),那么客戶端將不知道更新是否生效厂镇。
當故障恢復的時候纤壁,任何客戶端能夠看到的執(zhí)行成功的更新操作將不會回滾。
(5)實時性☆☆
在任何客戶端的系統(tǒng)視圖上的的時間間隔是有限的捺信,因此他在超過幾十秒的時間內(nèi)部會過期酌媒。這就意味著,服務器不會讓客戶端看一些過時的數(shù)據(jù)迄靠,而是關(guān)閉秒咨,強制客戶端轉(zhuǎn)到一個更新的服務器上。
解釋一下:
由于性能原因掌挚,讀操作由ZooKeeper服務器的內(nèi)存提供雨席,而且不參與寫操作的全局排序。這一特性可能會導致來自使用ZooKeeper外部機制交流的客戶端與ZooKeeper狀態(tài)的不一致吠式。舉例來說陡厘,客戶端A將Znode z的值a更新為a',A讓B來讀z特占,B讀到z的值是a而不是a’雏亚。這與ZooKeeper的保證機制是相容的(不允許的情況較作“同步一致的交叉客戶端視 圖”)。為了避免這種情況的發(fā)生摩钙,B在讀取z的值之前罢低,應該先調(diào)用z上的sync。Sync操作強制B連接上的ZooKeeper服務器與leader保 持一致這樣胖笛,當B讀到z的值時网持,他將成為A設(shè)置的值(或是之后的值)
容易混淆的是:
sync操作只能被異步調(diào)用12。這樣操作的原因是你不需要等待他的返回长踊,因為ZooKeeper保證了任何接下去的操作將會發(fā)生在sync在服務器上執(zhí)行以后功舀,即使操作是在sync完成前被調(diào)用的。
這些已執(zhí)行的保證后身弊,ZooKeeper更高級功能的設(shè)計與實現(xiàn)將會變得非常容易辟汰,例如:leader選舉、隊列阱佛,以及可撤銷鎖等機制的實現(xiàn)帖汞。
3.6 ZooKeeper會話
ZooKeeper客戶端與ensemble中的服務器列表配置一致,在啟動時凑术,他嘗試與表中的一個服務器相連接翩蘸。如果連接失敗了,他就嘗試表中的其他服務器淮逊,以此類推催首,知道他最終連接到其中一個扶踊,或者ZooKeeper的所有服務器都無法獲得時,連接失敗郎任。
一旦與ZooKeeper服務器連接成功秧耗,服務器會創(chuàng)建與客戶端的一個新的對話。每個回話都有超時時段舶治,這是應用程序在創(chuàng)建它時設(shè)定的分井。如果服務器沒有在超時時段內(nèi)得到請求,他可能會中斷這個會話歼疮。一旦會話被中斷了,他可能不再被打開诈唬,而且任何與會話相連接的臨時節(jié)點都將丟失韩脏。
無論什么時候會話持續(xù)空閑長達一定時間,都會由客戶端發(fā)送ping請求保持活躍(猶如心跳)铸磅。時間段要足夠小以監(jiān)測服務器故障(由讀操作超時反應)赡矢,并且能再回話超市時間段內(nèi)重新連接到另一個服務器。
在ZooKeeper中有幾個time參數(shù)阅仔。tick time是ZooKeeper中的基本時間長度吹散,為ensemble里的服務器所使用,用來定義對于交互運行的調(diào)度八酒。其他設(shè)置以tick time的名義定義空民,或者至少由它來約束。
創(chuàng)建更復雜的臨時性狀態(tài)的應用程序應該支持更長的會話超時羞迷,因為重新構(gòu)建的代價會更昂貴界轩。在一些情況下,我們可以讓應用程序在一定會話時間內(nèi)能夠重啟衔瓮,并且避免會話過期浊猾。(這可能更適合執(zhí)行維護或是升級)每個會話都由服務器給定一個唯一的身份和密碼,而且如果是在建立連接時被傳遞給ZooKeeper的話热鞍,只要沒有過期它能夠恢復會話葫慎。
這些特性可以視為一種可以避免會話過期的優(yōu)化,但它并不能代替用來處理會話過期薇宠。會話過期可能出現(xiàn)在機器突然故障時偷办,或是由于任何原因?qū)е碌膽贸绦虬踩P(guān)閉了,但在會話中斷前沒有重啟澄港。
3.7 ZooKeeper實例狀態(tài)
Zookeeper對象的轉(zhuǎn)變是通過其生命周期中的不同狀態(tài)來實現(xiàn)爽篷。可以使用getState()方法在任何時候去查詢他的狀態(tài):
public states getState()
Zookeeper狀態(tài)事務慢睡,如圖3.5所示
圖 3.5 Zookeeper狀態(tài)事務
getState()方法的返回類型是states逐工,states是枚舉類型代表Zookeeper對象可能所處的不同狀態(tài)铡溪,一個Zookeeper實例可能一次只處于一個狀態(tài)。一個新建的Zookeeper實例正在于Zookeeper服務器建立連接時泪喊,是處于CONNECTING狀態(tài)的棕硫。一旦連接建立好以后,他就變成了Connected狀態(tài)袒啼。
使用Zookeeper的客戶端可以通過注冊Watcher的方法來獲取狀態(tài)轉(zhuǎn)變的消息哈扮。一旦進入了CONNNECTED狀態(tài),Watcher將獲得一個KeepState值為SyncConnected的WatchedEvent蚓再。
注意Zookeeper的watcher有兩個職責:
<1>了解Zookeeper的狀態(tài)改變滑肉。傳遞給ZooKeeper對象構(gòu)造函數(shù)的(默認)watcher,被用來監(jiān)測狀態(tài)的改變摘仅。
<2>了解Zonde的改變靶庙。監(jiān)測Zonde的改變既可以使用專門的實例設(shè)置到讀操作上,也可以使用讀操作的默認watcher娃属。
Zookeeper實例可能失去或重新連接Zookeeper服務六荒,在CONNECTED和CONNECTING狀態(tài)中切換。如果連接斷開矾端,watcher得到一個Disconnected事件掏击。學要注意的是,這些狀態(tài)的遷移是由Zookeeper實例自己發(fā)起的秩铆,如果連接斷開他將自動嘗試自動連接砚亭。
如果任何一個close()方法被調(diào)用,或是會話由Expired類型的KeepState提示過期時殴玛,ZooKeeper可能會轉(zhuǎn)變成第三種狀態(tài)CLOSED钠惩。一旦處于CLOSED狀態(tài),Zookeeper對象將不再是活動的了(可以使用states的isActive()方法進行測試)族阅,而且不能被重用篓跛。客戶端必須建立一個新的Zookeeper實例才能重新連接到Zookeeper服務坦刀。