ZooKeeper概述
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);
}
}
}