Zookeeper学习

基础

zk基础

zk是什么

zookeeper是一个高性能、开源的分布式应用协调服务,使用文件系统目录树作为数据模型

提供了简单原始的功能,分布式应用可以基于它实现更高级的服务

  • 可以实现同步(分布式锁)
  • 配置管理
  • 集群管理

zk是一种分层的树形结构

  • 树形结构中每个节点称为Znode
  • 每个Znode都可以有数据(byte[]类型),也可以有子节点, 每个节点都可以存储1MB(默认)的数据。
  • Znode的路径使用斜线分割,例如/Zoo/Duck,zk中没有相对路径的说法,即所有节点的路径都要携程绝对路径的方式。
  • 当zk中节点的数据发生变化时,版本号会递增。
  • 可以对Znode中的数据进行读写操作

数据发布订阅/配置中心

数据发布/订阅即所谓的配置中心

发布者将数据发布到zk的一个或一些列节点上,订阅者进行数据订阅,可以即时得到数据的变化通知

  • 应用A将数据发布到zkServer的某个节点上
  • 应用B和C会先在zkServer上注册监听该节点的watcher(相当于listener,基于RPC实现)
  • 一旦该节点有数据变化,B和C上的watcher变化得到通知,继而从zkServer上获取最新的数据

服务路由与负载均衡

zk本质上是利用zk做配置中心来实现负载均衡

  1. 服务提供者把自己的域名及IP端口映射注册到zk中
  1. 服务消费者通过域名从zk中获取到对应的IP及端口,这里的IP及端口可能有多个,只是获取其中一个
  1. 当服务提供者宕机时,对应的域名与IP的对应就会减少一个映射。
  1. dubbo服务框架就是基于zk实现服务路由和负载。

基本概念

zk的数据节点
  1. Znode是zk树形结构中的数据节点,用于存储数据
  1. Znode分为持久节点和临时节点两种类型
    1. 持久节点:一旦创建,除非主动调用删除操作,否则一直存储在zk上
    1. 临时节点:与客户端会话绑定,一旦客户端失效,这个客户端创建的所有临时节点都会被删除
  1. 可以为持久节点或临时节点设置Sequential属性,如果设置该属性则会自动在该节点名称后追加一个整形数字。
    1. 持久顺序(PERSISTENT_SEQUENTIAL)节点 :除了具有持久(PERSISTENT)节点的特性之外, 子节点的名称还具有顺序性。比如 /node1/app0000000001/node1/app0000000002
    1. 临时顺序(EPHEMERAL_SEQUENTIAL)节点 :除了具备临时(EPHEMERAL)节点的特性之外,子节点的名称还具有顺序性

zk的watcher

watcher监听在Znode的节点上,当节点的数据更新或子节点的状态发生变化都会使客户端的watcher得到通知

连接zk服务

如果是docker部署的zk, 可以进容器内部访问.

docker exec -it zk bash  # 进入容器内部

cd bin

./zkCli.sh -server 127.0.0.1:2181  # 连接zk

zk的基本操作命令
  • 帮助命令 help
  • create [-s] [-e] path data acl
    • 创建zk节点
    • -s代表创建的节点具有顺序的属性
    • -e表示创建的是临时节点,默认情况下创建的是持久节点
    • path是节点的全路径
    • data为创建节点中的数据
    • acl用来进行权限控制, 默认情况下不做任何权限控制
    • create /node1 "node1" 在根目录创建了 node1 节点,存的数据是"node1"
    • create /node1/node1.1 123 在node1节点创建了 node1.1节点, 存的数据是123
  • ls [-s] path [watch]
    • 指定节点下的所有子节点,ls只能查看第一级的所有子节点
    • 其中path指定数据节点的路径
    • -s 是列出节点以及节点状态, 是ls和stat命令的结合
    • 加上watch参数表示监听path路径下所有子节点的变化
    • ls /node1 查看node1下的所有一级子节点
  • get path [watch]
    • 获取path节点的数据内容和属性信息
    • 加上watch参数表示监听path路径下所有子节点的变化
    • get /node1 查看node1中的数据
  • set path data [version]
    • 更新path路径节点的数据内容
    • data为更新的数据
    • version为指定数据被更新的版本,如果version比当前的dataVersion还小会报错
    • set /node1 qwer
  • delete path [version]
    • 删除路径为path的节点
    • version指定被删除数据的版本,一般不指定,表示删除最新的数据版本,若version为旧的版本则会报错
    • delete /node1
  • stat path
    • 查看节点状态
    • stat /node1

curator

基本使用

依赖
<dependency>
    <groupId>org.apache.curator</groupId>

    <artifactId>curator-recipes</artifactId>

    <version>4.2.0</version>

