1. ZooKeeper入門
1.1 概述
Zookeeper是一個開源的,爲分佈式應用提供協調服務的Apache項目.
ZooKeeper工作機制
ZooKeeper從設計模式角度來理解,是一個基於觀察者模式
設計的,分佈式服務管理框架
.它負責存儲和管理大家都關心的數據
,然後接受觀察者的註冊
,一旦這些數據發生變化,ZooKeeper就將負責通知已註冊的觀察者
做出相應的反應.
1.2 特點
- ZooKeeper由一個leader和多個follower組成.
- 集羣中只要有半數以上的節點存活,ZooKeeper就能正常服務.
- 全局一直:每個server保存一份相同的數據副本,Client無論連接到哪個server,數據都是一致的.
- 更新請求順序執行:來自同一個Client的更新請求,按發送的事件順序執行.
- 數據更新原子性,一次數據更新要麼成功,要麼失敗.
- 實時性,在一定時間範圍內,Client能讀到最新的數據.
1.3 數據結構
ZooKeeper數據模型的結構與Unix文件系統類似,整體上可以看成一棵樹, 每一個節點都成爲ZNode,每個ZNode默認能夠儲存1M的數據,每個ZNode都可以通過路徑作爲唯一的標識.
2. 本地模式
(1)事前準備:
- 下載,解壓ZooKeeper
下載地址:https://archive.apache.org/dist/zookeeper/
sudo tar xzvf zookeeper-3.4.14/ -C /opt/softwares/
修改目錄及子目錄權限
sudo chown -R USER:GROUP /opt/softwares/zookeeper-3.4.14
# 習慣性的創建符號鏈接,可忽略,看個人
sudo ln -s zookeeper-3.4.14 zkeeper
- jdk的配置.
# 1. 解壓縮
sudo tar xzvf /opt/download/jdk-8u202-linux-x64.tar.gz -C /opt/softwares/
# 2. 創建符號鏈接
sudo ln -s jdk1.8.0_202/ jdk
# 3. 配置環境變量
vi ~/.bash_profile
####在文件中添加相應信息
export JAVA_HOME=/opt/softwares/jdk
PATH=......:$JAVA_HOME/bin:$JAVA_HOME/sbin
#### 重讀配置文件
source ~/.bash_profile
#### 驗證配置是否成功
java -version
(2)配置修改:
- 將zookeeper目錄
conf/zoo_sample.cfg
文件複製/修改爲zoo.cfg
cp conf/zoo_sample.cfg zoo.cfg
- 修改zoo.cfg中dataDir屬性
dataDir=/opt/softwares/zkeeper/zkData
(3)ZooKeeper操作:
- 啓動zkServer
bin/zkServer.sh start
- 查看進程是否啓動成功
- 查看狀態:
bin/zkServer.sh status
- 啓動客戶端
bin/zkCli.sh
- 退出客戶端
quit
- 停止ZooKeeper
bin/zkServer.sh stop
3. 配置參數解讀
ZooKeeper中的配置文件zoo.cfg中參數含義解讀:
- tickTime=2000
通信心跳時間,ZooKeeper服務器與客戶端的心跳時間,單位:毫秒. - initLimit=10
初始通信時限,集羣中的Follower服務器與Leader服務器之間初始連接時能容忍的最多心跳次數,這裏的設置是超過10次,即20秒,則判斷連接失敗. - syncLimit=5
同步通信時限,集羣中Leader與Follower之間的最大響應時限,加入超過syncLimit * tickTime,則Leader會判定Follower掛掉,會將Follower從服務器列表中刪除. - dataDir
數據文件路徑+數據持久化路徑.用於保存ZooKeeper的數據. - clientPort = 2181
客戶端連接端口.
4. 分佈式安裝部署
(1) 集羣規劃
這裏用的還是之前基於Docker搭建的Hadoop集羣,三個節點分別爲:
172.18.0.2 cluster-hdfs-master
172.18.0.3 cluster-hdfs-slave1
172.18.0.4 cluster-hdfs-slave2
(2)在cluster-hdfs-master容器中進行配置
- 解壓安裝ZooKeeper
sudo tar xzvf zookeeper-3.4.14.tar.gz -C /opt/softwares/
# 爲了避免權限問題修改owner:group
sudo chown -R hadoop:hadoop /opt/softwares/zookeeper-3.4.14
- 進入ZooKeeper的目錄,在目錄中創建目錄,用於存放數據.
cd /opt/softwares/zookeeper-3.4.14 && mkdir zkData
- 在zkData目錄中創建名爲
myid
的文件
touch myid
- 在myid中添加與server對應的編號
注:編號爲數字
這裏我們按照下面的對應進行填寫,由於是在cluster-hdfs-master容器中,所以這裏填寫1
,稍後再去修改另外2個容器中的內容
cluster-hdfs-master 1
cluster-hdfs-slave1 2
cluster-hdfs-slave2 3
- 配置zoo.cfg文件
①在ZooKeeper的conf目錄下,將名爲zoo_sample.cfg的文件重命名或複製爲zoo.cfg
cp zoo_sample.cfg zoo.cfg
②修改數據存儲路徑爲我們創建的zkData路徑
dataDir=/opt/softwares/zookeeper-3.4.14/zkData
③添加集羣服務信息
################cluster info####################
server.1=cluster-hdfs-master:2888:3888
server.2=cluster-hdfs-slave1:2888:3888
server.3=cluster-hdfs-slave2:2888:3888
- 將zookeeper-3.4.14目錄分發到集羣中其他節點
①準備一個分發腳本這裏起名爲container-sync
#!/bin/bash
# Program:
# use rsync to copy the file or folder to the nodes among the cluster
# 2020/01/20 Shuu First release
cnt=$#
if [ $cnt -lt 1 ]; then
echo "Usage:`basename $0` argument"
exit 1
fi
filename=`basename $1`
# -P選項的作用是,如果目標爲符號鏈接,則追蹤到真實路徑
DIR=`cd $(dirname $1);pwd -P`
for line in `cat /home/hadoop/scripts/hosts.info`
do
HOST=`echo $line | cut -f2`
echo ==============$HOST===============
# -r 遞歸
# -u 文件存在的話如果有內容更新,則更新,否則不做操作
# -l 文件爲符號鏈接,則按照符號鏈接進行拷貝
rsync -ruvl $DIR/$filename $HOST:$DIR
done
爲了方便操作我們將container-sync移動到/usr/bin/目錄中.
# 給user賦予執行權限
sudo chmod u+x container-sync
# 移動到/usr/bin目錄
sudo mv container-sync /usr/bin/
②分發ZooKeeper目錄
/opt/softwares/
目錄需要進行相應的權限設置,比如sudo chmod 777 /opt/softwares
container-sync /opt/softwares/zookeeper-3.4.14
- 分別修改另外2個容器中myid文件中的內容
sudo docker exec slave1 /bin/bash -c "su - hadoop -c 'echo 2 > /opt/softwares/zookeeper-3.4.14/zkData/myid'"
sudo docker exec slave2 /bin/bash -c "su - hadoop -c 'echo 2 > /opt/softwares/zookeeper-3.4.14/zkData/myid'"
(3)配置解讀
server.A=B:C:D
A:
是一個數字,表示這是第幾號服務器;
集羣模式下,在conf下的zoo.cfg文件中所配置的dataDir
目錄下,需要配置一個名爲myid
的文件,文件中存放着當前節點的號碼
,ZooKeeper啓動時,會讀取此文件,然後與zoo.cfg文件中集羣信息進行比對,即A,從而判斷當前爲哪個Server.
B:
服務器地址
C:
集羣中Follower與Leader通信端口
D:
ZooKeeper集羣選舉時使用的端口
(4)啓動集羣
分別在集羣中的每個節點啓動ZooKeeper服務端.(這裏只是演示,後期建議寫成自啓腳本)
# 在ZooKeeper目錄下
bin/zkServer.sh start
# 查看ZooKeeper Server狀態
bin/zkServer.sh status
5. 客戶端常見命令
- 啓動客戶端
bin/zkCli.sh
命令 | 描述 |
---|---|
help | 顯示左右操作命令 |
ls path [watch] | 使用ls命令來查看當前znode中所包含的內容 |
ls2 path [path] | 查看path節點數據,並能查看到更豐富的內容信息 |
create | 創建znode -s Sequence Znode序列節點 -e Ephemeral Znode 臨時節點,重啓後消失 |
get path [watch] | 獲得path節點的數據 |
set | 設置節點的值 |
stat | 查看節點狀態 |
delte | 刪除節點 |
rmr | 遞歸刪除節點 |
6. IDE操作ZooKeeper
這裏使Idea爲例演示.
在pom.xml
中添加相應的信息
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>RELEASE</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.8.2</version>
</dependency> <dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.4.14</version>
</dependency>
</dependencies>
添加客戶端連接方法,並進行節點的創建,查詢子節點,刪除節點的操作.
雲服務器玩家需要在安全組,開放相應的端口
package conn;
import org.apache.zookeeper.*;
import org.junit.Before;
import org.junit.Test;
import java.io.IOException;
import java.util.List;
public class ZkpCli {
// 用的docker搭的,所以端口分別被我映射爲不同的端口
// 如果用的是多臺主機/虛擬機,則可以使用2181端口
private static final String HOSTS = "master:2181,slave1:2182,slave2:2183";
private static final int TIMEOUT = 2000;
private ZooKeeper zkClient = null;
@Before
public void conn() throws IOException {
zkClient = new ZooKeeper(HOSTS, TIMEOUT, new Watcher() {
public void process(WatchedEvent e) {
System.out.println(e.getState().toString());
}
});
}
@Test
public void create() throws KeeperException, InterruptedException {
for (int i = 0; i < 3; i++) {
String str = zkClient.create("/testCli" + i, "Test API".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
System.out.println(str);
}
}
@Test
public void getChildren() throws KeeperException, InterruptedException {
List<String> list = zkClient.getChildren("/", false);
for (String s : list) {
System.out.println(s);
}
}
@Test
public void deleteNode() throws KeeperException, InterruptedException {
for (int i = 0; i < 3; i++) {
zkClient.delete("/testCli" + i, 0);
}
}
}