天天看點

zookeeper使用和原理探究 (注意linux下防火牆導緻啟動失敗的坑,使用service iptables stop 關閉防火牆 使用service iptables status确認)

轉  http://www.blogjava.net/BucketLi/archive/2010/12/21/341268.html

zookeeper使用和原理探究(一)

zookeeper介紹

zookeeper是一個為分布式應用提供一緻性服務的軟體,它是開源的Hadoop項目中的一個子項目,并且根據google發表的<The Chubby lock service for loosely-coupled distributed systems>論文來實作的,接下來我們首先來安裝使用下這個軟體,然後再來探索下其中比較重要一緻性算法。  

zookeeper安裝和使用

zookeeper的安裝基本上可以按照 http://hadoop.apache.org/zookeeper/docs/current/ zookeeperStarted.html 這個頁面上的步驟完成安裝,這裡主要介紹下部署一個叢集的步驟,因為這個官方頁面似乎講得并不是非常詳細(Running

Replicated Zookeeper)。

由于手頭機器不足,是以在一台機器上部署了3個server,如果你手頭也比較緊,也可以這麼做。那麼我建了3個檔案夾,如下

server1   server2   server3

然後每個檔案夾裡面解壓一個zookeeper的下載下傳包,并且還建了幾個檔案夾,總體結構如下,最後那個是下載下傳過來壓縮包的解壓檔案

data dataLog logs zookeeper-3.3.2

那麼首先進入data目錄,建立一個myid的檔案,裡面寫入一個數字,比如我這個是server1,那麼就寫一個1,server2對應myid檔案就寫入2,server3對應myid檔案就寫個3

然後進入zookeeper-3.3.2/conf目錄,那麼如果是剛下過來,會有3個檔案,configuration.xml, log4j.properties,zoo_sample.cfg,這3個檔案我們首先要做的就是在這個目錄建立一個zoo.cfg的配置檔案,當然你可以把zoo_sample.cfg檔案改成zoo.cfg,配置的内容如下所示: 

tickTime=2000

initLimit=5

syncLimit=2

dataDir=xxxx/zookeeper/server1/data

dataLogDir=xxx/zookeeper/server1/dataLog

clientPort=2181

server.1=127.0.0.1:2888:3888

server.2=127.0.0.1:2889:3889

server.3=127.0.0.1:2890:3890

标紅的幾個配置應該官網講得很清楚了,隻是需要注意的是clientPort這個端口如果你是在1台機器上部署多個server,那麼每台機器都要不同的clientPort,比如我server1是2181,server2是2182,server3是2183,dataDir和dataLogDir也需要區分下。 

最後幾行唯一需要注意的地方就是 server.X 這個數字就是對應 data/myid中的數字。你在3個server的myid檔案中分别寫入了1,2,3,那麼每個server中的zoo.cfg都配server.1,server.2,server.3就OK了。因為在同一台機器上,後面連着的2個端口3個server都不要一樣,否則端口沖突,其中第一個端口用來叢集成員的資訊交換,第二個端口是在leader挂掉時專門用來進行選舉leader所用。

進入zookeeper-3.3.2/bin 目錄中,./zkServer.sh start啟動一個server,這時會報大量錯誤?其實沒什麼關系,因為現在叢集隻起了1台server,zookeeper伺服器端起來會根據zoo.cfg的伺服器清單發起選舉leader的請求,因為連不上其他機器而報錯,那麼當我們起第二個zookeeper執行個體後,leader将會被選出,進而一緻性服務開始可以使用,這是因為3台機器隻要有2台可用就可以選出leader并且對外提供服務(2n+1台機器,可以容n台機器挂掉)。

接下來就可以使用了,我們可以先通過 zookeeper自帶的用戶端互動程式來簡單感受下zookeeper到底做一些什麼事情。進入zookeeper-3.3.2/bin(3個server中任意一個)下,./zkCli.sh –server 127.0.0.1:2182,我連的是開着2182端口的機器。

那麼,首先我們随便打個指令,因為zookeeper不認識,他會給出指令的help,如下圖  

zookeeper使用和原理探究 (注意linux下防火牆導緻啟動失敗的坑,使用service iptables stop 關閉防火牆 使用service iptables status确認)

ls(檢視目前節點資料),

ls2(檢視目前節點資料并能看到更新次數等資料) ,

create(建立一個節點) ,

get(得到一個節點,包含資料和更新次數等資料),

set(修改節點)

delete(删除一個節點)

通過上述指令實踐,我們可以發現,zookeeper使用了一個類似檔案系統的樹結構,資料可以挂在某個節點上,可以對這個節點進行删改。另外我們還發現,當改動一個節點的時候,叢集中活着的機器都會更新到一緻的資料。 

zookeeper的資料模型

在簡單使用了zookeeper之後,我們發現其資料模型有些像作業系統的檔案結構,結構如下圖所示

zookeeper使用和原理探究 (注意linux下防火牆導緻啟動失敗的坑,使用service iptables stop 關閉防火牆 使用service iptables status确認)

(1)     每個節點在zookeeper中叫做znode,并且其有一個唯一的路徑辨別,如/SERVER2節點的辨別就為/APP3/SERVER2

(2)     Znode可以有子znode,并且znode裡可以存資料,但是EPHEMERAL類型的節點不能有子節點

(3)     Znode中的資料可以有多個版本,比如某一個路徑下存有多個資料版本,那麼查詢這個路徑下的資料就需要帶上版本。

(4)     znode 可以是臨時節點,一旦建立這個 znode 的用戶端與伺服器失去聯系,這個 znode 也将自動删除,Zookeeper 的用戶端和伺服器通信采用長連接配接方式,每個用戶端和 

伺服器通過心跳來保持連接配接,這個連接配接狀态稱為 session,如果 znode 是臨時節點,這個 session 失效,znode 也就删除了

(5)     znode 的目錄名可以自動編号,如 App1 已經存在,再建立的話,将會自動命名為 App2 

(6)     znode 可以被監控,包括這個目錄節點中存儲的資料的修改,子節點目錄的變化等,一旦變化可以通知設定監控的用戶端,這個功能是zookeeper對于應用最重要的特性,通過這個特性可以實作的功能包括配置的集中管理,叢集管理,分布式鎖等等。  

通過java代碼使用zookeeper 

Zookeeper的使用主要是通過建立其jar包下的Zookeeper執行個體,并且調用其接口方法進行的,主要的操作就是對znode的增删改操作,監聽znode的變化以及處理。 

以下為主要的API使用和解釋

zookeeper使用和原理探究 (注意linux下防火牆導緻啟動失敗的坑,使用service iptables stop 關閉防火牆 使用service iptables status确認)

//建立一個Zookeeper執行個體,第一個參數為目标伺服器位址和端口,第二個參數為Session逾時時間,第三個為節點變化時的回調方法

zookeeper使用和原理探究 (注意linux下防火牆導緻啟動失敗的坑,使用service iptables stop 關閉防火牆 使用service iptables status确認)

ZooKeeper zk = new ZooKeeper("127.0.0.1:2181", 500000,new Watcher() {

zookeeper使用和原理探究 (注意linux下防火牆導緻啟動失敗的坑,使用service iptables stop 關閉防火牆 使用service iptables status确認)

           // 監控所有被觸發的事件

zookeeper使用和原理探究 (注意linux下防火牆導緻啟動失敗的坑,使用service iptables stop 關閉防火牆 使用service iptables status确認)

             public void process(WatchedEvent event) {

zookeeper使用和原理探究 (注意linux下防火牆導緻啟動失敗的坑,使用service iptables stop 關閉防火牆 使用service iptables status确認)

           //dosomething

zookeeper使用和原理探究 (注意linux下防火牆導緻啟動失敗的坑,使用service iptables stop 關閉防火牆 使用service iptables status确認)

           }

zookeeper使用和原理探究 (注意linux下防火牆導緻啟動失敗的坑,使用service iptables stop 關閉防火牆 使用service iptables status确認)

      });

zookeeper使用和原理探究 (注意linux下防火牆導緻啟動失敗的坑,使用service iptables stop 關閉防火牆 使用service iptables status确認)

//建立一個節點root,資料是mydata,不進行ACL權限控制,節點為永久性的(即用戶端shutdown了也不會消失)

zookeeper使用和原理探究 (注意linux下防火牆導緻啟動失敗的坑,使用service iptables stop 關閉防火牆 使用service iptables status确認)

zk.create("/root", "mydata".getBytes(),Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);

zookeeper使用和原理探究 (注意linux下防火牆導緻啟動失敗的坑,使用service iptables stop 關閉防火牆 使用service iptables status确認)
zookeeper使用和原理探究 (注意linux下防火牆導緻啟動失敗的坑,使用service iptables stop 關閉防火牆 使用service iptables status确認)

//在root下面建立一個childone znode,資料為childone,不進行ACL權限控制,節點為永久性的

zookeeper使用和原理探究 (注意linux下防火牆導緻啟動失敗的坑,使用service iptables stop 關閉防火牆 使用service iptables status确認)

zk.create("/root/childone","childone".getBytes(), Ids.OPEN_ACL_UNSAFE,CreateMode.PERSISTENT);

zookeeper使用和原理探究 (注意linux下防火牆導緻啟動失敗的坑,使用service iptables stop 關閉防火牆 使用service iptables status确認)
zookeeper使用和原理探究 (注意linux下防火牆導緻啟動失敗的坑,使用service iptables stop 關閉防火牆 使用service iptables status确認)

//取得/root節點下的子節點名稱,傳回List<String>

zookeeper使用和原理探究 (注意linux下防火牆導緻啟動失敗的坑,使用service iptables stop 關閉防火牆 使用service iptables status确認)

zk.getChildren("/root",true);

zookeeper使用和原理探究 (注意linux下防火牆導緻啟動失敗的坑,使用service iptables stop 關閉防火牆 使用service iptables status确認)
zookeeper使用和原理探究 (注意linux下防火牆導緻啟動失敗的坑,使用service iptables stop 關閉防火牆 使用service iptables status确認)

//取得/root/childone節點下的資料,傳回byte[]

zookeeper使用和原理探究 (注意linux下防火牆導緻啟動失敗的坑,使用service iptables stop 關閉防火牆 使用service iptables status确認)

zk.getData("/root/childone", true, null);

zookeeper使用和原理探究 (注意linux下防火牆導緻啟動失敗的坑,使用service iptables stop 關閉防火牆 使用service iptables status确認)
zookeeper使用和原理探究 (注意linux下防火牆導緻啟動失敗的坑,使用service iptables stop 關閉防火牆 使用service iptables status确認)

//修改節點/root/childone下的資料,第三個參數為版本,如果是-1,那會無視被修改的資料版本,直接改掉

zookeeper使用和原理探究 (注意linux下防火牆導緻啟動失敗的坑,使用service iptables stop 關閉防火牆 使用service iptables status确認)

zk.setData("/root/childone","childonemodify".getBytes(), -1);

zookeeper使用和原理探究 (注意linux下防火牆導緻啟動失敗的坑,使用service iptables stop 關閉防火牆 使用service iptables status确認)
zookeeper使用和原理探究 (注意linux下防火牆導緻啟動失敗的坑,使用service iptables stop 關閉防火牆 使用service iptables status确認)

//删除/root/childone這個節點,第二個參數為版本,-1的話直接删除,無視版本

zookeeper使用和原理探究 (注意linux下防火牆導緻啟動失敗的坑,使用service iptables stop 關閉防火牆 使用service iptables status确認)

zk.delete("/root/childone", -1);

zookeeper使用和原理探究 (注意linux下防火牆導緻啟動失敗的坑,使用service iptables stop 關閉防火牆 使用service iptables status确認)
zookeeper使用和原理探究 (注意linux下防火牆導緻啟動失敗的坑,使用service iptables stop 關閉防火牆 使用service iptables status确認)

//關閉session

zookeeper使用和原理探究 (注意linux下防火牆導緻啟動失敗的坑,使用service iptables stop 關閉防火牆 使用service iptables status确認)

zk.close();

zookeeper使用和原理探究 (注意linux下防火牆導緻啟動失敗的坑,使用service iptables stop 關閉防火牆 使用service iptables status确認)

Zookeeper的主流應用場景實作思路(除去官方示例) 

(1)配置管理

集中式的配置管理在應用叢集中是非常常見的,一般商業公司内部都會實作一套集中的配置管理中心,應對不同的應用叢集對于共享各自配置的需求,并且在配置變更時能夠通知到叢集中的每一個機器。

Zookeeper很容易實作這種集中式的配置管理,比如将APP1的所有配置配置到/APP1

