Zookeeper安装
安装zookeeper
version: "3.1"
services:
zk:
image: daocloud.io/daocloud/zookeeper:latest # 其实是3.4.6版本的Zookeeper
container_name: zk
ports:
- 2181:2181
进入到zookeeper容器内部,打开客户端
docker exec -it zk bash
cd bin
./zkCli.sh
Zookeeper常用命令
# 查询当前节点下的全部子节点
ls 节点名称
# 例子 ls /
# 查询当前节点下的数据
get 节点名称
# 例子 get /zookeeper
# 创建结点
create [-s] [-e] znode名称 znode数据
# -s:sequence,有序节点
# -e:ephemeral,临时节点
# 例如 create /zookeeper/zbx zhang
# 修改结点
set znode名称 新数据
# 删除结点
delete znode名称 # 没有子节点的znode
deleteall znode名称
rmr znode名称 # 删除当前节点和全部的子节点
Zookeeper架构
每一个目录都可以称为一个znode,每个znode都可以有子节点,每个znode都能存储数据
znode类型
- 持久结点
- 持久有序结点
- 临时结点
- 临时有序结点
- …
Zookeeper提供了监听通知机制,客户端在监听某一个ZNode节点后,当这个ZNode被修改/删除后,Zookeeper会立即通知整个客户端,并执行客户端的回调函数。
Zookeeper集群
集群中结点的角色
1、 Leader
- Master主节点,可读可写
2、 Follower (默认的从节点)
- 从节点,可以读,如果请求为写,则转发给Leader;参与选举全新的Leader
3、 Observer
- 从节点,可以读,如果请求为写,则转发给Leader;不参与投票
4、 Looking
- 寻找Leader节点
集群中,如果Leader宕机,则集群不提供任何功能,此时集群内部会发起投票,选取一位全新的Leader
Zookeeper投票策略
1、每一个Zookeeper服务都会被分配一个全局唯一的myid,myid是一个数字。
2、Zookeeper在执行写数据时,每一个节点都有一个自己的FIFO的队列。保证写每一个数据的时候,顺序是不会乱的,Zookeeper还会给每一个数据分配一个全局唯一的zxid,数据越新zxid就越大。
选举Leader:
- 选举出zxid最大的节点作为Leader。
- 在zxid相同的节点中,选举出一个myid最大的节点,作为Leader。
搭建Zookeeper集群
(Zookeeper搭建集群采用的3.6.2版本)
version: "3.1"
services:
zk1:
image: zookeeper
restart: always
container_name: zk1
ports:
- 2181:2181
environment:
ZOO_MY_ID: 1
ZOO_SERVERS: server.1=zk1:2888:3888;2181 server.2=zk2:2888:3888;2181 server.3=zk3:2888:3888;2181
zk2:
image: zookeeper
restart: always
container_name: zk2
ports:
- 2182:2181
environment:
ZOO_MY_ID: 2
ZOO_SERVERS: server.1=zk1:2888:3888;2181 server.2=zk2:2888:3888;2181 server.3=zk3:2888:3888;2181
zk3:
image: zookeeper
restart: always
container_name: zk3
ports:
- 2183:2181
environment:
ZOO_MY_ID: 3
ZOO_SERVERS: server.1=zk1:2888:3888;2181 server.2=zk2:2888:3888;2181 server.3=zk3:2888:3888;2181
Java操作Zookeeper
导入依赖
<dependencies>
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.6.0</version>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
<version>4.0.1</version>
</dependency>
</dependencies>
编写连接工具类
public class ZkUtil {
public static CuratorFramework cf(){
RetryPolicy retryPolicy = new ExponentialBackoffRetry(3000,2);
CuratorFramework cf = CuratorFrameworkFactory.builder()
.connectString("192.168.199.109:2181,192.168.199.109:2182,192.168.199.109:2183")
.retryPolicy(retryPolicy)
.build();
cf.start();
return cf;
}
}
操作结点
public class Demo2 {
CuratorFramework cf = ZkUtil.cf();
// 获取子节点
@Test
public void getChildren() throws Exception {
List<String> strings = cf.getChildren().forPath("/");
for (String string : strings) {
System.out.println(string);
}
}
// 获取节点数据
@Test
public void getData() throws Exception {
byte[] bytes = cf.getData().forPath("/qf");
System.out.println(new String(bytes,"UTF-8"));
}
// 添加
@Test
public void create() throws Exception {
cf.create().withMode(CreateMode.PERSISTENT).forPath("/qf2","uuuu".getBytes());
}
// 修改
@Test
public void update() throws Exception {
cf.setData().forPath("/qf2","oooo".getBytes());
}
// 删除
@Test
public void delete() throws Exception {
cf.delete().deletingChildrenIfNeeded().forPath("/qf2");
}
// 查询znode状态
@Test
public void stat() throws Exception {
Stat stat = cf.checkExists().forPath("/qf");
System.out.println(stat);
}
}
监听通知机制
public class Demo3 {
CuratorFramework cf = ZkUtil.cf();
@Test
public void listen() throws Exception {
//1. 创建NodeCache对象,指定要监听的znode
NodeCache nodeCache = new NodeCache(cf,"/qf");
nodeCache.start();
//2. 添加一个监听器
nodeCache.getListenable().addListener(new NodeCacheListener() {
@Override
public void nodeChanged() throws Exception {
byte[] data = nodeCache.getCurrentData().getData();
Stat stat = nodeCache.getCurrentData().getStat();
String path = nodeCache.getCurrentData().getPath();
System.out.println("监听的节点是:" + path);
System.out.println("节点现在的数据是:" + new String(data,"UTF-8"));
System.out.println("节点状态是:" + stat);
}
});
System.out.println("开始监听!!");
//3. System.in.read();
System.in.read();
}
}