【总结】Apache Curator入门使用

一、Apache Curator简介

  • 解决了watcher的一次性的问题,注册一个watcher可以触发多次

  • Api简单易用

  • 可以递归创建节点

  • 提供ZooKeeper各种应用场景(recipe, 比如:分布式锁服务、集群领导选举、共享计数器、缓存机制、分布式队列等)的抽象封装

  • 提供了常用的Zookeeper工具类

  • 提供了一套Fluent风格的操作API

二、Curator基本操作

1. 重试策略

一共有五种重试策略

  1. 重试策略ExponentialBackoffRetry,重试N次限制总的重试时间

    • baseSleepTimeMs:两次重试之间的间隔时间
    • maxRetries:最大重试次数,如果超过次数就放弃
    • maxSleepMs:最大重试时间,如果超过该时间就放弃
    • 推荐的使用方式为RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 5);
  2. 重试策略RetryNTimes,重试N次

    • n:重试的次数
    • sleepMsBetweenRetries:两次重试之间的间隔时间
    • 推荐的使用方式为RetryPolicy retryPolicy = new RetryNTimes(3, 5000);
  3. 重试策略RetryOneTime,重试一次

    • sleepMsBetweenRetry:两次重试之间的间隔时间
    • 不推荐使用
  4. 重试策略RetryForever,一直在重试

    • retryIntervalMs:两次重试之间的间隔时间
    • 不推荐使用
  5. 重试策略RetryUntilElapsed

    • maxElapsedTimeMs:最大重试时间,重试时间超过maxElapsedTimeMs后,就不再重试
    • sleepMsBetweenRetries:两次重试之间的间隔时间
    • 推荐使用方式为RetryPolicy retryPolicy = new RetryUntilElapsed(2000, 3000);

2. 连接与关闭Zookeeper

//1 重试策略:初试时间为1s 重试10次
RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 10);
//2 通过工厂创建连接
CuratorFramework client = CuratorFrameworkFactory.builder()
        .connectString("192.168.1.110:2181")//连接地址
        .connectionTimeoutMs(3_000)//连接超时时间
        .sessionTimeoutMs(30_000)//会话超时时间
        .retryPolicy(retryPolicy)//重试策略
        .namespace("super")//命名空间,连接后所有的操作都是在这个/super节点之下
        .build();
//3 开启连接
client.start();
//4 关闭连接
client.close();

3. 创建节点

creatingParentsIfNeeded是递归创建节点,如果不存在父节点则同时会创建父节点

创建模式有以下几种:

  • CreateMode.PERSISTENT:永久节点
  • CreateMode.PERSISTENT_SEQUENTIAL:永久顺序节点
  • CreateMode.EPHEMERAL:临时节点
  • CreateMode.EPHEMERAL_SEQUENTIAL:临时顺序节点

权限控制

  • 通过withACL,添加一个权限列表List<ACL>
  • ACL类有两个属性
    • 第一个是perms,可以通过Perms的枚举选择,代表了权限字符串列表:crdwa
    • 第二个是Id,代表着五种权限机制,比如说world、digest等
  • Id类,有两个属性
    • 第一个是scheme,代表权限模式
    • 第二个是id,代表权限模式后面的字符串,如digest模式,user:BASE64(SHA1(password))
List<ACL> acls = new ArrayList<ACL>();
Id demo1 = new Id("digest", AclUtils.getDigestUserPwd("demo1:123456"));
Id demo2 = new Id("digest", AclUtils.getDigestUserPwd("demo2:123456"));
acls.add(new ACL(Perms.ALL, demo1));
acls.add(new ACL(Perms.READ, demo2));
acls.add(new ACL(Perms.DELETE | Perms.CREATE, demo2));

创建节点举例

String nodePath = "/super/demo";
byte[] data = "superme".getBytes();
// 创建节点
client.create().creatingParentsIfNeeded()
		.withMode(CreateMode.PERSISTENT)//创建模式
		.withACL(ZooDefs.Ids.OPEN_ACL_UNSAFE)//权限列表,如果想要将所有递归创建的节点都指定当前的权限,可以多一个参数true,如withACL(ZooDefs.Ids.OPEN_ACL_UNSAFE,true)
		.forPath(nodePath, data);

4. 删除和更新节点

String nodePath = "/super/demo";

// 更新节点数据
byte[] newData = "batman".getBytes();
client.setData().withVersion(0).forPath(nodePath, newData);

