目錄java
【操做目的】
因爲在ZooKeeper集羣中,會有一個Leader服務器負責管理和協調其餘集羣服務器,所以服務器的數量一般都是單數,例如3,5,7...等,這樣數量爲2n+1的服務器就能夠容許最多n臺服務器的失效。
【操做步驟】
本例中,咱們仍然使用三個節點搭建部署ZooKeeper集羣,搭建步驟以下:node
在centos01節點中,上傳ZooKeeper安裝文件zookeeper-3.4.9.tar.gz到目錄/opt/softwares/中,並將其解壓到目錄/opt/modules/,解壓命令以下:shell
tar -zxvf zookeeper-3.4.9.tar.gz -C /opt/modules/
(1)在ZooKeeper安裝目錄下新建文件夾dataDir,用於存放ZooKeeper數據。
(2)在ZooKeeper安裝目錄下的conf文件夾中新建配置文件zoo.cfg,加入如下內容:apache
tickTime=2000 initLimit=10 syncLimit=5 dataDir=/opt/modules/zookeeper-3.4.9/dataDir clientPort=2181 server.1=centos01:2888:3888 server.2=centos02:2888:3888 server.3=centos03:2888:3888
上述參數說明以下:
initLimit:集羣中的Follower服務器初始化鏈接Leader服務器時能等待的最大心跳數(鏈接超時時長)。默認爲10,即若是通過10個心跳以後Follower服務器仍然沒有收到Leader服務器的返回信息,則鏈接失敗。本例中該參數值爲5,參數tickTime爲2000,則鏈接超時時長爲52000=10秒(即tickTimeinitLimit=10秒)。
syncLimit:集羣中的Follower服務器與Leader服務器之間發送消息以及請求/應答時所能等待的最多心跳數。本例中,tickTime的值爲2,時長爲2*2000=4秒。
server.id:標識不一樣的ZooKeeper服務器。ZooKeeper能夠從「server.id=host:port1:port2」中讀取相關信息。其中,id值必須在整個集羣中是惟一的,且大小在1到255之間;host是服務器的名稱或IP地址;第一個端口(port1)是Leader端口,即該服務器做爲Leader時供Follower鏈接的端口;第二個端口(port2)是選舉端口,即選舉Leader服務器時供其它Follower鏈接的端口。
dataDir:ZooKeeper保存數據的目錄。
clientPort:客戶端鏈接ZooKeeper服務器的端口,ZooKeeper會監聽這個端口,接收客戶端的請求。
(3)在配置文件zoo.cfg中的參數dataDir指定的目錄下(此處爲ZooKeeper安裝目錄下的dataDir文件夾)新建一個名爲myid的文件,這個文件僅包含一行內容,即當前服務器的id值,與參數server.id中的id值相同。本例中,當前服務器(centos01)的id值爲1,則應該在myid文件中寫入數字1。ZooKeeper啓動時會讀取該文件,將其中的數據與zoo.cfg裏寫入的配置信息進行對比,從而獲取當前服務器的身份信息。centos
centos01節點安裝完成後,須要拷貝整個ZooKeeper安裝目錄到centos02和centos03節點,命令以下:api
scp -r /opt/modules/zookeeper-3.4.9/ hadoop@centos02:/opt/modules/ scp -r /opt/modules/zookeeper-3.4.9/ hadoop@centos03:/opt/modules/
拷貝完成後,須要將centos02和centos03節點中的myid文件的值修改成對應的數字,即做出如下操做:
修改centos02節點中的opt/modules/zookeeper-3.4.9/dataDir/myid文件中的值爲2。
修改centos03節點中的opt/modules/zookeeper-3.4.9/dataDir/myid文件中的值爲3。數組
分別進入每一個節點的ZooKeeper安裝目錄,執行以下命令:服務器
bin/zkServer.sh start
輸出如下信息表明啓動成功:
ZooKeeper JMX enabled by default
Using config: /usr/local/zookeeper-3.4.9/bin/../conf/zoo.cfg
Starting zookeeper ... STARTEDeclipse
須要注意的是,每臺服務器都要執行一遍啓動命令,這樣才能使得整個集羣啓動起來。ide
分別在各個節點上執行以下命令,查看ZooKeeper服務的狀態:
bin/zkServer.sh status
在centos01節點上查看服務狀態,輸出瞭如下信息:
ZooKeeper JMX enabled by default
Using config: /usr/local/zookeeper-3.4.9/bin/../conf/zoo.cfg
Mode: follower
在centos02服務器上查看服務狀態,輸出瞭如下信息:
ZooKeeper JMX enabled by default
Using config: /usr/local/zookeeper-3.4.9/bin/../conf/zoo.cfg
Mode: follower
在centos03服務器上查看服務狀態,輸出瞭如下信息:
ZooKeeper JMX enabled by default
Using config: /usr/local/zookeeper-3.4.9/bin/../conf/zoo.cfg
Mode: leader
因而可知,本例中centos03服務器上的ZooKeeper服務爲Leader,其他兩個ZooKeeper服務爲Follower。
若是在查看啓動狀態時輸出如下信息,說明ZooKeeper集羣啓動不成功,出現錯誤。
Error contacting service. It is probably not running.
此時須要修改bin/zkEvn.sh文件中的如下內容,將錯誤信息輸出到日誌文件。
if [ "x${ZOO_LOG4J_PROP}" = "x" ] then ZOO_LOG4J_PROP="INFO,CONSOLE" fi
將上述內容中的CONSOLE改成ROLLINGFILE,修改後的內容以下:
if [ "x${ZOO_LOG4J_PROP}" = "x" ] then ZOO_LOG4J_PROP="INFO,ROLLINGFILE" fi
修改後,從新啓動ZooKeeper集羣,查看在ZooKeeper安裝目錄下生成的日誌文件zookeeper.log,發現報如下錯誤:
java.net.NoRouteToHostException: 沒有到主機的路由。
產生上述錯誤的緣由是,系統沒有關閉防火牆,致使ZooKeeper集羣間鏈接不成功。所以須要關閉系統防火牆(爲了防止出錯,在最初的集羣環境配置的時候能夠直接將防火牆關閉),CentOS7關閉防火牆的命令以下:
systemctl stop firewalld.service systemctl disable firewalld.service
關閉各節點的防火牆後,從新啓動ZooKeeper,再一次查看啓動狀態,發現一切正常了。
在centos01節點上(其它節點也能夠),進入ZooKeeper安裝目錄,執行如下命令,能夠鏈接ZooKeeper服務器,鏈接成功後能夠輸入ZooKeeper的Shell命令進行操做與測試了。
[hadoop@centos01]$ bin/zkCli.sh -server centos01:2181
【操做目的】
ZooKeeper的命令行工具相似於Shell。當ZooKeeper服務啓動之後,能夠在其中一臺運行ZooKeeper服務的服務器中輸入如下命令(須要進入ZooKeeper安裝目錄),啓動一個客戶端,鏈接到ZooKeeper集羣:
[hadoop@centos01]$ bin/zkCli.sh -server centos01:2181
鏈接成功後,系統會輸出ZooKeeper的運行環境及配置信息,並在屏幕輸出「Welcome to ZooKeeper」等歡迎信息。以後就可使用ZooKeeper命令行工具了。
【操做步驟】
如下是ZooKeeper命令行工具的一些簡單操做示例:
(1)使用ls命令,能夠查看當前ZooKeeper中所包含的內容:
[zk: centos01:2181(CONNECTED) 4] ls / [zookeeper]
能夠看到,當前有一個名稱爲zookeeper的Znode節點。
(2)使用create命令,能夠建立一個新的Znode節點。例如,建立一個名爲「zk」的Znode以及在它上面存放的元數據字符串「myData」,命令及輸出信息以下:
[zk: centos01:2181(CONNECTED) 2] create /zk "myData" Created /zk
(3)使用get命令,能夠查看某個Znode的詳細信息及其包含的元數據字符串。例如,查看Znode節點/zk的詳細信息,命令及輸出信息以下:
[zk: centos01:2181(CONNECTED) 6] get /zk myData cZxid = 0x800000002 ctime = Thu Mar 22 10:12:11 CST 2018 mZxid = 0x800000002 mtime = Thu Mar 22 10:12:11 CST 2018 pZxid = 0x800000002 cversion = 0 dataVersion = 0 aclVersion = 0 ephemeralOwner = 0x0 dataLength = 6 numChildren = 0
(4)使用set命令,能夠修改Znode節點的元數據字符串。例如,將Znode節點/zk所關聯的字符串修改成「myDataUpdate」,命令及輸出信息以下:
[zk: centos01:2181(CONNECTED) 10] set /zk "myDataUpdate" cZxid = 0x800000002 ctime = Thu Mar 22 10:12:11 CST 2018 mZxid = 0x800000005 mtime = Thu Mar 22 10:18:19 CST 2018 pZxid = 0x800000002 cversion = 0 dataVersion = 3 aclVersion = 0 ephemeralOwner = 0x0 dataLength = 12 numChildren = 0
(5)使用delete命令,能夠將某個Znode節點刪除。例如,刪除上面建立的Znode節點/zk,命令以下:
[zk: centos01:2181(CONNECTED) 11] delete /zk
使用ZooKeeper命令行工具也能夠建立有層次的目錄。例如,在/zk節點目錄下建立新的目錄node1,並關聯其元數據爲「nodeData」,命令及輸出信息以下:
[zk: centos01:2181(CONNECTED) 18] create /zk/node1 "nodeData" Created /zk/node1
除了可使用命令行方式對ZooKeeper進行操做外,ZooKeeper還提供了Java API操做接口。下面對ZooKeeper的經常使用Java API接口進行介紹。
在編寫Java API以前,首先須要新建一個ZooKeeper項目。ZooKeeper項目的結構與普通的Java項目同樣,只是依賴的jar包不一樣。
在eclipse中新建一個Maven項目zk_demo(Maven項目的搭建此處不作過多講解),而後在該項目的pom.xml文件中添加如下代碼,以引入ZooKeeper的Java API依賴包:
<dependency> <groupId>org.apache.zookeeper</groupId> <artifactId>zookeeper</artifactId> <version>3.4.9</version> </dependency>
配置好pom.xml後,便可進行ZooKeeper Java API的編寫。
Zookeeper建立節點不支持遞歸調用,即沒法在父節點不存在的狀況下建立一個子節點,如在/zk0l節點不存在的狀況下建立/zk01/ch01節點;而且若是一個節點已經存在,那麼建立同名節點時,會拋出NodeExistsException異常。
下面咱們建立一個節點/ zk001,節點的元數據爲「zk001_data」,步驟以下:
在新建的zk_demo項目中新建Java類CreatePath.java,完整代碼以下所示:
import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.ZooDefs.Ids; public class CreatePath { public static void main(String[] args) throws Exception { String connectStr="192.168.170.128:2181,192.168.170.129:2181,192.168.170.130:2181"; //參數1:服務器鏈接字符串 //參數2:鏈接超時時間 //參數3:觀察者對象(回調函數) ZooKeeper zk = new ZooKeeper(connectStr, 3000, null); /* 1.CreateMode 取值 * PERSISTENT:持久化節點 * PERSISTENT_SEQUENTIAL:順序自動編號的目錄節點 * EPHEMERAL:臨時目錄節點,客戶端斷開鏈接時,這種節點會被自動刪除 * EPHEMERAL_SEQUENTIAL:臨時自動編號節點 * */ String path=zk.create("/zk001", "zk001_data".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); System.out.println(path); } }
ZooKeeper zk = new ZooKeeper(connectStr, 3000, null);
新建一個ZooKeeper對象,傳入三個參數,第一個參數爲以逗號分隔的服務器鏈接字符串,格式:「host:端口」,這裏須要把全部的ZooKeeper服務器的地址都寫上,而不是隻寫其中一臺。ZooKeeper客戶端對象將從鏈接串中挑選任意一個服務器進行鏈接,若是鏈接失敗,將嘗試鏈接另一個服務器,直到創建鏈接。這樣的好處是能保證ZooKeeper服務的高可靠性,防止由於其中一臺機器宕機而致使鏈接失敗。第二個參數爲鏈接超時時間,這裏是3秒。第三個參數爲觀察者對象,鏈接成功後會調用觀察者對象中的回調函數,這裏傳入null便可。
String path=zk.create("/zk001", "zk001_data".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
調用ZooKeeper對象的建立節點函數,返回建立的節點路徑,並須要傳入四個參數。第一個參數爲節點名稱。第二個參數爲節點數據,須要轉成字節數組。第三個參數爲權限控制,這裏使用ZooKeeper自帶的徹底開放權限Ids.OPEN_ACL_UNSAFE。第四個參數爲建立模式,它是一個枚舉類型,共有四個取值,PERSISTENT(持久化,這個目錄節點存儲的數據不會丟失 ,即客戶端失去鏈接以後不會被自動刪除)、PERSISTENT_SEQUENTIAL(順序自動編號的目錄節點,這種目錄節點在命名上會根據當前已經存在的節點數自動加 1,而後將已經成功建立的目錄節點名返回給客戶端)、EPHEMERAL(臨時目錄節點,客戶端斷開鏈接時,這種節點會被自動刪除)、EPHEMERAL_SEQUENTIAL(臨時自動編號目錄節點,客戶端斷開鏈接時,這種節點也會被自動刪除)。
直接在eclipse中右擊運行該程序便可。
咱們能夠經過調用ZooKeeper對象的setData()函數給節點添加數據,示例代碼以下:
@Test public void setNodeData() throws Exception { String connectStr="192.168.170.128:2181,192.168.170.129:2181,192.168.170.130:2181"; ZooKeeper zk = new ZooKeeper(connectStr, 3000, null); Stat stat = zk.setData("/zk002", "zk002_data2".getBytes(), -1); System.out.println(stat.getVersion()); }
代碼解析:
Stat stat = zk.setData("/zk002", "zk002_data2".getBytes(), -1);
setData函數的第一個參數爲節點路徑。第二個參數爲須要添加的數據,並轉成字節數組。第三個參數爲版本號,-1表明全部版本。
咱們能夠調用ZooKeeper對象的getData()函數,得到指定節點的數據,示例代碼以下:
@Test public void getNodeData() throws Exception { String connectStr="192.168.170.128:2181,192.168.170.129:2181,192.168.170.130:2181"; ZooKeeper zk = new ZooKeeper(connectStr, 3000, null); Stat stat=new Stat(); //返回指定路徑上的節點數據和節點狀態,節點的狀態會放入stat對象中 byte[] bytes=zk.getData("/zk002", null, stat); System.out.println(new String(bytes)); }
上述代碼獲取了節點/zk002的數據,並且轉成了字符串進行了輸出,並將節點/zk002的狀態放入了對象stat中。如需查看狀態信息,能夠從對象stat中進行輸出查看。
能夠看到,在getData函數的第二個參數,傳入的是null,也能夠指定一個觀察者對象watcher,對節點數據的變化進行監聽,一旦有數據改變,就會觸發watcher指定的回調函數。咱們對上方代碼加入觀察者後,示例代碼以下:
@Test public void getNodeDataWatch() throws Exception { String connectStr="192.168.170.128:2181,192.168.170.129:2181,192.168.170.130:2181"; ZooKeeper zk = new ZooKeeper(connectStr, 3000, null); Stat stat=new Stat(); //返回指定路徑上的節點數據和節點狀態,節點的狀態會放入stat對象中 byte[] bytes=zk.getData("/zk002", new Watcher(){ @Override public void process(WatchedEvent event) { System.out.println(event.getType()); } }, stat); System.out.println(new String(bytes)); //改變節點數據,觸發watch zk.setData("/zk002", "zk002_data_testwatch".getBytes(), -1); //爲了驗證是否觸發了watch,不讓程序結束 while(true){ Thread.sleep(3000); } }
代碼分析:
public void process(WatchedEvent event) { System.out.println(event.getType()); }
process 方法是 Watcher 接口中的一個回調方法,當 ZooKeeper 向客戶端發送一個 Watcher 事件通知時,客戶端就會對相應的 process 方法進行回調,從而實現對事件的處理。
process 方法包含 WatcherEvent 類型的參數,WatchedEvent 包含了每個事件的三個基本屬性:通知狀態(KeeperState)、事件類型(EventType)和節點路徑(Path),ZooKeeper 使用 WatchedEvent 對象來封裝服務端事件並傳遞給 Watcher,從而方便回調方法 process 對服務端事件進行處理。
上述代碼經過System.out.println(event.getType());輸出服務端的事件類型,輸出結果爲NodeDataChanged。從結果單詞的含義可知,節點數據被改變了。
while(true){ Thread.sleep(3000); }
爲了可以更好的驗證是否觸發了watch,不讓程序一次執行到底,從而加入了上方代碼,讓程序一直停留在此處。
咱們能夠經過調用ZooKeeper對象的delete()函數,對指定路徑節點進行刪除。示例代碼以下:
@Test public void deletePath() throws Exception{ String connectStr="192.168.170.128:2181,192.168.170.129:2181,192.168.170.130:2181"; ZooKeeper zk = new ZooKeeper(connectStr, 3000, null); //刪除節點 zk.delete("/zk001", -1); }
上述代碼中,delete函數須要傳入兩個參數,第一個參數爲須要刪除的節點路徑。第二個參數爲節點版本,若是是-1則表明刪除全部版本。
原創文章,轉載請註明出處!!