</dependency>

连接 ZooKeeper 客户端
public class MyTest {
    // 重试之间等待的初始时间
    private static final int BASE_SLEEP_TIME = 1000;
    // 最大重试次数
    private static final int MAX_RETRIES = 3;

    public static void main(String[] args) {

        // 重试策略
        RetryPolicy retryPolicy = new ExponentialBackoffRetry(BASE_SLEEP_TIME, MAX_RETRIES);

        CuratorFramework zkClient = CuratorFrameworkFactory.builder()
            // 要连接的服务器列表
            .connectString("192.168.79.130:2181")
            .retryPolicy(retryPolicy)
            .build();
        
        zkClient.start();
    }
}

创建节点

创建持久化节点

// 创建持久化节点 (默认就是持久化的), 父节点不存在时报错
zkClient.create().forPath("/node1");  // 创建node1父节点
zkClient.create().forPath("/node1/00001");
zkClient.create().withMode(CreateMode.PERSISTENT).forPath("/node1/00002");

// 当父节点不存在时, 会自动创建父节点 更推荐使用
zkClient.create()
    .creatingParentsIfNeeded()
    .withMode(CreateMode.PERSISTENT)
    .forPath("/node1/00001");

创建临时节点

zkClient.create()
    .creatingParentsIfNeeded()
    .withMode(CreateMode.EPHEMERAL)
    .forPath("/node1/00001","数据absdfasf".getBytes());

检查节点是否创建成功

//不为null的话,说明节点创建成功
Stat stat = zkClient.checkExists().forPath("/node1/00001");

获取/更新

获取数据

// 获取节点的数据内容,获取到的是 byte数组
byte[] bytes = zkClient.getData().forPath("/node1/00001");
System.out.println(new String(bytes));

更新节点数据

zkClient.setData().forPath("/node1/00001","qwerqwr".getBytes());

获取某个节点的所有子节点路径

List<String> childrenPaths = zkClient.getChildren().forPath("/node1");

删除节点
zkClient.delete().forPath("/node1/00001");

// 删除节点及其所有子节点
zkClient.delete().deletingChildrenIfNeeded().forPath("/node1");

监听器

监听节点数据变化

// 需要创建zkclient

String path = "/node1";

// 创建 NodeCache 实例
NodeCache nodeCache = new NodeCache(zkClient, path);

// 注册监听器
NodeCacheListener listener = () -> {
    if (nodeCache.getCurrentData() != null) {
        String data = new String(nodeCache.getCurrentData().getData());
        System.out.println("节点数据变化: " + data);
    } else {
        System.out.println("节点被删除");
    }
};
nodeCache.getListenable().addListener(listener);

// 启动 NodeCache
nodeCache.start();

// 模拟程序运行一段时间
Thread.sleep(60000);

监听节点状态变化

// 需要创建zkclient

String path = "/node1";

// 创建 TreeCache 实例
TreeCache treeCache = new TreeCache(zkClient, path);

// 注册监听器
TreeCacheListener listener = (curatorFramework, treeCacheEvent) -> {
    switch (treeCacheEvent.getType()) {
        case NODE_ADDED:
            System.out.println("Node added: " + treeCacheEvent.getData().getPath());
            break;
        case NODE_REMOVED:
            System.out.println("Node removed: " + treeCacheEvent.getData().getPath());
            break;
        case NODE_UPDATED:
            System.out.println("Node updated: " + treeCacheEvent.getData().getPath());
            break;
    }
};
treeCache.getListenable().addListener(listener);

// 启动 TreeCache
treeCache.start();

// 模拟程序运行一段时间
Thread.sleep(60000);

监听子节点状态变化

// 需要创建zkclient

String path = "/node1";

PathChildrenCache pathChildrenCache = new PathChildrenCache(zkClient, path, true);

// 给某个节点注册子节点监听器
PathChildrenCacheListener pathChildrenCacheListener = (curatorFramework, pathChildrenCacheEvent) -> {
    if (pathChildrenCacheEvent.getType() == PathChildrenCacheEvent.Type.CHILD_REMOVED) {
        System.out.println("子节点删除");
    } else if (pathChildrenCacheEvent.getType() == PathChildrenCacheEvent.Type.CHILD_ADDED) {
        System.out.println("子节点新增");
    } else if (pathChildrenCacheEvent.getType() == PathChildrenCacheEvent.Type.CHILD_UPDATED) {
        System.out.println("子节点修改");
    }
};

pathChildrenCache.getListenable().addListener(pathChildrenCacheListener);
// 启动pathChildrenCache
pathChildrenCache.start();

// 模拟程序运行一段时间
Thread.sleep(60000);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值