// 删除节点
client.delete()
     .guaranteed()                // 如果删除失败,那么在后端还是继续会删除,直到成功
     .deletingChildrenIfNeeded()  // 如果有子节点,就删除
     .withVersion(0)              // 版本号,如果不匹配会报错
     .forPath(nodePath);		  // 删除的节点地址

5. 查询节点以及子节点

String nodePath = "/super/demo";

// 判断节点是否存在,如果不存在则为空
Stat statExist = cto.client.checkExists().forPath(nodePath + "/abc");
System.out.println(statExist);//statExist!=null,则代表节点存在

// 读取节点数据
Stat stat = new Stat();
byte[] data = client.getData()
        .storingStatIn(stat)//将节点的状态信息也读取进来
        .forPath(nodePath);
System.out.println("节点" + nodePath + "的数据为: " + new String(data));
System.out.println("该节点的版本号为: " + stat.getVersion());

 // 查询子节点
 List<String> childNodes = client.getChildren()
         .forPath(nodePath);
 System.out.println("开始打印子节点:");
 for (String s : childNodes) {
     System.out.println(s);
 }

6. 创建watcher

一次性的创建方式

String nodePath = "/super/demo";
// 添加watcher 事件  当使用usingWatcher的时候,监听只会触发一次,监听完毕后就销毁
cto.client.getData().usingWatcher((Watcher) event -> {
    System.out.println("触发了watcher"+event);
}).forPath(nodePath);

cto.client.getData().usingWatcher((CuratorWatcher) event -> {
    System.out.println("触发了watcher"+event);
}).forPath(nodePath);

重复使用父节点的watcher

String nodePath = "/super/demo";

// NodeCache: 监听数据节点的变更,会触发事件
NodeCache nodeCache = new NodeCache(client, nodePath);

nodeCache.start(true);//buildInitial : 初始化的时候获取node的值并且缓存,true代表进行初始化,缓存节点值,默认为false

if (nodeCache.getCurrentData() != null) {
   System.out.println("节点初始化数据为:" + new String(nodeCache.getCurrentData().getData()));
} else {
   System.out.println("节点初始化数据为空...");
}

//创建监听器,当节点数据发生更改或者创建时,就会触发该方法
nodeCache.getListenable().addListener(() -> {
    //如果是因为删除,导致获取不到节点
    if (nodeCache.getCurrentData() == null) {
        System.out.println("节点被删除了~");
        return;
    }
    //获取数据
    String data = new String(nodeCache.getCurrentData().getData());
    //获取触发的节点路径
    String path = nodeCache.getCurrentData().getPath();
    System.out.println("节点路径:" + path + "数据:" + data);
});

重复使用子节点的watcher

String nodePath = "/super/demo";

// 为子节点添加watcher
// PathChildrenCache: 监听数据节点的增删改,会触发事件
String childNodePathCache = nodePath;

//新建一个子节点缓存
PathChildrenCache childrenCache = new PathChildrenCache(client, childNodePathCache, true);//cacheData: 设置缓存节点的数据状态,如果为true,也会将子节点的状态信息缓存下来


/**
 * StartMode: 初始化方式
 * POST_INITIALIZED_EVENT:异步初始化,初始化之后会触发初始化事件(推荐)
 * NORMAL:异步初始化
 * BUILD_INITIAL_CACHE:同步初始化
 */
childrenCache.start(StartMode.POST_INITIALIZED_EVENT);

//获取缓存的子节点数据
List<ChildData> childDataList = childrenCache.getCurrentData();
System.out.println("当前数据节点的子节点数据列表:");
for (ChildData cd : childDataList) {
    String childData = new String(cd.getData());
    System.out.println(childData);
}

//为子节点缓存添加监听器,可以对子节点触发的event的类型进行判断
childrenCache.getListenable().addListener((client, event) -> {
    if (event.getType().equals(PathChildrenCacheEvent.Type.INITIALIZED)) {//初始化事件触发
        System.out.println("子节点初始化ok...");
    } else if (event.getType().equals(PathChildrenCacheEvent.Type.CHILD_ADDED)) {//增加子节点
        String path = event.getData().getPath();
        if (path.equals(ADD_PATH)) {
            System.out.println("添加子节点:" + event.getData().getPath());
            System.out.println("子节点数据:" + new String(event.getData().getData()));
        } else if (path.equals("/super/imooc/e")) {
            System.out.println("添加不正确...");
        }
    } else if (event.getType().equals(PathChildrenCacheEvent.Type.CHILD_REMOVED)) {//删除子节点
        System.out.println("删除子节点:" + event.getData().getPath());
    } else if (event.getType().equals(PathChildrenCacheEvent.Type.CHILD_UPDATED)) {//子节点数据修改事件
        System.out.println("修改子节点路径:" + event.getData().getPath());
        System.out.println("修改子节点数据:" + new String(event.getData().getData()));
    }
});

