ZooKeeper是Hadoop Ecosystem中很是重要的組件,它的主要功能是爲分佈式系統提供一致性協調(Coordination)服務,與之對應的Google的相似服務叫Chubby。今天這篇文章分爲三個部分來介紹ZooKeeper,第一部分介紹ZooKeeper的基本原理,第二部分介紹ZooKeeper提供的Client API的使用,第三部分介紹一些ZooKeeper典型的應用場景。網絡
1. 數據模型
如上圖所示,ZooKeeper數據模型的結構與Unix文件系統很相似,總體上能夠看做是一棵樹,每一個節點稱作一個ZNode。每一個ZNode均可以經過其路徑惟一標識,好比上圖中第三層的第一個ZNode, 它的路徑是/app1/c1。在每一個ZNode上可存儲少許數據(默認是1M, 能夠經過配置修改, 一般不建議在ZNode上存儲大量的數據),這個特性很是有用,在後面的典型應用場景中會介紹到。另外,每一個ZNode上還存儲了其Acl信息,這裏須要注意,雖然說ZNode的樹形結構跟Unix文件系統很相似,可是其Acl與Unix文件系統是徹底不一樣的,每一個ZNode的Acl的獨立的,子結點不會繼承父結點的,關於ZooKeeper中的Acl能夠參考以前寫過的一篇文章《說說Zookeeper中的ACL》。併發
2.重要概念
2.1 ZNode
前文已介紹了ZNode, ZNode根據其自己的特性,能夠分爲下面兩類:app
ZNode還有一個Sequential的特性,若是建立的時候指定的話,該ZNode的名字後面會自動Append一個不斷增長的SequenceNo。分佈式
2.2 Session
Client與ZooKeeper之間的通訊,須要建立一個Session,這個Session會有一個超時時間。由於ZooKeeper集羣會把Client的Session信息持久化,因此在Session沒超時以前,Client與ZooKeeper Server的鏈接能夠在各個ZooKeeper Server之間透明地移動。oop
在實際的應用中,若是Client與Server之間的通訊足夠頻繁,Session的維護就不須要其它額外的消息了。不然,ZooKeeper Client會每t/3 ms發一次心跳給Server,若是Client 2t/3 ms沒收到來自Server的心跳回應,就會換到一個新的ZooKeeper Server上。這裏t是用戶配置的Session的超時時間。性能
2.3 Watcher
ZooKeeper支持一種Watch操做,Client能夠在某個ZNode上設置一個Watcher,來Watch該ZNode上的變化。若是該ZNode上有相應的變化,就會觸發這個Watcher,把相應的事件通知給設置Watcher的Client。須要注意的是,ZooKeeper中的Watcher是一次性的,即觸發一次就會被取消,若是想繼續Watch的話,須要客戶端從新設置Watcher。這個跟epoll裏的oneshot模式有點相似。spa
3. ZooKeeper特性
3.1 讀、寫(更新)模式
在ZooKeeper集羣中,讀能夠從任意一個ZooKeeper Server讀,這一點是保證ZooKeeper比較好的讀性能的關鍵;寫的請求會先Forwarder到Leader,而後由Leader來經過ZooKeeper中的原子廣播協議,將請求廣播給全部的Follower,Leader收到一半以上的寫成功的Ack後,就認爲該寫成功了,就會將該寫進行持久化,並告訴客戶端寫成功了。操作系統
3.2 WAL和Snapshot
和大多數分佈式系統同樣,ZooKeeper也有WAL(Write-Ahead-Log),對於每個更新操做,ZooKeeper都會先寫WAL, 而後再對內存中的數據作更新,而後向Client通知更新結果。另外,ZooKeeper還會按期將內存中的目錄樹進行Snapshot,落地到磁盤上,這個跟HDFS中的FSImage是比較相似的。這麼作的主要目的,一固然是數據的持久化,二是加快重啓以後的恢復速度,若是所有經過Replay WAL的形式恢復的話,會比較慢。線程
3.3 FIFO
對於每個ZooKeeper客戶端而言,全部的操做都是遵循FIFO順序的,這一特性是由下面兩個基本特性來保證的:一是ZooKeeper Client與Server之間的網絡通訊是基於TCP,TCP保證了Client/Server之間傳輸包的順序;二是ZooKeeper Server執行客戶端請求也是嚴格按照FIFO順序的。blog
3.4 Linearizability
在ZooKeeper中,全部的更新操做都有嚴格的偏序關係,更新操做都是串行執行的,這一點是保證ZooKeeper功能正確性的關鍵。
ZooKeeper Client Library提供了豐富直觀的API供用戶程序使用,下面是一些經常使用的API:
1. 名字服務(NameService)
分佈式應用中,一般須要一套完備的命令機制,既能產生惟一的標識,又方便人識別和記憶。 咱們知道,每一個ZNode均可以由其路徑惟一標識,路徑自己也比較簡潔直觀,另外ZNode上還能夠存儲少許數據,這些都是實現統一的NameService的基礎。下面以在HDFS中實現NameService爲例,來講明實現NameService的基本布驟:
2. 配置管理(Configuration Management)
在分佈式系統中,常會遇到這樣的場景: 某個Job的不少個實例在運行,它們在運行時大多數配置項是相同的,若是想要統一改某個配置,一個個實例去改,是比較低效,也是比較容易出錯的方式。經過ZooKeeper能夠很好的解決這樣的問題,下面的基本的步驟:
3. 組員管理(Group Membership)
在典型的Master-Slave結構的分佈式系統中,Master須要做爲「總管」來管理全部的Slave, 當有Slave加入,或者有Slave宕機,Master都須要感知到這個事情,而後做出對應的調整,以便不影響整個集羣對外提供服務。以HBase爲例,HMaster管理了全部的RegionServer,當有新的RegionServer加入的時候,HMaster須要分配一些Region到該RegionServer上去,讓其提供服務;當有RegionServer宕機時,HMaster須要將該RegionServer以前服務的Region都從新分配到當前正在提供服務的其它RegionServer上,以便不影響客戶端的正常訪問。下面是這種場景下使用ZooKeeper的基本步驟:
4. 簡單互斥鎖(Simple Lock)
咱們知識,在傳統的應用程序中,線程、進程的同步,均可以經過操做系統提供的機制來完成。可是在分佈式系統中,多個進程之間的同步,操做系統層面就無能爲力了。這時候就須要像ZooKeeper這樣的分佈式的協調(Coordination)服務來協助完成同步,下面是用ZooKeeper實現簡單的互斥鎖的步驟,這個能夠和線程間同步的mutex作類比來理解:
5. 互斥鎖(Simple Lock without Herd Effect)
上一節的例子中有一個問題,每次搶鎖都會有大量的進程去競爭,會形成羊羣效應(Herd Effect),爲了解決這個問題,咱們能夠經過下面的步驟來改進上述過程:
這裏須要補充一點,一般在分佈式系統中用ZooKeeper來作Leader Election(選主)就是經過上面的機制來實現的,這裏的持鎖者就是當前的「主」。
6. 讀寫鎖(Read/Write Lock)
咱們知道,讀寫鎖跟互斥鎖相比不一樣的地方是,它分紅了讀和寫兩種模式,多個讀能夠併發執行,但寫和讀、寫都互斥,不能同時執行行。利用ZooKeeper,在上面的基礎上,稍作修改也能夠實現傳統的讀寫鎖的語義,下面是基本的步驟:
7. 屏障(Barrier)
在分佈式系統中,屏障是這樣一種語義: 客戶端須要等待多個進程完成各自的任務,而後才能繼續往前進行下一步。下用是用ZooKeeper來實現屏障的基本步驟:
8. 雙屏障(Double Barrier)
雙屏障是這樣一種語義: 它能夠用來同步一個任務的開始和結束,當有足夠多的進程進入屏障後,纔開始執行任務;當全部的進程都執行完各自的任務後,屏障才撤銷。下面是用ZooKeeper來實現雙屏障的基本步驟: