分佈式環境中大多數服務是容許部分失敗,也容許數據不一致,但有些最基礎的服務是須要高可靠性,高一致性的,這些服務是其餘分佈式服務運轉的基礎,好比naming service、分佈式lock等,這些分佈式的基礎服務有如下要求:html
對於這種有些挑戰CAP原則 的服務該如何設計,是一個挑戰,也是一個不錯的研究課題,Apache的ZooKeeper也許給了咱們一個不錯的答案。ZooKeeper是一個分佈式的,開放源碼的分佈式應用程序協調服務, 它暴露了一個簡單的原語集,分佈式應用程序能夠基於它實現同步服務,配置維護和命名服務等。關於ZooKeeper更多信息能夠參見 官方文檔java
ZooKeeper的基本使用node
搭一個分佈式的ZooKeeper環境比較簡單,基本步驟以下:算法
1)在各服務器安裝 ZooKeepershell
下載ZooKeeper後在各服務器上進行解壓便可apache
tar -xzf zookeeper-3.2.2.tar.gzapi
2)配置集羣環境服務器
分別各服務器的zookeeper安裝目錄下建立名爲zoo.cfg的配置文件,內容填寫以下:數據結構
其中zoo1和zoo2分別對應集羣中各服務器的機器名或ip,server.1和server.2中1和2分別對應各服務器的zookeeper id,id的設置方法爲在dataDir配置的目錄下建立名爲myid的文件,並把id做爲其文件內容便可,在本例中就分爲設置爲1和2。其餘配置具體含義可見官方文檔。框架
3)啓動集羣環境
分別在各服務器下運行zookeeper啓動腳本
/home/admin/zookeeper-3.2.2/bin/zkServer.sh start
4)應用zookeeper
應用zookeeper能夠在是shell中執行命令,也能夠在java或c中調用程序接口。
在shell中執行命令,可運行如下命令:
bin/zkCli.sh -server 10.20.147.35:2181
其中 10.20.147.35爲集羣中任一臺機器的ip或機器名。執行後可進入zookeeper的操做面板,具體如何操做可見官方文檔
在java中經過調用程序接口來應用zookeeper較爲複雜一點,須要瞭解watch和callback等概念,不過試驗最簡單的CURD倒不須要這些,只須要使用ZooKeeper這個類便可,具體測試代碼以下:
以上代碼比較簡單,查看一下zooKeeper的api doc就知道如何使用了
ZooKeeper的實現機理
ZooKeeper的實現機理是我看過的開源框架中最複雜的,它的解決是分佈式環境中的一致性問題,這個場景也決定了其實現的複雜性。看了兩三天的源碼仍是有些摸不着頭腦,有些超出了個人能力,不過經過看文檔和其餘高人寫的文章大體清楚它的原理和基本結構。
1)ZooKeeper的基本原理
ZooKeeper是以Fast Paxos算法爲基礎的,在前一篇 blog 中大體介紹了一下paxos,而沒有提到的是paxos存在活鎖的問題,也就是當有多個 proposer交錯提交時,有可能互相排斥致使沒有一個proposer能提交成功,而Fast Paxos做了一些優化,經過選舉產生一個leader,只有leader才能提交propose,具體算法可見Fast Paxos 。所以,要想弄得ZooKeeper首先得對Fast Paxos有所瞭解。
2)ZooKeeper的基本運轉流程
ZooKeeper主要存在如下兩個流程:
選舉Leader過程當中算法有不少,但要達到的選舉標準是一致的:
同步數據這個流程是ZooKeeper的精髓所在,而且就是Fast Paxos算法的具體實現。一個牛人畫了一個ZooKeeper數據流動圖,比較直觀地描述了ZooKeeper是如何同步數據的。
以上兩個核心流程我暫時還不能悟透其中的精髓,這也和我尚未徹底理解Fast Paxos算法有關,有待後續深刻學習
ZooKeeper的應用領域
Tim在blog中提到了Paxos所能應用的幾個主要場景,包括database replication、naming service、config配置管理、access control list等等,這也是ZooKeeper能夠應用的幾個主要場景。此外, ZooKeeper官方文檔中提到了幾個更爲基礎的分佈式應用,這也算是ZooKeeper的妙用吧
1)分佈式Barrier
Barrier是一種控制和協調多個任務觸發次序的機制,簡單說來就是搞個閘門把欲執行的任務給攔住,等全部任務都處於能夠執行的狀態時,才放開閘門。它的機理能夠見下圖所示:
在單機上JDK提供了CyclicBarrier這個類來實現這個機制,但在分佈式環境中JDK就無能爲力了。在分佈式裏實現Barrer須要高一致性作保障,所以 ZooKeeper能夠派上用場,所採起的方案就是用一個Node做爲Barrer的實體,須要被Barrer的任務經過調用exists()檢測這個Node的存在,當須要打開Barrier的時候,刪掉這個Node,ZooKeeper的watch機制會通知到各個任務能夠開始執行。
2) 分佈式 Queue
與 Barrier相似 分佈式環境中 實現Queue也須要高一致性作保障, ZooKeeper提供了一個種簡單的方式,ZooKeeper經過一個Node來維護Queue的實體,用其children來存儲Queue的內容,而且 ZooKeeper的create方法中提供了順序遞增的模式,會自動地在name後面加上一個遞增的數字來插入新元素。能夠用其 children來構建一個queue的數據結構,offer的時候使用create,take的時候按照children的順序刪除第一個便可。 ZooKeeper保障了各個server上數據是一致的,所以也就實現了一個 分佈式 Queue。take和offer的實例代碼以下所示:
3)分佈式lock
利用 ZooKeeper實現 分佈式lock,主要是經過一個Node來表明一個Lock,當一個client去拿鎖的時候,會在這個Node下建立一個自增序列的child,而後經過getChildren()方式來check建立的child是否是最靠前的,若是是則拿到鎖,不然就調用exist()來check第二靠前的child,並加上watch來監視。當拿到鎖的child執行完後歸還鎖,歸還鎖僅僅須要刪除本身建立的child,這時watch機制會通知到全部沒有拿到鎖的client,這些child就會根據前面所講的拿鎖規則來競爭鎖。