Zookeeper学习
ZooKeeper(动物园管理者)简称ZK,一个分布式的,开放源码的分布式应用程序
协调服务
,是Google的Chubby一个开源的实现,是Hadoop和Hbase的重要组件。ZooKeeper使用Java 所编写,但是支持Java和C两种编程语言。
协调服务
-
dubbo框架 springcloud框架 zk 注册中心
-
Hadoop Hbase组件﹐集群架构﹑ zk集群管理者
-
zk实现分布式锁
内存数据模型
模型结构
2.模型的特点
- 每个子目录如/node1都被称作一个znode(节点)。这个znode是被它所在的路径唯一标识
- znode可以有子节点目录,并且每个znode 可以存储数据
- znode是有版本的,每个znode中存储的数据可以有多个版本,也就是一个访问路径中可以存储多份数据
- znode可以被监控,包括这个目录节点中存储的数据的修改,子节点目录的变化等,一旦变化可以通知设置监控的客户端
节点的分类
持久节点(PERSISTENT)
是指在节点创建后,就一直存在,直到有删除操作来主动删除这个节点――不会因为创建该节点的客户端会话失效而消失
持久顺序节点(PERSISTENT_SEQUENTIAL)
这类节点的基本特性和上面的节点类型是一致的。额外的特性是,在ZK中,每个父节点会为他的第一级子节点维护一份
时序
,会记录每个子节点创建的先后顺序
。基于这个特性,在创建子节点的时候,可以设置这个属性,那么在创建节点过程中,ZK会自动为给定节点名加上一个数字后缀,作为新的节点名。这个数字后缀的范围是整型的最大值。
临时节点(EPHEMERAL)
和持久节点不同的是,临时节点的生命周期和客户端会话绑定。也就是说,如果客户端会话失效,那么这个节点就会自动被清除掉。注意,这里提到的是
会话失效
,而非连接断开。另外,在临时节点下面不能创建子节点
。
临时顺序节点(EPHEMERAL_SEQUENTIAL)
具有临时节点特点,额外的特性是,每个父节点会为他的第一级子节点维护一份时序。这点和刚才提到的持久顺序节点类似
起名字按首字母 分别是 P 、 PS、 E、 ES
安装
手动安装
1.安装jdk8
2.解压zookeeper-3.4.12.tar.gz
tar -zxvf zookeeper-3.4.12.tar.gz
3.配置 进入 conf目录 有个模板文件 复制一份并重新命名
cp zoo_sample.cfg zoo.cfg
可以看到默认端口2181
tickTime:集群节点之间的的心跳 默认每2s
initLimit:初始化限制 初始化集群时 集群节点同步超时时间 默认10*(tickTime) = 20s
syncLimit:集群运行过程中 同步超时时间 默认5*(tickTime) = 10s
dataDir: 默认存储数据的位置
clientPort:默认端口号
maxClientCnxns: 线程池的数量(最大客户端连接数) 默认60
4. 启动服务 后面需要更上配置文件
./zkServer.sh start /root/zookeeper-3.4.12/conf/zoo.cfg
./zkServer.sh stop /root/zookeeper-3.4.12/conf/zoo.cfg 关闭服务
ZooKeeper JMX enabled by default
Using config: /root/zookeeper-3.4.12/conf/zoo.cfg
Starting zookeeper ... STARTED
5.使用jps查看是否启动
[root@localhost bin]# jps
1412 QuorumPeerMain
1429 Jps
6.使用客户端命令查看是否正常运行
./zkCli.sh -server 192.168.29.133:2181
7.测试以下命令
[zk: 192.168.29.133:2181(CONNECTED) 0] ls /
[zookeeper]
Docker安装zookeeper
获取zk的镜像
-
docker pull zookeeper : 3.4.14
启动zk服务
-
docker run --name zk -p 2181:2181 -d zookeeper :3.4.14
客户端基本指令
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
常用总结:
--查看该路径下的节点
ls 路径
--创建一个节点
create 节点 数据
create /node1 zhangan 默认 持久节点
create -s /node1 zhangan 顺序节点
create -e /node1 zhangan 临时节点
create -s -e /node1 zhangan 临时顺序节点
--查看节点状态
stat 节点
[zk: 192.168.29.133:2181(CONNECTED) 12] stat /node1
cZxid = 0x4 创建id
ctime = Mon Jun 07 16:18:20 CST 2021 创建时间
mZxid = 0x4 修改
mtime = Mon Jun 07 16:18:20 CST 2021 修改时间
pZxid = 0x4 父节点id
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 7
numChildren = 0
--获取节点数据
get 节点
[zk: 192.168.29.133:2181(CONNECTED) 13] get /node1
zhangan
cZxid = 0x4
ctime = Mon Jun 07 16:18:20 CST 2021
mZxid = 0x4
mtime = Mon Jun 07 16:18:20 CST 2021
pZxid = 0x4
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 7
numChildren = 0
--修改数据
set 节点 数据
--历史记录
history
--删除节点
delete 节点 只能删除没有子节点的
--递归删除
rmr 节点
--推出会话(会话失效)
quit
watch节点监听机制
客户端可以监测znode节点的变化。Zonode节点的变化触发相应的事件,然后清除对该节点的监测。当监测一个znode节点时候,Zookeeper会发送通知给监测节点。一个Watch事件是一个一次性的触发器
,当被设置了Watch的数据和目录发生了改变的时候,则服务器将这个改变发送给设置了Watch的客户端以便通知它们。
1s /path true
监听节点目录的变化
get /path true
监听节点数据的变化。
Java操作ZK
引入依赖
<dependency>
<groupId>com.101tec</groupId>
<artifactId>zkclient</artifactId>
<version>0.10</version>
</dependency>
上代码!
package com.zhangan;
import com.zhangan.entity.User;
import org.I0Itec.zkclient.IZkChildListener;
import org.I0Itec.zkclient.IZkDataListener;
import org.I0Itec.zkclient.ZkClient;
import org.I0Itec.zkclient.serialize.SerializableSerializer;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.data.Stat;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.io.IOException;
import java.util.Date;
import java.util.List;
/**
* @Author: 张安
* @Date: 2021/6/7 16:49
* @Description:
*/
public class TestZkClient {
private ZkClient client;
@Before
public void before() {
/**
* 参数
* 服务器ip
* 会话超时时间 单位 毫秒
* 连接超时时间
* 序列化方式
*/
client = new ZkClient("192.168.29.133:2181", 60000 * 30,
60000, new SerializableSerializer());
}
@After
public void after() {
client.close();
}
//在zk中创建节点
@Test
public void createNode() {
//持久节点
client.create("/node1", "zhangan", CreateMode.PERSISTENT);
//持久顺序节点
client.create("/node1/ps", "zhanganps", CreateMode.PERSISTENT_SEQUENTIAL);
//临时节点
client.create("/node1/e", "zhangane", CreateMode.EPHEMERAL);
//临时顺序节点
client.create("/node1/es", "zhanganes", CreateMode.EPHEMERAL_SEQUENTIAL);
}
//删除节点
@Test
public void delete() {
//删除
boolean delete = client.delete("/node1");
//递归删除
boolean deleteRecursive = client.deleteRecursive("/node1");
}
//查询当前节点下的所有子节点
@Test
public void query() {
List<String> children = client.getChildren("/");
for (String child : children) {
System.out.println("child = " + child);
}
}
//查看某一节点信息
@Test
public void get() {
Object data = client.readData("/node1");
System.out.println("data = " + data);
}
//查看节点状态
@Test
public void stat() {
Stat stat = new Stat();
Object data = client.readData("/node1", stat);
System.out.println("data = " + data);
System.out.println("stat = " + stat);
System.out.println("stat.getDataLength() = " + stat.getDataLength());
}
//修改节点
@Test
public void update() {
client.writeData("/node1", new User(1, "张安", new Date()));
User data = client.readData("/node1");
System.out.println(data);
}
//监听节点数据变化 永久监听 使用java代码操作才会触发
@Test
public void watchData() throws IOException {
client.subscribeDataChanges("/node1", new IZkDataListener() {
//数据变化时触发
@Override
public void handleDataChange(String dataPath, Object data) throws Exception {
System.out.println("数据变化时触发-> 当前节点路径" + dataPath);
System.out.println("修改之后的数据" + data);
}
//数据删除时触发
@Override
public void handleDataDeleted(String dataPath) throws Exception {
System.out.println("数据删除时触发-> 当前节点路径" + dataPath);
}
});
System.in.read();//阻塞客户端 为了测试
}
//监听节点目录变化
@Test
public void watchPath() throws IOException {
client.subscribeChildChanges("/node1", new IZkChildListener() {
//节点目录发生变化时 触发
@Override
public void handleChildChange(String parentPath, List<String> currentChilds) throws Exception {
System.out.println("父节点名称 = " + parentPath);
for (String child : currentChilds) {
System.out.println("变化后各个子节点的名称 = " + child);
}
}
});
System.in.read();//阻塞客户端 为了测试
}
}