zookeeper常用 java api

9 篇文章 0 订阅

zookeeper常用 java api


  • zk原生api
  1. 连接的创建是异步的,需要开发人员自行编码实现等待
  2. 不支持自动超时重连,需要手动重连会话
  3. Watcher注册一次后失效,需要手动重新Watcher
  4. 不支持递归创建多级目录
  • zkClient
  1. session会话超时重连
  2. 解决Watcher反复注册
  3. 简化API开发
  • Apache Curator
  1. 包含zClient提供的功能
  2. 提供更多复杂问题的解决方案,如:分布式锁
  3. 提供常用Zookeeper工具类
  4. 提供Fluent编程风格(对象.对象.对象.对象…)

Curator API

导入 maven 依赖
<dependency>
  <groupId>org.apache.zookeeper</groupId>
  <artifactId>zookeeper</artifactId>
  <version>3.4.12</version>
</dependency>

<dependency>
  <groupId>org.apache.curator</groupId>
  <artifactId>curator-framework</artifactId>
  <version>4.2.0</version>
</dependency>
<dependency>
  <groupId>org.apache.curator</groupId>
  <artifactId>curator-recipes</artifactId>
  <version>4.2.0</version>
</dependency>
根对象和zookeeper集群地址
// ck客户端实例
protected CuratorFramework client;
// zk的集群地址
private static final String ZK_URL = "192.168.184.10:2181,192.168.184.20:2181,192.168.184.30:2181";
建立连接
/**
 * 构造的时候建立连接
 */
public CuratorService() {
    // 每间隔1000ms重试连接一次,最多重连3次
    RetryPolicy retryPolicy = new RetryNTimes(3, 1000);
    client = CuratorFrameworkFactory.builder().connectString(ZK_URL)
    // 会话超时时间(保持发送心跳),超时会重连
    .sessionTimeoutMs(10000).retryPolicy(retryPolicy)
    // 声明命名空间(可选),所有节点会在该目录的基础下进行操作,类似于eclipse或idea的workspace
    .namespace("seecen")
    // 授权acl,后续创建acl多层节点时,如/a/b,,无法创建/b的节点,因为/a创建好后就需要auth授权才能创建/b
    // 要访问带权限的节点之前,也需要先授权
    .authorization("digest", "bigpig:123456".getBytes())
    .build();
    // 开启连接
    client.start();
}
/**********[main调用]**********/
// 输出连接状态
System.out.println(curatorService.client.getState());
创建节点
/**
 * 创建节点
 * @param createMode 节点类型:持久节点,有序持久节点,临时节点,有序临时节点
 * @param acl 访问控制权限
 * @param path 路径,支持递归创建父级节点
 * @param data 最后路径的数据
 * @throws Exception
 */
public void createNode(CreateMode createMode, List<ACL> acl, String path, byte[] data) throws Exception {
    // creatingParentsIfNeeded 支持递归创建节点,如 /aa/bb/cc
    client.create().creatingParentsIfNeeded().withMode(createMode)
            // 递归多层节点时候,acl也会赋给所有父节点(前提是父节点是新建的)
            // 且创建者需要在链接的时候有授权 .authorization("digest", "bigpig:123456".getBytes())
            .withACL(acl, true)
            .forPath(path, data);
}
/**********[main调用]**********/
// 实例化并创建zk连接
CuratorService curatorService = neCuratorService();
// 该目录会创建在namespace下
curatorService.createNode(CreateMode.PERSISTENT,ZooDefs.Ids.OPEN_ACL_UNSAFE,"/oracle/aliyun/redhat",  "test数据".getBytes("UTF-8"));

/**********[递归创建节点并且赋予acl权限]**********/
// 创建多层节点,并且全部设置权限。需要授权.authorization("digest", "bigpig:123456".getBytes())
String password = DigestAuthenticationProvider.generateDigest("bigpig:123456");
Id digest = new Id("digest", password);
// 授予创建和删除和写的权限
ACL acl1 = new ACL(ZooDefs.Perms.CREATE | ZooDefs.Perms.DELETE | ZooDefs.Perms.WRITE, digest);
curatorService.createNode(CreateMode.PERSISTENT, Collections.singletonList(acl1), "/oracle/aliyun/redhat", "hello world".getBytes());
更新节点
/**
 * 更新节点数据
 * @param version 乐观锁的版本号,对应zk节点中的dataVersion,如果不一致会抛出异常
 * @param path 要修改的节点路径
 * @param data 要修改的新数据
 * @throws Exception
 */
