[TOC] 8.1 集群環境搭建 【操作目的】 由於在ZooKeeper集群中,會有一個Leader伺服器負責管理和協調其他集群伺服器,因此伺服器的數量通常都是單數,例如3,5,7...等,這樣數量為2n+1的伺服器就可以允許最多n台伺服器的失效。 【操作步驟】 本例中,我們仍然使用三個節點搭建部署 ...
目錄
8.1 集群環境搭建
【操作目的】
由於在ZooKeeper集群中,會有一個Leader伺服器負責管理和協調其他集群伺服器,因此伺服器的數量通常都是單數,例如3,5,7...等,這樣數量為2n+1的伺服器就可以允許最多n台伺服器的失效。
【操作步驟】
本例中,我們仍然使用三個節點搭建部署ZooKeeper集群,搭建步驟如下:
1.上傳ZooKeeper安裝文件
在centos01節點中,上傳ZooKeeper安裝文件zookeeper-3.4.9.tar.gz到目錄/opt/softwares/中,並將其解壓到目錄/opt/modules/,解壓命令如下:
tar -zxvf zookeeper-3.4.9.tar.gz -C /opt/modules/
2.編寫配置文件
(1)在ZooKeeper安裝目錄下新建文件夾dataDir,用於存放ZooKeeper數據。
(2)在ZooKeeper安裝目錄下的conf文件夾中新建配置文件zoo.cfg,加入以下內容:
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里寫入的配置信息進行對比,從而獲取當前伺服器的身份信息。
3.拷貝ZooKeeper安裝信息到其它節點
centos01節點安裝完成後,需要拷貝整個ZooKeeper安裝目錄到centos02和centos03節點,命令如下:
scp -r /opt/modules/zookeeper-3.4.9/ hadoop@centos02:/opt/modules/
scp -r /opt/modules/zookeeper-3.4.9/ hadoop@centos03:/opt/modules/
4.修改其它節點配置
拷貝完成後,需要將centos02和centos03節點中的myid文件的值修改為對應的數字,即作出以下操作:
修改centos02節點中的opt/modules/zookeeper-3.4.9/dataDir/myid文件中的值為2。
修改centos03節點中的opt/modules/zookeeper-3.4.9/dataDir/myid文件中的值為3。
5.啟動ZooKeeper
分別進入每個節點的ZooKeeper安裝目錄,執行如下命令:
bin/zkServer.sh start
輸出以下信息代表啟動成功:
ZooKeeper JMX enabled by default
Using config: /usr/local/zookeeper-3.4.9/bin/../conf/zoo.cfg
Starting zookeeper ... STARTED
需要註意的是,每台伺服器都要執行一遍啟動命令,這樣才能使得整個集群啟動起來。
6.查看啟動狀態
分別在各個節點上執行如下命令,查看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,再一次查看啟動狀態,發現一切正常了。
7.測試客戶端連接
在centos01節點上(其它節點也可以),進入ZooKeeper安裝目錄,執行以下命令,可以連接ZooKeeper伺服器,連接成功後可以輸入ZooKeeper的Shell命令進行操作與測試了。
[hadoop@centos01]$ bin/zkCli.sh -server centos01:2181
8.2 命令行操作
【操作目的】
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
8.3 Java API操作
除了可以使用命令行方式對ZooKeeper進行操作外,ZooKeeper還提供了Java API操作介面。下麵對ZooKeeper的常用Java API介面進行介紹。
8.3.1 創建Java工程
在編寫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的編寫。
8.3.2 創建節點
Zookeeper創建節點不支持遞歸調用,即無法在父節點不存在的情況下創建一個子節點,如在/zk0l節點不存在的情況下創建/zk01/ch01節點;並且如果一個節點已經存在,那麼創建同名節點時,會拋出NodeExistsException異常。
下麵我們創建一個節點/ zk001,節點的元數據為“zk001_data”,步驟如下:
1.編寫代碼
在新建的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);
}
}
2.程式解讀
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(臨時自動編號目錄節點,客戶端斷開連接時,這種節點也會被自動刪除)。
3.運行程式
直接在eclipse中右擊運行該程式即可。
8.3.3 添加數據
我們可以通過調用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代表所有版本。
8.3.4 獲取數據
我們可以調用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,不讓程式一次執行到底,從而加入了上方代碼,讓程式一直停留在此處。
8.3.5 刪除節點
我們可以通過調用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則代表刪除所有版本。
原創文章,轉載請註明出處!!