[轉]ZooKeeper的學習與應用java
http://blog.csdn.net/rengq126/article/details/7393227node
1. ZooKeeper的學習與應用程序員
1.1. 概述算法
ZooKeeper是Apache在不少雲計算項目中的一個,與Hadoop密切相關,這種狀況致使我一開始認爲ZooKeeper的搭建須要Hadoop項目做爲支持,可是最後發現徹底不須要,它是能夠單獨運行的一個項目。shell
在網上看到了一個很不錯的關於ZooKeeper的介紹: 顧名思義動物園管理員,他是拿來管大象(Hadoop) 、 蜜蜂(Hive) 、 小豬(Pig) 的管理員, Apache Hbase和 Apache Solr 以及LinkedIn sensei 等項目中都採用到了 Zookeeper。ZooKeeper是一個分佈式的,開放源碼的分佈式應用程序協調服務,ZooKeeper是以Fast Paxos算法爲基礎,實現同步服務,配置維護和命名服務等分佈式應用。數據庫
從介紹能夠看出,ZooKeeper更傾向於對大型應用的協同維護管理工做。IBM則給出了IBM對ZooKeeper的認知: Zookeeper 分佈式服務框架是 Apache Hadoop 的一個子項目,它主要是用來解決分佈式應用中常常遇到的一些數據管理問題,如:統一命名服務、狀態同步服務、集羣管理、分佈式應用配置項的管理等。express
總之,我認爲它的核心詞就是一個單詞,協調。apache
1.2. ZooKeeper的特徵緩存
在Hadoop權威指南中看到了關於ZooKeeper的一些核心特徵,閱讀以後感受總結的甚是精闢,在這裏引用並總結。服務器
1.2.1. 簡易
ZooKeeper的最重要核心就是一個精簡文件系統,提供一些簡單的操做以及附加的抽象(例如排序和通知)。
1.2.2. 易表達
ZooKeeper的原型是一個豐富的集合,它們是一些已建好的塊,能夠用來構建大型的協做數據結構和協議,例如:分佈式隊列、分佈式鎖以及一組對等體的選舉。
1.2.3. 高可用性
ZooKeeper運行在一些集羣上,被設計成可用性較高的,所以應用程序能夠依賴它。ZooKeeper能夠幫助你的系統避免單點故障,從而創建一個可靠的應用程序。
1.2.4. 鬆散耦合
ZooKeeper的交互支持參與者之間並不瞭解對方。例如:ZooKeeper能夠被當作一種公共的機制,使得進程彼此不知道對方的存在也能夠相互發現而且交互,對等方可能甚至不是同步的。
這一特色我感受最能體如今集羣的部署啓動過程當中。像Hadoop當把配置文件寫好以後,而後運行啓動腳本,則251,241,242中做爲集羣的虛擬機是同步啓動的,也就是DataNode,NameNode,TaskTracker,以及JobTracker的啓動並運行時在一次啓動過程當中啓動的,就是運行一次啓動腳本文件,則都啓動起來。可是ZooKeeper的啓動過程卻不是這樣的。我在251,241,242部署了ZooKeeper集羣,並進行啓動,則啓動的過程是這樣的:首先ssh到251而後啓動,這時候251的集羣節點啓動起來,可是控制檯一直報錯,大概的含義就是沒有檢測到其餘兩個結點。接着分別ssh到241,242,分別啓動集羣中的剩下的結點,當241啓動起來時,回到251查看,發現報錯的信息減小,意思是隻差一個結點。當251,241,242三臺服務器的結點所有啓動起來,則三臺的服務器的控制檯打印出正常的信息。
1.2.5. ZooKeeper是一個庫
ZooKeeper提供了一個開源的、共享的執行存儲,以及通用協做的方法,分擔了每一個程序員寫通用協議的負擔。隨着時間的推移,人們能夠增長和改進這個庫來知足本身的需求。
1.3. Zookeeper基本知識
在這一小結,我介紹關於ZooKeeper的一些基本理論知識,以便對ZooKeeper有一個基本感性的認識吧,因爲學習的時間不長,有些的認識多是比較片面的,以後若是有了更深層次的認識,會補充於以後的月總結中。
1.3.1. 層次化的名字空間
ZooKeeper的整個名字空間的結構是層次化的,和通常的Linux文件系統結構很是類似,一顆很大的樹。這也就是ZooKeeper的數據結構狀況。名字空間的層次由斜槓/來進行分割,在名稱空間裏面的每個結點的名字空間惟一由這個結點的路徑來肯定。
圖3.1 ZooKeeper的層次化名字空間
每個節點擁有自身的一些信息,包括:數據、數據長度、建立時間、修改時間等等。從這樣一類既含有數據,又做爲路徑表標示的節點的特色中,能夠看出,ZooKeeper的節點既能夠被看作是一個文件,又能夠被看作是一個目錄,它同時具備兩者的特色。爲了便於表達,從此咱們將使用Znode來表示所討論的ZooKeeper節點。
1.3.2. Znode
Znode維護着數據、ACL(access control list,訪問控制列表)、時間戳等交換版本號等數據結構,它經過對這些數據的管理來讓緩存生效而且令協調更新。每當Znode中的數據更新後它所維護的版本號將增長,這很是相似於數據庫中計數器時間戳的操做方式。
另外Znode還具備原子性操做的特色:命名空間中,每個Znode的數據將被原子地讀寫。讀操做將讀取與Znode相關的全部數據,寫操做將替換掉全部的數據。除此以外,每個節點都有一個訪問控制列表,這個訪問控制列表規定了用戶操做的權限。
ZooKeeper中一樣存在臨時節點。這些節點與session同時存在,當session生命週期結束,這些臨時節點也將被刪除。臨時節點在某些場合也發揮着很是重要的做用。
1.3.3. Watch機制
Watch機制就和單詞自己的意思同樣,看。看什麼?具體來說就是某一個或者一些Znode的變化。官方給出的定義:一個Watch事件是一個一次性的觸發器,當被設置了Watch的數據發生了改變的時候,則服務器將這個改變發送給設置了Watch的客戶端,以便通知它們。
Watch機制主要有如下三個特色:
1 一次性的觸發器(one-time trigger)
當數據改變的時候,那麼一個Watch事件會產生而且被髮送到客戶端中。可是客戶端只會收到一次這樣的通知,若是之後這個數據再次發生改變的時候,以前設置Watch的客戶端將不會再次收到改變的通知,由於Watch機制規定了它是一個一次性的觸發器。
2 發送給客戶端
這個代表了Watch的通知事件是從服務器發送給客戶端的,是異步的,這就代表不一樣的客戶端收到的Watch的時間可能不一樣,可是ZooKeeper有保證:當一個客戶端在看到Watch事件以前是不會看到結點數據的變化的。例如:A=3,此時在上面設置了一次Watch,若是A忽然變成4了,那麼客戶端會先收到Watch事件的通知,而後纔會看到A=4。
3被設置Watch的數據
這代表了一個結點能夠變換的不一樣方式。一個Znode變化方式有兩種,結點自己數據的變化以及結點孩子的變化。所以Watch也能夠設置爲這個Znode的結點數據,固然也能夠設置爲Znode結點孩子。
1.3.4. ACL訪問控制列表
這是另一個和Linux操做系統很是類似的地方,ZooKeeper使用ACL來控制對旗下Znode結點們的訪問。ACL的實現和Linux文件系統的訪問權限十分相似:它經過設置權限爲來代表是否容許對一個結點的相關內容的改變。
可是與傳統Linux機制不太相同,一個結點的數據沒有相似「擁有者,組用戶,其餘用戶」的概念,在ZooKeeper中,ACL經過設置ID以及與其關聯的權限來完成訪問控制的。ACL的權限組成語法是:
(scheme:expression, perms)
前者代表設置的ID,逗號後面表示的是ID相關的權限,例如:
(ip:172.16.16.1, READ)
指明瞭IP地址爲如上的用戶的權限爲只讀。
如下列舉如下ACL所具備的權限
CREATE:代表你能夠建立一個Znode的子結點。
READ:你能夠獲得這個結點的數據以及列舉該結點的子結點狀況。
WRITE:設置一個結點的數據。
DELETE:能夠刪除一個結點
ADMIN:對一個結點設置權限。
1.4. ZooKeeper的部署以及簡單使用
要想使用ZooKeeper,首先就要把它部署在服務器上跑起來,就想Apache,Tomcat,FtpServer等服務器同樣。ZooKeeper的部署方式主要有三種,單機模式、僞集羣模式、集羣模式。其實剩下的兩種模式都是集羣模式的特殊狀況。
1.4.1. 基本的環境變量配置
Java大型的項目中,環境變量的配置很重要,若是沒有很好的配置環境變量的話,甚至項目連啓動都是難事。
export ZOOKEEPER_HOME=/home/zookeeper-3.3.3
export PATH=$PATH:$ZOOKEEPER_HOME/bin:$ZOOKEEPER_HOME/conf
1.4.2. ZooKeeper的單機模式部署
ZooKeeper的單機模式一般是用來快速測試客戶端應用程序的,在實際過程當中不多是單機模式。單機模式的配置也比較簡單。
l 編寫配置文件zoo.cfg
zookeeper-3.3.3/conf文件夾下面就是要編寫配置文件的位置了。在文件夾下面新建一個文件zoo.cfg。ZooKeeper的運行默認是讀取zoo.cfg文件裏面的內容的。如下是一個最簡單的配置文件的樣例:
tickTime=2000
dataDir=/var/zookeeper
clientPort=2181
在這個文件中,咱們須要指定 dataDir 的值,它指向了一個目錄,這個目錄在開始的時候須要爲空。下面是每一個參數的含義:
tickTime :基本事件單元,以毫秒爲單位。這個時間是做爲 Zookeeper 服務器之間或客戶端與服務器之間維持心跳的時間間隔,也就是每一個 tickTime 時間就會發送一個心跳。
dataDir :存儲內存中數據庫快照的位置,顧名思義就是 Zookeeper 保存數據的目錄,默認狀況下,Zookeeper 將寫數據的日誌文件也保存在這個目錄裏。
clientPort :這個端口就是客戶端鏈接 Zookeeper 服務器的端口,Zookeeper 會監聽這個端口,接受客戶端的訪問請求。
使用單機模式時用戶須要注意:這種配置方式下沒有 ZooKeeper 副本,因此若是 ZooKeeper 服務器出現故障, ZooKeeper 服務將會中止。
l 執行運行腳本
在zookeeper-3.3.3/bin文件夾下面運行zkServer.sh便可,運行完畢以後則ZooKeeper服務變啓動起來。
./zkServer.sh start
腳本默認調用zoo.cfg裏面的配置,所以程序正常啓動。
1.4.3. ZooKeeper的集羣模式部署
ZooKeeper的集羣模式下,多個Zookeeper服務器在工做前會選舉出一個Leader,在接下來的工做中這個被選舉出來的Leader死了,而剩下的Zookeeper服務器會知道這個Leader死掉了,在活着的Zookeeper集羣中會繼續選出一個Leader,選舉出Leader的目的是爲了能夠在分佈式的環境中保證數據的一致性。如圖所示:
圖3.2 ZooKeeper集羣模式圖
l 確認集羣服務器的數量
因爲ZooKeeper集羣中,會有一個Leader負責管理和協調其餘集羣服務器,所以服務器的數量一般都是單數,例如3,5,7...等,這樣2n+1的數量的服務器就能夠容許最多n臺服務器的失效。
l 編寫配置文件
配置文件須要在每臺服務器中都要編寫,如下是一個配置文件的樣本:
# Filename zoo.cfg
tickTime=2000
dataDir=/var/zookeeper/
clientPort=2181
initLimit=5
syncLimit=2
server.1=202.115.36.251:2888:3888
server.2=202.115.36.241:2888:3888
server.3=202.115.36.242:2888:3888
initLimit:這個配置項是用來配置 Zookeeper 接受客戶端(這裏所說的客戶端不是用戶鏈接 Zookeeper 服務器的客戶端,而是 Zookeeper 服務器集羣中鏈接到 Leader 的 Follower 服務器)初始化鏈接時最長能忍受多少個心跳時間間隔數。當已經超過 10 個心跳的時間(也就是 tickTime)長度後 Zookeeper 服務器尚未收到客戶端的返回信息,那麼代表這個客戶端鏈接失敗。總的時間長度就是 5*2000=10 秒。
syncLimit:這個配置項標識 Leader 與 Follower 之間發送消息,請求和應答時間長度,最長不能超過多少個 tickTime 的時間長度,總的時間長度就是 2*2000=4 秒
server.A=B:C:D:其中 A 是一個數字,表示這個是第幾號服務器;B 是這個服務器的 ip 地址;C 表示的是這個服務器與集羣中的 Leader 服務器交換信息的端口;D 表示的是萬一集羣中的 Leader 服務器掛了,須要一個端口來從新進行選舉,選出一個新的 Leader,而這個端口就是用來執行選舉時服務器相互通訊的端口。若是是僞集羣的配置方式,因爲 B 都是同樣,因此不一樣的 Zookeeper 實例通訊端口號不能同樣,因此要給它們分配不一樣的端口號。
l 建立myid文件
除了修改 zoo.cfg 配置文件,集羣模式下還要配置一個文件 myid,這個文件在 dataDir 目錄下,這個文件裏面就只有一個數據就是 A 的值,Zookeeper 啓動時會讀取這個文件,拿到裏面的數據與 zoo.cfg 裏面的配置信息比較從而判斷究竟是那個 server。
l 執行運行腳本
和單機模式下的運行方式基本相同,值得注意的地方就是要分別在不一樣服務器上執行一次,例如分別在251,241,242上運行:
./zkServer.sh start
這樣才能使得整個集羣啓動起來。
1.4.4. ZooKeeper的集羣僞分佈
其實在企業中式不會存在的,另外爲了測試一個客戶端程序也沒有必要存在,只有在物質條件比較匱乏的條件下才會存在的模式。
集羣僞分佈模式就是在單機下模擬集羣的ZooKeeper服務,在一臺機器上面有多個ZooKeeper的JVM同時運行。
l 確認集羣僞服務器的數量
2n+1,和以前的集羣分佈相同。
l 編寫配置文件
在/conf文件夾新建三個配置文件,zoo1.cfg,zoo2.cfg以及zoo3.cfg。配置文件分別以下編寫:
Zoo1.cfg
tickTime=2000
initLimit=10
syncLimit=5
dataDir=/root/hadoop-0.20.2/zookeeper-3.3.1/d_1
clientPort=2181
server.1=localhost:2887:3887
server.2=localhost:2888:3888
server.3=localhost:2889:3889
Zoo2.cfg
tickTime=2000
initLimit=10
syncLimit=5
dataDir=/root/hadoop-0.20.2/zookeeper-3.3.1/d_2
clientPort=2182
server.1=localhost:2887:3887
server.2=localhost:2888:3888
server.3=localhost:2889:3889
Zoo3.cfg
tickTime=2000
initLimit=10
syncLimit=5
dataDir=/root/hadoop-0.20.2/zookeeper-3.3.1/d_3
clientPort=2183
server.1=localhost:2887:3887
server.2=localhost:2888:3888
server.3=localhost:2889:3889
因爲三個服務都在同一臺電腦上,所以這裏要保證地址的惟一性,所以要特別注意IP地址和端口號不要互相沖突,以避免影響程序的正確執行。
l 建立myid文件
這個同集羣模式部署,在各自的文件夾下面建立。
l 執行運行腳本
因爲全部的配置文件都在/conf文件夾下面,所以要執行三次,並且要加文件名的參數,否則會默認執行zoo.cfg這個文件,以下:
./zkServer.sh start zoo1.cfg
./zkServer.sh start zoo2.cfg
./zkServer.sh start zoo3.cfg
執行完畢後,將完成ZooKeeper的集羣僞分佈的啓動。
1.4.5. 經過ZooKeeper命令行工具訪問ZooKeeper
ZooKeeper命令行工具相似於Linux的shell環境,不過功能確定不及shell啦,可是使用它咱們能夠簡單的對ZooKeeper進行訪問,數據建立,數據修改等操做。
當啓動 ZooKeeper 服務成功以後,輸入下述命令,鏈接到 ZooKeeper 服務:
zkCli.sh –server 202.115.36.251:2181
鏈接成功後,系統會輸出 ZooKeeper 的相關環境以及配置信息,並在屏幕輸出「 Welcome to ZooKeeper 」等信息。
命令行工具的一些簡單操做以下:
1 )使用 ls 命令來查看當前 ZooKeeper 中所包含的內容:
[zk: 202.115.36.251:2181(CONNECTED) 1] ls /
2 )建立一個新的 znode ,使用 create /zk myData 。這個命令建立了一個新的 znode 節點「 zk 」以及與它關聯的字符串:
[zk: 202.115.36.251:2181(CONNECTED) 2] create /zk "myData"
3 )咱們運行 get 命令來確認 znode 是否包含咱們所建立的字符串:
[zk: 202.115.36.251:2181(CONNECTED) 3] get /zk
4 )下面咱們經過 set 命令來對 zk 所關聯的字符串進行設置:
[zk: 202.115.36.251:2181(CONNECTED) 4] set /zk "zsl"
5 )下面咱們將剛纔建立的 znode 刪除:
[zk: 202.115.36.251:2181(CONNECTED) 5] delete /zk
1.4.6. 使用API來訪問ZooKeeper
API訪問ZooKeeper纔是客戶端主要的使用手段,經過在客戶端編寫豐富多彩的程序,來達到對ZooKeeper的利用。這裏給出一個簡單的例子:(深刻的還沒能力給出啊,例子是從網上找的很清晰明瞭)
1. import java.io.IOException;
2.
3. import org.apache.zookeeper.CreateMode;
4. import org.apache.zookeeper.KeeperException;
5. import org.apache.zookeeper.Watcher;
6. import org.apache.zookeeper.ZooDefs.Ids;
7. import org.apache.zookeeper.ZooKeeper;
8.
9. public class demo {
10. // 會話超時時間,設置爲與系統默認時間一致
11. private static final int SESSION_TIMEOUT=30000;
12.
13. // 建立 ZooKeeper 實例
14. ZooKeeper zk;
15.
16. // 建立 Watcher 實例
17. Watcher wh=new Watcher(){
18. public void process(org.apache.zookeeper.WatchedEvent event)
19. {
20. System.out.println(event.toString());
21. }
22. };
23.
24. // 初始化 ZooKeeper 實例
25. private void createZKInstance() throws IOException
26. {
27. zk=new ZooKeeper("localhost:2181",demo.SESSION_TIMEOUT,this.wh);
28.
29. }
30.
31. private void ZKOperations() throws IOException,InterruptedException,KeeperException
32. {
33. System.out.println("\n1. 建立 ZooKeeper 節點 (znode : zoo2, 數據: myData2 ,權限: OPEN_ACL_UNSAFE ,節點類型: Persistent");
34. zk.create("/zoo2","myData2".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
35.
36. System.out.println("\n2. 查看是否建立成功: ");
37. System.out.println(new String(zk.getData("/zoo2",false,null)));
38.
39. System.out.println("\n3. 修改節點數據 ");
40. zk.setData("/zoo2", "shenlan211314".getBytes(), -1);
41.
42. System.out.println("\n4. 查看是否修改爲功: ");
43. System.out.println(new String(zk.getData("/zoo2", false, null)));
44.
45. System.out.println("\n5. 刪除節點 ");
46. zk.delete("/zoo2", -1);
47.
48. System.out.println("\n6. 查看節點是否被刪除: ");
49. System.out.println(" 節點狀態: ["+zk.exists("/zoo2", false)+"]");
50. }
51.
52. private void ZKClose() throws InterruptedException
53. {
54. zk.close();
55. }
56.
57. public static void main(String[] args) throws IOException,InterruptedException,KeeperException {
58. demo dm=new demo();
59. dm.createZKInstance( );
60. dm.ZKOperations();
61. dm.ZKClose();
62. }
63.}
此類包含兩個主要的 ZooKeeper 函數,分別爲 createZKInstance ()和 ZKOperations ()。其中 createZKInstance ()函數負責對 ZooKeeper 實例 zk 進行初始化。 ZooKeeper 類有兩個構造函數,咱們這裏使用 「 ZooKeeper ( String connectString, , int sessionTimeout, , Watcher watcher )」對其進行初始化。所以,咱們須要提供初始化所需的,鏈接字符串信息,會話超時時間,以及一個 watcher 實例。 17 行到 23 行代碼,是程序所構造的一個 watcher 實例,它可以輸出所發生的事件。
ZKOperations ()函數是咱們所定義的對節點的一系列操做。它包括:建立 ZooKeeper 節點( 33 行到 34 行代碼)、查看節點( 36 行到 37 行代碼)、修改節點數據( 39 行到 40 行代碼)、查看修改後節點數據( 42 行到 43 行代碼)、刪除節點( 45 行到 46 行代碼)、查看節點是否存在( 48 行到 49 行代碼)。另外,須要注意的是:在建立節點的時候,須要提供節點的名稱、數據、權限以及節點類型。此外,使用 exists 函數時,若是節點不存在將返回一
個 null 值。
1.5. 小結
對於ZooKeeper的認識目前處在比較淺顯的狀態,瞭解到了基本的服務的部署以及大概ZooKeeper的工做原理。不少東西都是隻懂得皮毛,如今可以深深地感覺到「只有結合具體的應用才能使你對一個東西有較深的瞭解」這句話的深入含義了。