public void updateNode(int version, String path, byte[] data) throws Exception {
    client.setData().withVersion(version).forPath(path, data);
}

/**********[main调用]**********/
// 修改数据,版本号不对,会报乐观锁异常, 0 为版本号对应dataVersion
curatorService.updateNode(0, "/oracle/aliyun/redhat", "更新数据".getBytes("UTF-8"));
删除节点
/**
 * 删除节点
 * @param version
 * @param path
 * @throws Exception
 */
public void deleteNode(int version, String path) throws Exception {
    client.delete()
            .guaranteed()// 保证删除成功。如果网络抖动,后台会继续删除,直到成功
            .deletingChildrenIfNeeded()// 递归删除path下所有子节点路径
            .withVersion(version)
            .forPath(path);
}
/**********[main调用]**********/
// 删除数据,版本号不对,会报乐观锁异常,支持递归删除子节点,0为版本号对应dataVersion
curatorService.deleteNode(0, "/oracle");
判断存在并获取Stat信息

备注:stat信息如下所示

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dHICF8Ho-1573804097680)(2C804C16A63B4797B966AC566DC99B76)]

/**
 * 判断一个路径是否存在,并返回Stat信息
 * @param path 路径
 * @return stat 即命令中stat返回的许多属性值
 * @throws Exception
 */
public Stat exist(String path) throws Exception {
    return client.checkExists().forPath(path);
}
/**********[main调用]**********/
// 判断路径节点是否存在,存在返回stat对象,不存在返回null
Stat stat = curatorService.exist("/oracle/aliyun/redhat");
获取数据
/**
 * 获取指定路径的数据
 * @param path 路径
 * @return byte[]数据
 * @throws Exception
 */
public byte[] getPathData(String path) throws Exception {
    return client.getData().forPath(path);
}
/**********[main调用]**********/
// 获取数据
byte[] pathData = curatorService.getPathData("/oracle/aliyun/redhat");
System.out.println(new String(pathData));
获取所有子节点(不含孙节点)
/**
 * 获取一个path下的子节点(不含孙节点)
 * @param path 指定路径
 * @return List<String> 子节点
 * @throws Exception
 */
public List<String> getChildren(String path) throws Exception {
    return client.getChildren().forPath(path);
}
/**********[main调用]**********/
// 获取路径的子节点,注意:不包含孙节点
List<String> children = curatorService.getChildren("/oracle");
for (String child : children) {
    System.out.println(child);
}
一次性监听节点

该方式只能监听并触发一次

/**
 * 监听路径变化,做出动作,注:该方式只会监听第一次
 * @param path 要监听的路径节点
 * @param watcher 监听对象,具体监听到path后要做的事情
 * @throws Exception
 */
public void oneListen(String path, CuratorWatcher watcher) throws Exception {
    client.getData().usingWatcher(watcher).forPath(path);
}
/**********[main调用]**********/
curatorService.oneListen("/oracle", new CuratorWatcher() {
    public void process(WatchedEvent event) throws Exception {
        System.out.println(event.getPath() + "发生了" + event.getType() + "事件!");
    }
});
重复监听节点
/**
 * 监听路径数据变化,做出动作,注:该方式每一次都会监听,且只对数据改变会触发
 * @param path 要监听的路径节点
 * @return NodeCache节点和数据对象,并且如果不想监听了,需要外面关闭它
 * @throws Exception
 */
public NodeCache anyListen(String path) throws Exception {
    final NodeCache nodeCache = new NodeCache(client, path);
    nodeCache.getListenable().addListener(new NodeCacheListener() {
        public void nodeChanged() throws Exception {
            if(nodeCache.getCurrentData() != null) {
                System.out.println(nodeCache.getPath() + ":" + new String(nodeCache.getCurrentData().getData()));
            }
        }
    });
    // 开始监听
    nodeCache.start();
    return nodeCache;
}
/**********[main调用]**********/
NodeCache nodeCache = curatorService.anyListen("/oracle");
监听子节点的CUD[create,update,delete]变化
/**
 * 重复监听一个路径下的所有子节点(不含孙节点)的CRUD
 * @param path 父节点路径
 * @param pathChildrenCacheListener 监控对象
 * @return PathChildrenCache 如果不想监听了,需要在外面关闭它
 * @throws Exception
 */
