zookeeper概念、應用場景、資料組織、叢集搭建、客戶端操作、Java客戶端、curator
一、zookeeper簡介
1.1 zookeeper簡介
Apache的很多專案以動物來命令,比如Hadoop(大象)、Hive(小蜜蜂)、Pig(豬豬),這些專案都是hadoop生態系統的成員。Hadoop生態系統是為了解決大資料儲存、大資料計算和大資料資料分析的,解決大資料問題的核心思想是分散式,而分散式系統的開發中一個關鍵問題是如何解決資料在不同系統之間的一致性問題。zookeeper顧名思義是動物園管理者,之所以叫動物園管理者是因為zookeeper是用來管理這些分散式系統的。
Hadoop Hive Pig
除了管理這些”動物“以外,zookeeper還管理Apche Hbase、Apche Solr等知名專案。zookeeper不是一個完整的產品,它是一個為分散式應用提供一致性服務的軟體,開發者可以使用zookeeper解決分散式系統資料一致性問題,被稱為分散式系統的基石。
1.2 zookeeper架構
zookeeper採用客戶端-伺服器架構,上圖涉及zookeeper中的五個基本概念:
Server
ZooKeeper總體中的一個節點,為客戶端提供所有的服務。
Clinet
客戶端,分散式應用叢集中的一個節點,從伺服器訪問資訊。
Leader
主節點,負責跟蹤從節點狀態和任務的有效性,並分配任務到從節點。
Flower
從節點,對外提供服務。
Ensemble
伺服器組,也就是平常說的叢集,形成ensemble所需的最小節點數為3。
叢集特性:
客戶端可以連線到每個server,每個server的資料完全相同。
每個follower都和leader有連線,接受leader的資料更新操作。
Server記錄事務日誌和快照到持久儲存。
大多數server可用,整體服務就可用(2n+1個服務允許n個失效)。
通俗一點來理解,Zookeeper是由一個leader,多個follower組成的叢集,叢集中儲存一份相同的資料副本,client無論連線到哪個server,資料都是一致的。
1.3 zookeeper資料組織
zookeeper資料特點:一致、有頭、資料樹。
1.4 zookeeper應用場景
叢集管理:利用臨時節點特性,節點關聯的是機器的主機名、IP地址等相關資訊,叢集單點故障也屬於該範疇。
統一命名:利用節點的唯一性和目錄節點樹結構。
配置管理:節點關聯的是配置資訊。
分散式鎖:節點關聯的是要競爭的資源。
二、 Zookeeper基礎
現在玩轉單機版的安裝配置和基礎操作。
2.1 下載
zookeeper下載地址:
https://archive.apache.org/dist/zookeeper/
解壓縮:
tar -zxvf zookeeper-3.4.12.tar.gz
cd zookeeper-3.4.12
cp conf/zoo_sample.cfg conf/zoo.cfg
zoo.cfg為zookeeper的配置檔案,編輯內容如下:
syncLimit=5
dataDir=/usr/local/zookeeper/data
dataLogDir=/usr/local/zookeeper/logs
clientPort=2181
syncLimit: Leader伺服器與follower伺服器之間資訊同步允許的最大時間間隔,如果超過次間隔,預設follower伺服器與leader伺服器之間斷開連結
dataDir: 儲存zookeeper資料的目錄
dataLogDir:儲存zookeeper日誌路徑,當此配置不存在時預設路徑與dataDir一致
clientPort:客戶端訪問zookeeper時經過伺服器端時的埠號
2.2 啟動
執行啟動命令, 看到如下提示說明zookeeper啟動成功:
➜ ~ sudo zkServer.sh start
Password:
ZooKeeper JMX enabled by default
Using config: /usr/local/zookeeper/bin/../conf/zoo.cfg
Starting zookeeper ... STARTED
2.3 客戶端操作
Zookeeper提供了一個客戶端指令碼zkCli.sh(Windows平臺是zkCli.cmd)用於和伺服器互動,進入Zookeeper的bin目錄,執行連線伺服器命令:
./bin/zkCli.sh
1
看到如下提示說明連線成功:
......
WATCHER::
WatchedEvent state:SyncConnected type:None path:null
上述命令預設連線本地的Zookeeper,如果需要連線指定的Zookeeper,命令格式如下:
./bin/zkCli.sh -server ip:port
1
成功連線Zookeeper伺服器以後,就可以執行資料CRUD操作。
輸入help檢視Zookeeper的一些命令的用法提示:
[zk: localhost:2181(CONNECTED) 2] help
ZooKeeper -server host:port cmd args
stat path [watch]
set path data [version]
ls path [watch]
delquota [-n|-b] path
ls2 path [watch]
setAcl path acl
setquota -n|-b val path
history
redo cmdno
printwatches on|off
delete path [version]
sync path
listquota path
rmr path
get path [watch]
create [-s][-e] path data acl
addauth scheme auth
quit
getAcl path
close
connect host:port
2.3.1 建立
建立Zookeeper節點使用create命令,命令格式如下:
create [-s][-e] path data acl
1
-s或-e分別指定節點特性,順序或臨時節點,若不指定,則表示持久節點;acl用來進行許可權控制。
例如,建立名為/zk-node 的節點,值為abc,不加引數預設建立永久節點。
[zk: localhost:2181(CONNECTED) 0] create /zk-node abc
Created /zk-node
建立順序節點:
[zk: localhost:2181(CONNECTED) 4] create -s /zk-order 123
Created /zk-order0000000190
建立臨時節點:
[zk: localhost:2181(CONNECTED) 2] create -e /zk-tmp 123
Created /zk-tmp
臨時節點會在客戶端會話結束後消失,使用quit命令退出後再次連線,/zk-tmp節點會消失。
2.3.2 讀取
ls: 列出路徑下的所有節點
例如,列出根目錄下的所有節點:
ls /
Ls2: 列出子節點的同時列出節點的狀態資訊:
[zk: localhost:2181(CONNECTED) 11] ls2 /
[cluster, zk-node, brokers, zookeeper, zk-order0000000190, admin]
cZxid = 0x0
ctime = Thu Jan 01 08:00:00 CST 1970
mZxid = 0x0
mtime = Thu Jan 01 08:00:00 CST 1970
pZxid = 0x17c5
cversion = 371
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 0
numChildren = 15
get:獲取某一個節點的值:
[zk: localhost:2181(CONNECTED) 5] get /zk-node
123
cZxid = 0x17c0
ctime = Mon Oct 29 20:12:45 CST 2018
mZxid = 0x17c0
mtime = Mon Oct 29 20:12:45 CST 2018
pZxid = 0x17c0
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 3
numChildren = 0
2.3.3 更新
更新使用set命令,格式如下:
set path data [version]
1
例如:/zk-node的值為123,更新為abc:
set /zk-node abc
1
2.3.4 刪除
刪除Zookeeper上的節點使用delete命令,格式如下:
delete path [version]
1
如果節點下還有子節點,不能直接刪除:
[zk: localhost:2181(CONNECTED) 15] create /a a
Created /a
[zk: localhost:2181(CONNECTED) 16] create /a/b b
Created /a/b
[zk: localhost:2181(CONNECTED) 17] delete /a
Node not empty: /a
[zk: localhost:2181(CONNECTED) 19] delete /a/b
2.4 使用zkui
下載zkui的原始碼: https://github.com/DeemOpen/zkui
編譯: mvn clean install
拷貝zookeeper配置檔案到target目錄下,和打包出來的jar檔案同級
啟動zkUi : sudo java -jar target/zkui-2.0-SNAPSHOT-jar-with-dependencies.jar
訪問9090埠: http://ip:9090/
使用者名稱和密碼在zkui/config.cfg中
三、Zookeeper叢集
部署Zookeeper時例項的個數為一般為2N+1個,理由是Zookeeper的選舉、增刪改操作都需要半數以上伺服器通過。
準備三臺網路互通的centos伺服器或者虛擬機器,ip分別為:192.168.255.129、192.168.255.132、192.168.255.134。
下載Zookeeper安裝包並解壓到指定目錄,複製一份配置檔案:
cp conf/zoo_sample.cfg conf/zoo.cfg
1
zoo.cfg在三臺伺服器上的配置是一樣的:
tickTime=2000
initLimit=10
syncLimit=5
dataDir=/usr/local/zookeeper/zookeeper-3.4.12/data
dataLogDir=/usr/local/ www.dfgjyl.cn zookeeper/zookeeper-3.4.12/logs
clientPort=2181
server.1=192.168.255.129:2888:3888
server.2=192.168.255.132:2888:3888
server.3=192.168.255.134:2888:3888
叢集中的每臺ZK server都會有一個用於惟一標識自己的id,myid檔案儲存在dataDir目錄中,指定了當前server的server id。在三臺伺服器上的dataDir目錄下(/usr/local/zookeeper/zookeeper-3.4.12/data)新建myid檔案並寫入一個數字,確保每個節點數字都不一樣:
touch myid
echo 1 > myid
上述配置完成以後啟動Zookeeper:
./bin/zkServer.sh start
1
如果一切順利,Zookeeper啟動成功,執行jps檢視程序命令,可以看到QuorumPeerMain:
[
11860 Jps
2300 QuorumPeerMain
檢視叢集中各節點的狀態:
[[email protected] zookeeper-3.4.12]# ./bin/zkServer.sh status
ZooKeeper JMX enabled by default
Using config: /usr/local/zookeeper/zookeeper-3.4.12/bin/../conf/zoo.cfg
Mode: leader
三臺機器上Zookeeper的執行狀態以及角色狀態如上圖所示。
四、Java客戶端
4.1 maven座標
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper<www.michenggw.com /artifactId>
<version>3.4.12</version>
<type>pom</type>
</dependency>
4.2 建立會話
AbstractZkClient裡面封裝了連線和關閉Zookeeper的方法.
import org.apache.zookeeper.WatchedEvent;
import org.apache.www.yigouyule2.cn zookeeper.Watcher;
import org.apache.zookeeper.Watcher.Event.EventType;
import org.apache.zookeeper.Watcher.Event.KeeperState;
import org.apache.zookeeper.ZooKeeper;
import java.io.IOException;
import java.util.concurrent.CountDownLatch;
public class AbstractZkClient implements Watcher {
private static final int SESSION_TIME = 5000;
private static CountDownLatch countDownLatch = new CountDownLatch(1);
protected static ZooKeeper www.mcyllpt.com zooKeeper;
public void connect(String hosts) throws IOException, InterruptedException {
zooKeeper = new ZooKeeper(hosts, SESSION_TIME, new ZkClient());
countDownLatch.await(www.mhylpt.com);
}
public void close() throws InterruptedException {
zooKeeper.close();
}
@Override
public void process(WatchedEvent event) {
try {
if (KeeperState.SyncConnected == event.getState()) {
if (Event.EventType.None == event.getType() &&
null == event.getPath()) {
countDownLatch.countDown();
} else if (EventType.NodeCreated == event.getType()) {
System.out.println("("+event.getPath()+")Created");
this.zooKeeper.exists(event.getPath(), true);
} else if (EventType.NodeDeleted==event.getType()) {
System.out.println("Node("+event.getPath()+")Deleted");
this.zooKeeper.exists(event.getPath(), true);
} else if (EventType.NodeDataChanged==event.getType()) {
System.out.println("Node("+event.getPath()
+")DataChanged");
this.zooKeeper.exists(event.getPath(), true);
}
}
} catch (Exception e) {
e.printStackTrace();
4.3 增刪改查
package zookeeper;
import org.apache.log4j.Logger;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.ZooDefs.Ids;
import java.util.List;
public class ZkClient extends AbstractZkClient {
static String HOSTS = "192.168.255.132:2181,192.168.255.129:2181,192.168.255.134:2181";
public static final Logger logger = Logger.getLogger(ZkClient.class);
/**
* 建立節點
*
* @param path 節點路徑
* @param data 節點value
* @throws KeeperException
* @throws InterruptedException
*/
public void create(String path, byte[] data) throws KeeperException, InterruptedException {
this.zooKeeper.create(path, data, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
}
/**
* 讀取節點資料
*
* @param path 節點路徑
* @throws KeeperException
* @throws InterruptedException
*/
public void getChild(String path) throws KeeperException, InterruptedException {
try {
List<String> list = this.zooKeeper.getChildren(path, true);
if (list.isEmpty()) {
logger.info(path + "中沒有節點");
} else {
logger.info(path + "中存在節點");
for (String child : list) {
logger.info("節點為:" + child);
}
}
} catch (KeeperException.NoNodeException e) {
logger.error(e.getStackTrace());
}
}
/**
* 讀取節點資料
*
* @param path 節點路徑
* @return
* @throws KeeperException
* @throws InterruptedException
*/
public byte[] getData(String path) throws KeeperException, InterruptedException {
return this.zooKeeper.getData(path, true, null);
}
/**
* 刪除節點
*
* @param path 節點路徑
* @param version 版本號
* @return
*/
public void deleteNode(String path, int version) throws KeeperException, InterruptedException {
this.zooKeeper.delete(path, version);
}
/**
* 判斷節點是否存在
*
* @param path
*/
public void existNode(String path) throws KeeperException, InterruptedException {
this.zooKeeper.exists(path, true);
}
/**
* 更新節點
*
* @param path
* @param data
* @throws KeeperException
* @throws InterruptedException
*/
public void updateNode(String path, byte[] data) throws KeeperException, InterruptedException {
this.zooKeeper.setData(path, data, -1);
}
public static void main(String[] args) throws Exception {
ZkClient zkClient = new ZkClient();
zkClient.connect(HOSTS);
zkClient.getChild("/");
zkClient.existNode("/zk-book");
zkClient.updateNode("/zk-book", "667GG".getBytes());
五、Curator
Curator是Netflix公司開源的一個Zookeeper客戶端,與Zookeeper提供的原生客戶端相比,Curator的抽象層次更高,簡化了Zookeeper客戶端的開發量。Curator官網: http://curator.apache.org/getting-started.html
5.1 版本說明
curator版本 Zookeeper版本
2.x.x 3.4.x、3.5.x
3.x.x 3.5.x
Zookeeper版本為3.4.12,使用2.x.x的curator:
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-framework</artifactId>
<version>2.12.0<version>
</dependency>
如果curator的版本比Zookeeper版本高,會丟擲如下異常:
Exception in thread "main" org.apache.zookeeper.KeeperException$UnimplementedException: KeeperErrorCode = Unimplemented for ...
1
5.2 建立會話
static String HOSTS = "192.168.255.132:2181,192.168.255.129:2181=";
static CuratorFramework client = CuratorFrameworkFactory.builder()
.connectString(HOSTS)
.sessionTimeoutMs(5000)
.retryPolicy(new ExponentialBackoffRetry(1000,3))
.namespace("pp")
.build();
5.3 基本操作
建立持久節點:
String path = "/a/b";
client.create()
.creatingParentsIfNeeded()
.withMode(CreateMode.PERSISTENT)
.forPath(path,"ab".getBytes());
建立臨時節點:
client.create()
.creatingParentsIfNeeded()
.withMode(CreateMode.EPHEMERAL)
.forPath(path,"temp".getBytes());
獲取節點資料:
client.getData().forPath("/a/b");
1
更新節點資料:
client.setData().forPath("/a/b", "newValue".getBytes());
1
刪除節點:
client.delete().forPath("/a/b");
1
六、總結
上述內容介紹簡介了zookeeper概念、應用場景、資料組織、客戶端操作、Java客戶端以及zookeeper的客戶端框架curator等基礎內容,關於zookeeper學習的進階可以從底層實現原理(paxos演算法、zab協議等理論基礎)和實際應用(實現分散式鎖、Leader選舉等應用場景)兩大方面繼續深入學習。