1 相關(guān)概念
Zookeeper是Hadoop生態(tài)系統(tǒng)中分布式的服務(wù)管理框架,負責存儲和管理集群中的公共數(shù)據(jù)如配置信息等,并且對節(jié)點進行注冊和通知管理。它具有如下幾個特點:
- 集群由一個領(lǐng)導者(Leader),多個跟隨者(Follower)組成
- 集群中只要有半數(shù)以上節(jié)點存活,Zookeeper集群就能正常服務(wù)。所以Zookeeper適合安裝奇數(shù)臺服務(wù)器。
- 全局數(shù)據(jù)一致:每個Server保存一份相同的數(shù)據(jù)副本,Client無論連接到哪個Server,數(shù)據(jù)都是一致的。
- 按照請求順序執(zhí)行更新,同一個Client的按發(fā)送順序依次執(zhí)行。
- 更新原子性,一次數(shù)據(jù)更新要么成功,要么失敗。
- 實時性,在一定時間范圍內(nèi),Client能讀到最新數(shù)據(jù)
Zookeeper常應用于以下幾個場景:
- 統(tǒng)一命名服務(wù):在分布式環(huán)境下,經(jīng)常需要對應用/服務(wù)進行統(tǒng)一命名,便于識別
- 集群狀態(tài)管理:將節(jié)點信息寫入ZooKeeper上的一個ZNode,實時監(jiān)控節(jié)點狀態(tài)變化
- 統(tǒng)一配置管理:將集群中應用的配置文件信息交由Zookeeper管理,一旦發(fā)生修改會通知各個客戶端,從而保證所有結(jié)點的配置信息一致
- 節(jié)點上下線:服務(wù)器節(jié)點上下線時通知Zookeeper,客戶端實時掌握服務(wù)器在線列表
- 軟負載均衡:在Zookeeper中記錄每臺服務(wù)器的訪問數(shù),讓訪問數(shù)最少的服務(wù)器去處理最新的客戶端請求
1.1 選舉機制
zookeeper在首次啟動和運行中進行節(jié)點選舉的機制不同。
首次啟動
在集群啟動之后zookeeper會進行Leader選舉,主要策略是選舉服務(wù)器ID較大的節(jié)點,得票數(shù)超過一半就會當選。
假設(shè)有五臺節(jié)點依次啟動:
- 服務(wù)器1啟 動,發(fā)起一次選舉。投自己一票。此時服務(wù)器1票數(shù)一票,不夠半數(shù)以上(3票),選舉無法完成,服務(wù)器1狀態(tài)保持LOOKING
- 服務(wù)器2啟動發(fā)起一次選舉。服務(wù)器1和2分別投自己一票并交換選票信息:此時服務(wù)器1發(fā)現(xiàn)服務(wù)器2的myid比自己目前投票推舉的(服務(wù)器1) 大,更改選票為推舉服務(wù)器2。此時服務(wù)器1票數(shù)0票,服務(wù)器2票數(shù)2票,沒有半數(shù)以上結(jié)果,選舉無法完成,服務(wù)器1,2狀態(tài)保持LOOKING
- 服務(wù)器3啟動,發(fā)起一次選舉。同理,服務(wù)器1和2都會更改選票為服務(wù)器3。此次投票結(jié)果:服務(wù)器1為0票,服務(wù)器2為0票,服務(wù)器3為3票,超過半數(shù),服務(wù)器3當選Leader。服務(wù)器1,2更改狀態(tài)為FOLLOWING,服務(wù)器3更改狀態(tài)為LEADING;
- 服務(wù)器4啟動,發(fā)起一次選舉。此時服務(wù)器1,2,3已經(jīng)不是LOOKING狀態(tài),不會更改選票信息。交換選票信息結(jié)果:服務(wù)器3為3票,服務(wù)器4為 1票。此時服務(wù)器4服從多數(shù),更改選票信息為服務(wù)器3,并更改狀態(tài)為FOLLOWING;
- 服務(wù)器5啟動,同4一樣成為FOLLOWING。
運行中
如果某節(jié)點在運行中和Leader失去連接,此時有兩種情況:當前節(jié)點失聯(lián)或Leader失聯(lián)。
如果當前節(jié)點失聯(lián),leader仍存在,雖然當前節(jié)點會試圖去選舉Leader,但與其他節(jié)點交換信息時會被告知當前服務(wù)器的Leader信息,然后重新和Leader機器建立連接,并進行狀態(tài)同步即可。
如果是Leader失聯(lián),則會進行重新選舉,重新選舉時會依次比較Epoch、ZXID和SID,如果Epoch較大則當選為Leader,若相等則比較ZXID、最后比較SID。其中
- Epoch是每個Leader任期的代號,代號越大代表其版本越新
- ZXID:事務(wù)ID。用來標識一次服務(wù)器狀態(tài)的變更,每次修改 ZooKeeper 狀態(tài)都會按順序產(chǎn)生一個事務(wù) ID,越大代表記錄了最新的數(shù)據(jù)更改
- SID:服務(wù)器ID。用來唯一標識一臺ZooKeeper集群中的機器,不能重復,和myid一致
1.2 數(shù)據(jù)寫入
當客戶端發(fā)起寫數(shù)據(jù)請求時,一般有兩種情況:請求到達Leader節(jié)點或Follower節(jié)點
- 如果請求直接到達Leader節(jié)點,那么leader除了自己寫入外還會向Follower發(fā)送寫數(shù)據(jù)請求,當集群中一般節(jié)點完成寫入后就會向客戶端返回響應。之后再向其他Follower發(fā)送寫數(shù)據(jù)請求,完成數(shù)據(jù)同步。
- 如果請求先到達Follower,會將請求轉(zhuǎn)發(fā)到Leader,然后由Leader向各節(jié)點發(fā)送寫請求,半數(shù)完成后將響應傳達給接收請求的那個Follower,由它向客戶端返回完成的響應。
2 使用
2.1 安裝配置
從官網(wǎng)https://zookeeper.apache.org/下載安裝包,解壓到/opt/module目錄下后修改文件夾名稱,
tar -zxvf apache-zookeeper-3.5.7-bin.tar.gz -C /opt/module/
cd /opt/module
mv apache-zookeeper-3.5.7-bin/ zookeeper
在目錄下新建zkData文件夾用于存放zookeeper的數(shù)據(jù),在其中新建文件myid。這個文件是用于保存標識節(jié)點的唯一編號,zookeeper通過讀取該文件中的編號來區(qū)分該節(jié)點是哪個,這里輸入其節(jié)點編號2
cd zookeeper
mkdir zkData
vim myid
2
將配置文件zookeeper/conf/zoo_sample.cfg 重命名為 zoo.cfg,在其中修改數(shù)據(jù)存儲路徑配置,并在文件末尾添加集群節(jié)點信息
dataDir=/opt/module/zookeeper/zkData
...
#######################cluster##########################
server.2=hadoop102:2888:3888
server.3=hadoop103:2888:3888
server.4=hadoop104:2888:3888
其他配置參數(shù)的含義為
- tickTime = 2000:通信心跳時間,Zookeeper服務(wù)器與客戶端心跳時間,單位毫秒
- initLimit = 10:Leader和Follower初始連接時的最多心跳數(shù)
- syncLimit = 5:Leader和Follower同步時最多心跳次數(shù),超過5次心跳無響應會從列表刪掉Follower
- dataDir:保存Zookeeper中數(shù)據(jù)的目錄
- clientPort = 2181:客戶端連接端口
在節(jié)點信息server.2=hadoop102:2888:3888中,
- 數(shù)字2代表節(jié)點的ID值,和zkData/myid文件中的節(jié)點編號對應,Zookeeper啟動時會把配置文件和myid中的編號進行比較從而判斷集群節(jié)點的啟動情況。
- hadoop102代表節(jié)點的主機地址;
- 2888服務(wù)器 Follower 與集群中的 Leader 服務(wù)器交換信息的端口;
- 3888是集群Leader重新選舉的通信端口,如果集群中的 Leader 服務(wù)器掛了,通過該端口來重新進行選舉
將配置好的Zookeeper分發(fā)到其他節(jié)點。
之后需要在其他節(jié)點修改zkData/myid文件中的主機編號,這里將hadoop103的myid修改為3,hadoop104修改為4
2.2 啟動
啟動各節(jié)點的zookeeper服務(wù)端,通過jps可以看到zookeeper后臺運行的進程
[tory@hadoop102 zookeeper]$ bin/zkServer.sh start
[tory@hadoop102 zookeeper]$ jps
3540 QuorumPeerMain
有時候在啟動zookeeper的時候,顯示錯誤如下,系統(tǒng)檢測到zookeeper是在運行狀態(tài),但是jps查看卻沒有相關(guān)進程。這個可能是由于異常關(guān)機造成zkData目錄下殘留文件zookeeper_server.pid,將其刪除后從新啟動即可
Starting zookeeper … already running as process 1805
查看zookeeper的運行狀態(tài),可以看到hadoop103節(jié)點運行模式是leader
[tory@hadoop103 zookeeper]# bin/zkServer.sh status
JMX enabled by default
Using config: /opt/module/zookeeper/bin/../conf/zoo.cfg
Mode: leader
如果逐個登錄各節(jié)點并啟動zookeeper操作會過于繁瑣,可以將啟動、停止等命令寫到一個腳本,通過SSH登錄各個節(jié)點完成啟動操作。如下所示編寫腳本zk.sh,針對start、stop、status命令,通過for循環(huán)遍歷各主機節(jié)點hadoop102、103、104,利用ssh登錄并調(diào)用zkServer.sh執(zhí)行操作
#!/bin/bash
case $1 in
"start"){
# 遍歷集群主機列表
for i in hadoop102 hadoop103 hadoop104
do
echo ---------- zookeeper $i 啟動 ------------
# 調(diào)用zookeeper命令
ssh $i "/opt/module/zookeeper/bin/zkServer.sh start"
done
};;
"stop"){
for i in hadoop102 hadoop103 hadoop104
do
echo ---------- zookeeper $i 停止 ------------
ssh $i "/opt/module/zookeeper/bin/zkServer.sh stop"
done
};;
"status"){
for i in hadoop102 hadoop103 hadoop104
do
echo ---------- zookeeper $i 狀態(tài) ------------
ssh $i "/opt/module/zookeeper/bin/zkServer.sh status"
done
};;
esac
將該腳本增加執(zhí)行權(quán)限,然后放到path路徑所在的目錄下就可以全局訪問
chmod u+x zk.sh
通過腳本就可以對集群所有節(jié)點進行操作,如下所示查看集群節(jié)點狀態(tài)
[tory@hadoop102 bin]$ zk.sh status
---------- zookeeper hadoop102 狀態(tài) ------------
ZooKeeper JMX enabled by default
Using config: /opt/module/zookeeper/bin/../conf/zoo.cfg
Client port found: 2181. Client address: localhost.
Mode: follower
---------- zookeeper hadoop103 狀態(tài) ------------
ZooKeeper JMX enabled by default
Using config: /opt/module/zookeeper/bin/../conf/zoo.cfg
Client port found: 2181. Client address: localhost.
Mode: leader
---------- zookeeper hadoop104 狀態(tài) ------------
ZooKeeper JMX enabled by default
Using config: /opt/module/zookeeper/bin/../conf/zoo.cfg
Client port found: 2181. Client address: localhost.
Mode: follower
2.3 命令行客戶端
在命令行通過客戶端連接zookeeper,通過-server
指定連接的服務(wù)主機端口
[tory@hadoop102 zookeeper]$ bin/zkCli.sh -server hadoop102:2181
創(chuàng)建節(jié)點
通過create
命令可以創(chuàng)建節(jié)點和目錄。
[zk: hadoop102:2181(CONNECTED) 2] create /China
Created /China
[zk: hadoop102:2181(CONNECTED) 3] create /China/Beijing "ChaoYang"
Created /China/Beijing
創(chuàng)建的節(jié)點默認是永久的,通過參數(shù)-e
可以指定節(jié)點的類型為臨時節(jié)點(Ephemeral),臨時節(jié)點在客戶端斷開連接后就會消失。
通過參數(shù)-s
可以為節(jié)點名稱后添加編號,從而避免節(jié)點重復
[zk: hadoop102:2181(CONNECTED) 7] create /China/Beijing
Node already exists: /China/Beijing
[zk: hadoop102:2181(CONNECTED) 8] create -s /China/Beijing
Created /China/Beijing0000000001
查看節(jié)點信息
通過ls
可以查看目錄下的所有子節(jié)點,-s
可以顯示節(jié)點的詳細信息,如下所示查看根節(jié)點
[zk: hadoop102:2181(CONNECTED) 5] ls -s /
[zookeeper]cZxid = 0x0
ctime = Thu Jan 01 08:00:00 CST 1970
mZxid = 0x0
mtime = Thu Jan 01 08:00:00 CST 1970
pZxid = 0x0
cversion = -1
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 0
numChildren = 1
其中各參數(shù)的含義為
- czxid:創(chuàng)建節(jié)點的事務(wù) zxid
- ctime:znode 被創(chuàng)建的毫秒數(shù)(從 1970 年開始)
- mzxid:znode 最后更新的事務(wù) zxid
- mtime:znode 最后修改的毫秒數(shù)(從 1970 年開始)
- pZxid:znode 最后更新的子節(jié)點 zxid
- cversion:znode 子節(jié)點變化號,znode 子節(jié)點修改次數(shù)
- dataversion:znode 數(shù)據(jù)變化號
- aclVersion:znode 訪問控制列表的變化號
- ephemeralOwner:如果是臨時節(jié)點,這個是 znode 擁有者的 session id。如果不是臨時節(jié)點則是 0
- dataLength:znode 的數(shù)據(jù)長度
- numChildren:znode 子節(jié)點數(shù)量
通過set
可以修改節(jié)點中的內(nèi)容,get
查看節(jié)點中的內(nèi)容,stat
可以查看節(jié)點詳細信息
[zk: hadoop102:2181(CONNECTED) 0] set /China/Beijing "HaiDian"
[zk: hadoop102:2181(CONNECTED) 1] get /China/Beijing
HaiDian
通過delete
可以刪除單個節(jié)點,deleteall
刪除節(jié)點及其子節(jié)點
[zk: hadoop102:2181(CONNECTED) 2] delete /China/Beijing
[zk: hadoop102:2181(CONNECTED) 3] deleteall /China
監(jiān)聽器
客戶端注冊監(jiān)聽它關(guān)心的目錄節(jié)點,當目錄節(jié)點發(fā)生變化(數(shù)據(jù)改變、節(jié)點刪除、子目錄節(jié)點增加刪除)時,ZooKeeper 會通知客戶端。監(jiān)聽機制保證 ZooKeeper 保存的任何的數(shù)據(jù)的任何改變都能快速的響應到監(jiān)聽了該節(jié)點的應用程序。
如下所示在 hadoop104 主機上注冊監(jiān)聽/China/Beijing節(jié)點數(shù)據(jù)變化
[zk: localhost:2181(CONNECTED) 26] get -w /China/Beijing
之后在 hadoop103 主機上修改節(jié)點的數(shù)據(jù)
[zk: localhost:2181(CONNECTED) 1] set /China/Beijing “FengTai”
觀察 hadoop104 主機收到數(shù)據(jù)變化的監(jiān)聽
WATCHER::
WatchedEvent state:SyncConnected type:NodeDataChanged
path:/China/Beijing
注意:在hadoop103再次修改/China/Beijing的值,hadoop104上不會再收到監(jiān)聽。因為注冊一次,只能監(jiān)聽一次。想再次監(jiān)聽,需要再次注冊。
2.4 Java API
在Java開發(fā)中需要連接和訪問zookeeper時可以使用官方API包,首先在maven中引入對應版本的依賴
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.5.7</version>
</dependency>
首先創(chuàng)建客戶端連接對象zkClient,在其中指定服務(wù)端地址connectString和超時時間,并且可以通過Watcher監(jiān)聽事件,只要節(jié)點發(fā)生變化就會觸發(fā)監(jiān)聽器,從而執(zhí)行回調(diào)函數(shù)process
# zookeeper服務(wù)端地址
private static String connectString ="hadoop102:2181,hadoop103:2181,hadoop104:2181";
# 連接超時時間
private static int sessionTimeout = 2000;
private ZooKeeper zkClient = null;
zkClient = new ZooKeeper(connectString, sessionTimeout, new Watcher() {
// 收到事件通知后的回調(diào)函數(shù)(用戶的業(yè)務(wù)邏輯)
@Override
public void process(WatchedEvent watchedEvent) {
System.out.println(watchedEvent.getType() + "--" + watchedEvent.getPath());
}
});
通過zkClient的create()
方法可以創(chuàng)建節(jié)點,exist()
可以返回節(jié)點是否存在,getChildren()
可以獲取指定目錄下的子節(jié)點,其第二個參數(shù)可以傳入一個監(jiān)聽器,如果是true則默認為創(chuàng)建zkClient時的監(jiān)聽器
// 參數(shù) 1:要創(chuàng)建的節(jié)點的路徑; 參數(shù) 2:節(jié)點數(shù)據(jù) ; 參數(shù) 3:節(jié)點權(quán)限 ;參數(shù) 4:節(jié)點的類型
String nodeCreated = zkClient.create("/atguigu", "shuaige".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
// 判斷節(jié)點是否存在
Stat stat = zkClient.exists("/China", false);
System.out.println(stat == null ? "not exist" : "exist");
// 獲取子節(jié)點
List<String> children = zkClient.getChildren("/", true);
for (String child : children) {
System.out.println(child);
}
服務(wù)器上下線監(jiān)測
通過zookeeper可以模擬實現(xiàn)對集群節(jié)點上下線進行監(jiān)測,當服務(wù)器節(jié)點上線時,作為臨時節(jié)點注冊到zookeeper中的/servers目錄下,如/servers/hadoop102,而節(jié)點下線時臨時節(jié)點就會從目錄中刪除。這樣客戶端就可以通過監(jiān)聽zookeeper中節(jié)點信息從而隨時掌握存活的服務(wù)器列表。
如下所示為服務(wù)器端向zookeeper進行注冊的代碼文章來源:http://www.zghlxwxcb.cn/news/detail-460162.html
import java.io.IOException;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.ZooDefs.Ids;
public class DistributeServer {
private static String connectString = "hadoop102:2181,hadoop103:2181,hadoop104:2181";
private static int sessionTimeout = 2000;
private ZooKeeper zk = null;
private String parentNode = "/servers";
public static void main(String[] args) throws Exception {
DistributeServer server = new DistributeServer();
// 1 獲取 zk 連接
server.getConnect();
// 2 利用 zk 連接注冊服務(wù)器信息
server.registerServer(args[0]);
// 3 啟動業(yè)務(wù)功能
server.business(args[0]);
}
// 創(chuàng)建到 zk 的客戶端連接
public void getConnect() throws IOException {
zk = new ZooKeeper(connectString, sessionTimeout, new Watcher() {
@Override
public void process(WatchedEvent event) {
}
});
}
// 注冊服務(wù)器
public void registerServer(String hostname) throws Exception {
String create = zk.create(parentNode + "/" +hostname,
hostname.getBytes(), Ids.OPEN_ACL_UNSAFE,
CreateMode.EPHEMERAL_SEQUENTIAL);
System.out.println(hostname + " is online " + create);
}
// 業(yè)務(wù)功能
public void business(String hostname) throws Exception {
System.out.println(hostname + " is working ...");
Thread.sleep(Long.MAX_VALUE);
}
}
如下所示為客戶端監(jiān)聽服務(wù)器節(jié)點變化的實現(xiàn)文章來源地址http://www.zghlxwxcb.cn/news/detail-460162.html
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooKeeper;
public class DistributeClient {
private static String connectString = "hadoop102:2181,hadoop103:2181,hadoop104:2181";
private static int sessionTimeout = 2000;
private ZooKeeper zk = null;
private String parentNode = "/servers";
public static void main(String[] args) throws Exception {
DistributeClient client = new DistributeClient();
// 1 獲取 zk 連接
client.getConnect();
// 2 獲取 servers 的子節(jié)點信息,從中獲取服務(wù)器信息列表
client.getServerList();
// 3 業(yè)務(wù)進程啟動
client.business();
}
// 創(chuàng)建到 zk 的客戶端連接
public void getConnect() throws IOException {
zk = new ZooKeeper(connectString, sessionTimeout, new Watcher() {
@Override
public void process(WatchedEvent event) {
try {
getServerList(); // 再次啟動監(jiān)聽
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
// 獲取服務(wù)器列表信息
public void getServerList() throws Exception {
// 1 獲取服務(wù)器子節(jié)點信息,并且對服務(wù)器節(jié)點的變化進行監(jiān)聽
List<String> children = zk.getChildren(parentNode, true);
// 2 存儲服務(wù)器信息列表
ArrayList<String> servers = new ArrayList<>();
// 3 遍歷所有節(jié)點,獲取節(jié)點中的主機名稱信息
for (String child : children) {
byte[] data = zk.getData(parentNode + "/" + child, false, null);
servers.add(new String(data));
}
// 4 打印服務(wù)器列表信息
System.out.println(servers);
}
// 業(yè)務(wù)功能
public void business() throws Exception {
System.out.println("client is working ...");
Thread.sleep(Long.MAX_VALUE);
}
}
到了這里,關(guān)于使用Zookeeper對集群節(jié)點進行管理的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!