public PathChildrenCache anyListenChildren(String path, PathChildrenCacheListener pathChildrenCacheListener) throws Exception {
    PathChildrenCache pathChildrenCache = new PathChildrenCache(client, path, true);
    pathChildrenCache.getListenable().addListener(pathChildrenCacheListener);
    // 异步初始化,初始化之后触发事件
    pathChildrenCache.start(PathChildrenCache.StartMode.POST_INITIALIZED_EVENT);
    return pathChildrenCache;
}
/**********[main调用]**********/
// 重复监听子节点
PathChildrenCache pathChildrenCache = curatorService.anyListenChildren("/oracle", new PathChildrenCacheListener() {
    public void childEvent(CuratorFramework client, PathChildrenCacheEvent event) throws Exception {
        if (event.getType() == PathChildrenCacheEvent.Type.CHILD_ADDED
                || event.getType() == PathChildrenCacheEvent.Type.CHILD_UPDATED
                || event.getType() == PathChildrenCacheEvent.Type.CHILD_REMOVED) {
            System.out.println(event.getData().getPath() + ":" + new String(event.getData().getData()));
        } else if (event.getType() == PathChildrenCacheEvent.Type.INITIALIZED) {
            System.out.println("初始化OK");
        } else if (event.getType() == PathChildrenCacheEvent.Type.CONNECTION_RECONNECTED) {
            System.out.println("连接成功");
        }
    }
});
给已经存在的节点授权
/**
 * 给已经存在的路径节点授权,支持递归.前提需要认证了父节点的权限
 * @param path
 * @param acls
 * @throws Exception
 */
public void grantAcl(String path,int aclVersion, List<ACL> acls) throws Exception{
    client.setACL().withVersion(aclVersion).withACL(acls).forPath(path);
}
/**********[main调用]**********/
String path = "/oracle/aliyun/redhat";
String password = DigestAuthenticationProvider.generateDigest("bigpig:123456");
Id digest = new Id("digest", password);
ACL acl1 = new ACL(ZooDefs.Perms.ALL, digest);
Stat stat = curatorService.exist(path);
if (stat != null) {
    // 需要对应stat中的aclVersion
    curatorService.grantAcl(path, stat.getAversion(), Collections.singletonList(acl1));
}
释放资源

如果要关闭client对象或NodeCache监听对象, 可以使用提供好的工具类CloseableUtils

CloseableUtils.closeQuietly(client);
CloseableUtils.closeQuietly(nodeCache);
完整代码
  • CuratorService.java
import org.apache.curator.RetryPolicy;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.framework.api.CuratorWatcher;
import org.apache.curator.framework.recipes.cache.NodeCache;
import org.apache.curator.framework.recipes.cache.NodeCacheListener;
import org.apache.curator.framework.recipes.cache.PathChildrenCache;
import org.apache.curator.framework.recipes.cache.PathChildrenCacheListener;
import org.apache.curator.retry.RetryNTimes;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.data.ACL;
import org.apache.zookeeper.data.Stat;

import java.util.List;

/**
 * Curator封装类
 */
public class CuratorService {

    // ck客户端实例
    protected CuratorFramework client;
    // zk的集群地址
    private static final String ZK_URL = "192.168.184.10:2181,192.168.184.20:2181,192.168.184.30:2181";

    /**
     * 构造的时候建立连接
     */
    public CuratorService() {
        // 每间隔1000ms重试连接一次,最多重连3次
        RetryPolicy retryPolicy = new RetryNTimes(3, 1000);
        client = CuratorFrameworkFactory.builder().connectString(ZK_URL)
                // 会话超时时间(保持发送心跳),超时会重连
                .sessionTimeoutMs(10000).retryPolicy(retryPolicy)
                // 声明命名空间(可选),所有节点会在该目录的基础下进行操作,类似于eclipse或idea的workspace
                .namespace("seecen")
                // 授权acl,后续创建acl多层节点时,如/a/b,,无法创建/b的节点,因为/a创建好后就需要auth授权才能创建/b
                // 要访问带权限的节点之前,也需要先授权
                .authorization("digest", "bigpig:123456".getBytes())
                .build();
        // 开启连接
        client.start();
    }

    /**
     * 创建节点
     * @param createMode 节点类型:持久节点,有序持久节点,临时节点,有序临时节点
     * @param acl 访问控制权限
     * @param path 路径,支持递归创建父级节点
     * @param data 最后路径的数据
     * @throws Exception
     */
    public void createNode(CreateMode createMode, List<ACL> acl, String path, byte[] data) throws Exception {
        // creatingParentsIfNeeded 支持递归创建节点,如 /aa/bb/cc
        client.create().creatingParentsIfNeeded().withMode(createMode)
                // 递归多层节点时候,acl也会赋给所有父节点(前提是父节点是新建的)
                // 且创建者需要在链接的时候有授权 .authorization("digest", "bigpig:123456".getBytes())
                .withACL(acl, true)
                .forPath(path, data);
    }