三、watcher统一配置修改

image-20200921165758514

public class Client1 {

   public CuratorFramework client = null;
   public static final String zkServerPath = "192.168.1.110:2181";

   public Client1() {
      RetryPolicy retryPolicy = new RetryNTimes(3, 5000);
      client = CuratorFrameworkFactory.builder()
            .connectString(zkServerPath)
            .sessionTimeoutMs(10000).retryPolicy(retryPolicy)
            .namespace("workspace").build();
      client.start();
   }
   
   public void closeZKClient() {
      if (client != null) {
         this.client.close();
      }
   }
   
   public final static String CONFIG_NODE_PATH = "/super/demo";
   public final static String SUB_PATH = "/redis-config";
   public static CountDownLatch countDown = new CountDownLatch(1);
   
   public static void main(String[] args) throws Exception {
      //连接Zookeeper
      Client1 cto = new Client1();
      System.out.println("client1 启动成功...");
      
       //需要在父节点添加对子节点的监听
      final PathChildrenCache childrenCache = new PathChildrenCache(cto.client, CONFIG_NODE_PATH, true);
      childrenCache.start(StartMode.BUILD_INITIAL_CACHE);
      
      // 添加监听器
      childrenCache.getListenable().addListener(new PathChildrenCacheListener() {
         public void childEvent(CuratorFramework client, PathChildrenCacheEvent event) throws Exception {
            // 监听节点变化
            if(event.getType().equals(PathChildrenCacheEvent.Type.CHILD_UPDATED)){
               String configNodePath = event.getData().getPath();
               if (configNodePath.equals(CONFIG_NODE_PATH + SUB_PATH)) {
                  System.out.println("监听到配置发生变化,节点路径为:" + configNodePath);
                  
                  // 读取节点数据
                  String jsonConfig = new String(event.getData().getData());
                  System.out.println("节点" + CONFIG_NODE_PATH + "的数据为: " + jsonConfig);
                  
                  // 从json转换配置
                  RedisConfig redisConfig = null;
                  if (StringUtils.isNotBlank(jsonConfig)) {
                     redisConfig = JsonUtils.jsonToPojo(jsonConfig, RedisConfig.class);
                  }
                  
                  // 配置不为空则进行相应操作
                  if (redisConfig != null) {
                     String type = redisConfig.getType();
                     String url = redisConfig.getUrl();
                     String remark = redisConfig.getRemark();
                     // 判断事件
                     if (type.equals("add")) {
                        System.out.println("监听到新增的配置,准备下载...");
                        // ... 连接ftp服务器,根据url找到相应的配置
                        Thread.sleep(500);
                        System.out.println("开始下载新的配置文件,下载路径为<" + url + ">");
                        // ... 下载配置到你指定的目录
                        Thread.sleep(1000);
                        System.out.println("下载成功,已经添加到项目中");
                        // ... 拷贝文件到项目目录
                     } else if (type.equals("update")) {
                        System.out.println("监听到更新的配置,准备下载...");
                        // ... 连接ftp服务器,根据url找到相应的配置
                        Thread.sleep(500);
                        System.out.println("开始下载配置文件,下载路径为<" + url + ">");
                        // ... 下载配置到你指定的目录
                        Thread.sleep(1000);
                        System.out.println("下载成功...");
                        System.out.println("删除项目中原配置文件...");
                        Thread.sleep(100);
                        // ... 删除原文件
                        System.out.println("拷贝配置文件到项目目录...");
                        // ... 拷贝文件到项目目录
                     } else if (type.equals("delete")) {
                        System.out.println("监听到需要删除配置");
                        System.out.println("删除项目中原配置文件...");
                     }
                     
                     // TODO 视情况统一重启服务
                  }
               }
            }
         }
      });
      
      countDown.await();
      
      cto.closeZKClient();
   }
   
}
  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值