Zookeeper
Zookeeper的作用
- Zookeeper可以作为注册中心,实现服务的注册与发现。
- Zookeeper基于监听通知机制可以实现配置的统一管理。
- Zookeeper可以基于他存储节点的特点,实现分布式锁。
- Zookeeper可以作为很多中间件的集群统一协调管理。
安装Zookeeper
docker-compose.yml
version: "3.1" services: zk: image: >daocloud.io/daocloud/zookeeper:latest restart: always container_name: zk ports: - 2181:2181
Zookeeper的架构
Zookeeper本身就时一个文件存储系统:Zookeeper内部存储着大量的znode,每一个znode节点都可以有多个子节点,每一个znode都可以单独的存储数据
znode类型
- 持久的znode:永久的保存在Zookeeper
- 持久有序的znode:永久的保存在Zookeeper。添加节点时,自动在节点名称后追加一个有序的序号。
- 临时的znode:客户端和Zookeeper服务断开连接后,当前znode自动删除。
- 临时有序的znode:客户端和Zookeeper服务断开连接后,当前znode自动删除。添加节点时,自动在节点名称后追加一个有序的序号。
Zookeeper提供的监听通知机制:客户端可以监听Zookeeper服务中的znode,在znode改变时,Zookeeper会通知所有监听的客户端。
Zookeeper常用命令
查询
# 查询当前节点下的全部子节点 ls 节点名称 # 例子 ls / # 查询当前节点下的数据 get 节点名称 # 例子 get /zookeeper
创建节点
create [-s] [-e] znode名称 znode数据 # -s:sequence,有序节点 # -e:ephemeral,临时节点
修改节点数据
set znode名称 新数据
** 删除节点数据**
# 没有子节点的znode delete znode名称 # 删除当前节点和全部的子节点 rmr znode名称 # rmr看版本,有的版本是deleteall
Zookeeper集群结构
Zookeeper集群架构
- Zookeeper的集群是有Leader的。
- Leader宕机后,Zookeeper集群不会对外提供功能。
- Zookeeper内部会存在投票机制,重新选举一个Leader。
- Zookeeper必须有master节点,不然无法正常工作(就是必须有两个及以上)
- Leader可以执行读写的操作。
- Slave可以执行读的操作。
Zookeeper集群节点的解决
- Leader:
- 执行处理读和写的操作。
- Leader是唯一可以处理写操作的, 并且在执行写操作时,会根据一个FIFO的队列来保证写数据的顺序性。
- 其他节点的唯一调度者。
- Follower:
- 执行读操作。
- 在执行写操作时,进行投票。
- 在重新选举Leader时,会参与投票。
- Observer:
- 执行读操作。
- Looking:
- 在Zookeeper节点刚刚启动时,寻找Leader。
Zookeeper投票策略
- 每一个Zookeeper服务都会被分配一个全局唯一的myid,myid是一个数字。
- Zookeeper在执行写数据时,每一个节点都有一个自己的FIFO的队列。保证写每一个数据的时候,顺序是不会乱的,Zookeeper还会给每一个数据分配一个全局唯一的zxid,数据越新zxid就越大。
选举Leader:
- 选举出zxid最大的节点作为Leader。
- 在zxid相同的节点中,选举出一个myid最大的节点,作为Leader。
搭建Zookeeper集群
ZOO_SERVERS: server(固定的).1(myid)=zk1(服务名,上边那个,不是container_name):2888(集群通讯端口):3888(集群投票端口);2181(容器内端口号)
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> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency> </dependencies>
** 编写连接Zookeeper集群的工具类**
public class ZkUtil { public static CuratorFramework cf(){ // 指定重试策略 3秒重试一次,一共重试2次 RetryPolicy retryPolicy = new ExponentialBackoffRetry(3000,2); // 成功构建出链接Zookeeper的对象 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; } }
Java操作Znode节点
查询
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()); } **修改** ```java @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(); } }