    /**
     * 更新节点数据
     * @param version 乐观锁的版本号,对应zk节点中的dataVersion,如果不一致会抛出异常
     * @param path 要修改的节点路径
     * @param data 要修改的新数据
     * @throws Exception
     */
    public void updateNode(int version, String path, byte[] data) throws Exception {
        client.setData().withVersion(version).forPath(path, data);
    }

    /**
     * 删除节点
     * @param version
     * @param path
     * @throws Exception
     */
    public void deleteNode(int version, String path) throws Exception {
        client.delete()
                .guaranteed()// 保证删除成功。如果网络抖动,后台会继续删除,直到成功
                .deletingChildrenIfNeeded()// 递归删除path下所有子节点路径
                .withVersion(version)
                .forPath(path);
    }

    /**
     * 判断一个路径是否存在,并返回Stat信息
     * @param path 路径
     * @return stat 即命令中stat返回的许多属性值
     * @throws Exception
     */
    public Stat exist(String path) throws Exception {
        return client.checkExists().forPath(path);
    }

    /**
     * 获取指定路径的数据
     * @param path 路径
     * @return byte[]数据
     * @throws Exception
     */
    public byte[] getPathData(String path) throws Exception {
        return client.getData().forPath(path);
    }

    /**
     * 获取一个path下的子节点(不含孙节点)
     * @param path 指定路径
     * @return List<String> 子节点
     * @throws Exception
     */
    public List<String> getChildren(String path) throws Exception {
        return client.getChildren().forPath(path);
    }


    /**
     * 监听路径变化,做出动作,注:该方式只会监听第一次
     * @param path 要监听的路径节点
     * @param watcher 监听对象,具体监听到path后要做的事情
     * @throws Exception
     */
    public void oneListen(String path, CuratorWatcher watcher) throws Exception {
        client.getData().usingWatcher(watcher).forPath(path);
    }

    /**
     * 监听路径数据变化,做出动作,注:该方式每一次都会监听,且只对数据改变会触发
     * @param path 要监听的路径节点
     * @return NodeCache节点和数据对象,并且如果不想监听了,需要外面关闭它
     * @throws Exception
     */
    public NodeCache anyListen(String path) throws Exception {
        final NodeCache nodeCache = new NodeCache(client, path);
        nodeCache.getListenable().addListener(new NodeCacheListener() {
            public void nodeChanged() throws Exception {
                if(nodeCache.getCurrentData() != null) {
                    System.out.println(nodeCache.getPath() + ":" + new String(nodeCache.getCurrentData().getData()));
                }
            }
        });
        // 开始监听
        nodeCache.start();
        return nodeCache;
    }

    /**
     * 监听一个路径下的所有子节点(不含孙节点)的CRUD
     * @param path 父节点路径
     * @param pathChildrenCacheListener 监控对象
     * @return PathChildrenCache 如果不想监听了,需要在外面关闭它
     * @throws Exception
     */
    public PathChildrenCache anyListenChildren(String path, PathChildrenCacheListener pathChildrenCacheListener) throws Exception {
        PathChildrenCache pathChildrenCache = new PathChildrenCache(client, path, true);
        pathChildrenCache.getListenable().addListener(pathChildrenCacheListener);
        // 异步初始化,初始化之后触发事件
        pathChildrenCache.start(PathChildrenCache.StartMode.POST_INITIALIZED_EVENT);
        return pathChildrenCache;
    }

    /**
     * 给已经存在的路径节点授权,支持递归.前提需要认证了父节点的权限
     * @param path
     * @param acls
     * @throws Exception
     */
    public void grantAcl(String path,int aclVersion, List<ACL> acls) throws Exception{
        client.setACL().withVersion(aclVersion).withACL(acls).forPath(path);
    }
}
  • Test.java

import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.api.CuratorWatcher;
import org.apache.curator.framework.recipes.cache.*;
import org.apache.curator.framework.recipes.locks.InterProcessMutex;
import org.apache.curator.utils.CloseableUtils;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.ZooDefs;
import org.apache.zookeeper.data.ACL;
import org.apache.zookeeper.data.Id;
import org.apache.zookeeper.data.Stat;
import org.apache.zookeeper.server.auth.DigestAuthenticationProvider;