znode下,APP1所有機器一啟動就對/APP1這個節點進行監控(zk.exist("/APP1",true)),并且實作回調方法Watcher,那麼在zookeeper上/APP1

znode節點下資料發生變化的時候,每個機器都會收到通知,Watcher方法将會被執行,那麼應用再取下資料即可(zk.getData("/APP1",false,null));

以上這個例子隻是簡單的粗顆粒度配置監控,細顆粒度的資料可以進行分層級監控,這一切都是可以設計和控制的。     

zookeeper使用和原理探究 (注意linux下防火牆導緻啟動失敗的坑,使用service iptables stop 關閉防火牆 使用service iptables status确認)

(2)叢集管理 

應用叢集中,我們常常需要讓每一個機器知道叢集中(或依賴的其他某一個叢集)哪些機器是活着的,并且在叢集機器因為當機,網絡斷鍊等原因能夠不在人工介入的情況下迅速通知到每一個機器。

Zookeeper同樣很容易實作這個功能,比如我在zookeeper伺服器端有一個znode叫/APP1SERVERS,那麼叢集中每一個機器啟動的時候都去這個節點下建立一個EPHEMERAL類型的節點,比如server1建立/APP1SERVERS/SERVER1(可以使用ip,保證不重複),server2建立/APP1SERVERS/SERVER2,然後SERVER1和SERVER2都watch

/APP1SERVERS這個父節點,那麼也就是這個父節點下資料或者子節點變化都會通知對該節點進行watch的用戶端。因為EPHEMERAL類型節點有一個很重要的特性,就是用戶端和伺服器端連接配接斷掉或者session過期就會使節點消失,那麼在某一個機器挂掉或者斷鍊的時候,其對應的節點就會消失,然後叢集中所有對/APP1SERVERS進行watch的用戶端都會收到通知,然後取得最新清單即可。

另外有一個應用場景就是叢集選master,一旦master挂掉能夠馬上能從slave中選出一個master,實作步驟和前者一樣,隻是機器在啟動的時候在APP1SERVERS建立的節點類型變為EPHEMERAL_SEQUENTIAL類型,這樣每個節點會自動被編号,例如          

zookeeper使用和原理探究 (注意linux下防火牆導緻啟動失敗的坑,使用service iptables stop 關閉防火牆 使用service iptables status确認)

zk.create("/testRootPath/testChildPath1","1".getBytes(), Ids.OPEN_ACL_UNSAFE,CreateMode.EPHEMERAL_SEQUENTIAL);

zookeeper使用和原理探究 (注意linux下防火牆導緻啟動失敗的坑,使用service iptables stop 關閉防火牆 使用service iptables status确認)
zookeeper使用和原理探究 (注意linux下防火牆導緻啟動失敗的坑,使用service iptables stop 關閉防火牆 使用service iptables status确認)

zk.create("/testRootPath/testChildPath2","2".getBytes(), Ids.OPEN_ACL_UNSAFE,CreateMode.EPHEMERAL_SEQUENTIAL);

zookeeper使用和原理探究 (注意linux下防火牆導緻啟動失敗的坑,使用service iptables stop 關閉防火牆 使用service iptables status确認)
zookeeper使用和原理探究 (注意linux下防火牆導緻啟動失敗的坑,使用service iptables stop 關閉防火牆 使用service iptables status确認)

zk.create("/testRootPath/testChildPath3","3".getBytes(), Ids.OPEN_ACL_UNSAFE,CreateMode.EPHEMERAL_SEQUENTIAL);

zookeeper使用和原理探究 (注意linux下防火牆導緻啟動失敗的坑,使用service iptables stop 關閉防火牆 使用service iptables status确認)
zookeeper使用和原理探究 (注意linux下防火牆導緻啟動失敗的坑,使用service iptables stop 關閉防火牆 使用service iptables status确認)

// 建立一個子目錄節點

zookeeper使用和原理探究 (注意linux下防火牆導緻啟動失敗的坑,使用service iptables stop 關閉防火牆 使用service iptables status确認)

zk.create("/testRootPath/testChildPath4","4".getBytes(), Ids.OPEN_ACL_UNSAFE,CreateMode.EPHEMERAL_SEQUENTIAL);

zookeeper使用和原理探究 (注意linux下防火牆導緻啟動失敗的坑,使用service iptables stop 關閉防火牆 使用service iptables status确認)
zookeeper使用和原理探究 (注意linux下防火牆導緻啟動失敗的坑,使用service iptables stop 關閉防火牆 使用service iptables status确認)