import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class Test {


    public static void main(String[] args) throws Exception {
        // 实例化并创建zk连接
        CuratorService curatorService = new CuratorService();
        // 输出连接状态(STARTED)
        System.out.println(curatorService.client.getState());
        /*递归创建节点*/
//        curatorService.createNode(CreateMode.PERSISTENT,
//                ZooDefs.Ids.OPEN_ACL_UNSAFE,
//                "/oracle/aliyun/redhat",  // 注:该目录会创建在namespace下
//                "test数据".getBytes("UTF-8"));
        /*给已经存在的节点授权*/
//        String path = "/oracle/aliyun/redhat";
//        String password = DigestAuthenticationProvider.generateDigest("bigpig:123456");
//        Id digest = new Id("digest", password);
//        ACL acl1 = new ACL(ZooDefs.Perms.CREATE | ZooDefs.Perms.DELETE | ZooDefs.Perms.WRITE, digest);
//        ACL acl1 = new ACL(ZooDefs.Perms.ALL, digest);
//        Stat stat = curatorService.exist(path);
//        if (stat != null) {
//            curatorService.grantAcl(path, stat.getAversion(), Collections.singletonList(acl1));
//        }
        /*递归创建节点并且赋予acl权限*/

//        // 创建多层节点,并且全部设置权限。需要授权.authorization("digest", "bigpig:123456".getBytes())
//        String password = DigestAuthenticationProvider.generateDigest("bigpig:123456");
//        Id digest = new Id("digest", password);
//        ACL acl1 = new ACL(ZooDefs.Perms.CREATE | ZooDefs.Perms.DELETE | ZooDefs.Perms.WRITE, digest);
//        curatorService.createNode(CreateMode.PERSISTENT, Collections.singletonList(acl1), "/oracle/aliyun/redhat", "hello world".getBytes());

        /*修改数据,版本号不对,会报乐观锁异常*/
        // curatorService.updateNode(0, "/oracle/aliyun/redhat", "更新数据".getBytes("UTF-8"));

        /* 删除数据,版本号不对,会报乐观锁异常,支持递归删除子节点*/
        // curatorService.deleteNode(0, "/oracle");
        /*获取数据*/
//        byte[] pathData = curatorService.getPathData("/oracle/aliyun/redhat");
//        System.out.println(new String(pathData));
        /*判断路径节点是否存在,存在返回stat,不存在返回null*/
//        Stat stat = curatorService.exist("/oracle/aliyun/redhat");
//        System.out.println(stat.getVersion());
        /*获取路径的子节点,注意:不包含孙节点*/
//        List<String> children = curatorService.getChildren("/oracle");
//        for (String child : children) {
//            System.out.println(child);
//        }
        /*监听一次节点*/
//        curatorService.oneListen("/oracle", new CuratorWatcher() {
//            @Override
//            public void process(WatchedEvent event) throws Exception {
//                System.out.println(event.getPath() + "发生了" + event.getType() + "事件!");
//            }
//        });
        /*重复监听节点*/
//        NodeCache nodeCache = curatorService.anyListen("/oracle");
//         监听10S后关闭
//        Thread.sleep(1000 * 10);
//        CloseableUtils.closeQuietly(nodeCache);

        /*重复监听子节点*/
//        PathChildrenCache pathChildrenCache = curatorService.anyListenChildren("/oracle", new PathChildrenCacheListener() {
//            public void childEvent(CuratorFramework client, PathChildrenCacheEvent event) throws Exception {
//                if (event.getType() == PathChildrenCacheEvent.Type.CHILD_ADDED
//                        || event.getType() == PathChildrenCacheEvent.Type.CHILD_UPDATED
//                        || event.getType() == PathChildrenCacheEvent.Type.CHILD_REMOVED) {
//                    System.out.println(event.getData().getPath() + ":" + new String(event.getData().getData()));
//                } else if (event.getType() == PathChildrenCacheEvent.Type.INITIALIZED) {
//                    System.out.println("初始化OK");
//                } else if (event.getType() == PathChildrenCacheEvent.Type.CONNECTION_RECONNECTED) {
//                    System.out.println("连接成功");
//                }
//            }
//        });


        /**
         * 阻塞程序不结束
         * 1. 如果创建的是临时节点,这里阻塞;否则程序运行完关闭,临时节点也被删除了
         * 2. 监听节点,然后去zkCli.sh里面修改,如果这里不阻塞,程序就结束了
         * **/
        // System.in.read();
        // 关闭连接
        CloseableUtils.closeQuietly(curatorService.client);
        // 输出连接状态(STOPPED)
        System.out.println(curatorService.client.getState());


    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值