System.out.println(zk.getChildren("/testRootPath", false));

zookeeper使用和原理探究 (注意linux下防火牆導緻啟動失敗的坑,使用service iptables stop 關閉防火牆 使用service iptables status确認)

 列印結果:[testChildPath10000000000,

testChildPath20000000001, testChildPath40000000003, testChildPath30000000002]

zookeeper使用和原理探究 (注意linux下防火牆導緻啟動失敗的坑,使用service iptables stop 關閉防火牆 使用service iptables status确認)

zk.create("/testRootPath", "testRootData".getBytes(),Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);

zookeeper使用和原理探究 (注意linux下防火牆導緻啟動失敗的坑,使用service iptables stop 關閉防火牆 使用service iptables status确認)
zookeeper使用和原理探究 (注意linux下防火牆導緻啟動失敗的坑,使用service iptables stop 關閉防火牆 使用service iptables status确認)
zookeeper使用和原理探究 (注意linux下防火牆導緻啟動失敗的坑,使用service iptables stop 關閉防火牆 使用service iptables status确認)

zk.create("/testRootPath/testChildPath1","1".getBytes(), Ids.OPEN_ACL_UNSAFE,CreateMode.EPHEMERAL);

zookeeper使用和原理探究 (注意linux下防火牆導緻啟動失敗的坑,使用service iptables stop 關閉防火牆 使用service iptables status确認)
zookeeper使用和原理探究 (注意linux下防火牆導緻啟動失敗的坑,使用service iptables stop 關閉防火牆 使用service iptables status确認)

zk.create("/testRootPath/testChildPath2","2".getBytes(), Ids.OPEN_ACL_UNSAFE,CreateMode.EPHEMERAL);

zookeeper使用和原理探究 (注意linux下防火牆導緻啟動失敗的坑,使用service iptables stop 關閉防火牆 使用service iptables status确認)
zookeeper使用和原理探究 (注意linux下防火牆導緻啟動失敗的坑,使用service iptables stop 關閉防火牆 使用service iptables status确認)

zk.create("/testRootPath/testChildPath3","3".getBytes(), Ids.OPEN_ACL_UNSAFE,CreateMode.EPHEMERAL);

zookeeper使用和原理探究 (注意linux下防火牆導緻啟動失敗的坑,使用service iptables stop 關閉防火牆 使用service iptables status确認)
zookeeper使用和原理探究 (注意linux下防火牆導緻啟動失敗的坑,使用service iptables stop 關閉防火牆 使用service iptables status确認)
zookeeper使用和原理探究 (注意linux下防火牆導緻啟動失敗的坑,使用service iptables stop 關閉防火牆 使用service iptables status确認)

zk.create("/testRootPath/testChildPath4","4".getBytes(), Ids.OPEN_ACL_UNSAFE,CreateMode.EPHEMERAL);

zookeeper使用和原理探究 (注意linux下防火牆導緻啟動失敗的坑,使用service iptables stop 關閉防火牆 使用service iptables status确認)
zookeeper使用和原理探究 (注意linux下防火牆導緻啟動失敗的坑,使用service iptables stop 關閉防火牆 使用service iptables status确認)
zookeeper使用和原理探究 (注意linux下防火牆導緻啟動失敗的坑,使用service iptables stop 關閉防火牆 使用service iptables status确認)

列印結果:[testChildPath2, testChildPath1, testChildPath4, testChildPath3]

我們預設規定編号最小的為master,是以當我們對/APP1SERVERS節點做監控的時候,得到伺服器清單,隻要所有叢集機器邏輯認為最小編号節點為master,那麼master就被選出,而這個master當機的時候,相應的znode會消失,然後新的伺服器清單就被推送到用戶端,然後每個節點邏輯認為最小編号節點為master,這樣就做到動态master選舉。

zookeeper使用和原理探究 (注意linux下防火牆導緻啟動失敗的坑,使用service iptables stop 關閉防火牆 使用service iptables status确認)

總結 

我們初步使用了一下zookeeper并且嘗試着描述了幾種應用場景的具體實作思路,接下來的文章,我們會嘗試着去探究一下zookeeper的高可用性與leaderElection算法。

繼續閱讀