Zookeeper的基本使用及概述(四) ------ zookeeper api的使用

Zookeeper的基本使用及概述(四) ------ zookeeper api的使用

之前对使用命令行操作zookeeper进行了说明明,这里介绍怎么使用java api来对zookeeper进行操作,以及对zookeeper原生api进行简单的封装。

1. 开发环境准备

Java版本: jdk 1.8
开发工具: idea 2019.3.4
maven: 3.6.1
zookeeper: 3.5.10
linux发行版:centos 7

创建maven工程引入Zookeeper依赖

       <dependency>
            <groupId>org.apache.zookeeper</groupId>
            <artifactId>zookeeper</artifactId>
            <version>3.5.10</version>
        </dependency>

2. 代码编写

(1) 编写 IZookpConnection接口

这个接口的主要目的是用来连接zookeeper服务并提供一些基本的API用以操作zookeeper。

/**
 * @Author 笔墨画诗
 * @Version 1.0.0
 * @Create 2022/9/27 18:46
 * @Desc Zookeeper连接以及基本操作api
 */
public interface IZookpConnection {

    /**
     * 获取zookeeper对象
     * @return
     * @throws IOException
     */
    ZooKeeper getZooKeeper() throws IOException;

    /**
     * 创建数据节点
     * @param path
     * @param data
     * @param mode
     * @throws KeeperException
     * @throws InterruptedException
     */
    void create(String path, String data, CreateMode mode) throws KeeperException, InterruptedException;

    /**
     * 获取直系子节点列表
     * @param path
     * @return
     * @throws KeeperException
     * @throws InterruptedException
     */
    List<String> getChildrenNode(String path) throws KeeperException, InterruptedException;

    /**
     * 更新节点数据
     * @param path
     * @param data
     * @throws KeeperException
     * @throws InterruptedException
     */
    void setData(String path, String data) throws KeeperException, InterruptedException;

    /**
     * 获取节点数据
     * @param path
     * @return
     * @throws KeeperException
     * @throws InterruptedException
     */
    String getData(String path) throws KeeperException, InterruptedException;

    /**
     * 删除节点
     * @param path
     * @throws KeeperException
     * @throws InterruptedException
     */
    void delete(String path) throws KeeperException, InterruptedException;
}

(2) 实现 IZookpConnection接口

对IZookpConnection接口进行实现。

/**
 * @Author 笔墨画诗
 * @Version 1.0.0
 * @Create 2022/9/29 18:49
 * @Desc 默认实现
 */
public class DefaultZookpConnectionImpl implements IZookpConnection {

    public static final int DEFAULT_TIMEOUT = 100000;  // 默认超时时间,一定要大于心跳时间
    private ZooKeeper zooKeeper;                       // 实际操作zookeeper的对象
    private String url;                                // 将要连接的服务器地址
    private Watcher watcher;                           // 事件监听器
    private int timeout;                               // 超时时间

    public DefaultZookpConnectionImpl(String url, Watcher watcher){
        this(url,watcher,DEFAULT_TIMEOUT);
    }

    public DefaultZookpConnectionImpl(String url, Watcher watcher,int timeout){
        this.url     = url;
        this.watcher = watcher;
        this.timeout = timeout;
        connect();
    }

    @Override
    public ZooKeeper getZooKeeper() {
        return this.zooKeeper;
    }
	/**
	 * 连接zookeeper服务器
	 */
    private void connect() {
        try {
            this.zooKeeper = new ZooKeeper(url,timeout,watcher);
        } catch (IOException e) {
            throw new ZookpException("Zookeeper Connect Failed",e);
        }
    }

    @Override
    public void create(String path, String data, CreateMode mode) throws KeeperException, InterruptedException {
        ArrayList<ACL> acl = ZooDefs.Ids.OPEN_ACL_UNSAFE; 
        /**
          * path :  节点创建的路径
          * data[] :节点创建要保存的数据,是个byte类型的
          * acl :   节点创建的权限信息(4种类型)
                * ANYONE_ID_UNSAFE : world模式设置id
                * AUTH_IDS :        为auth模式设置的ID,代表了任何已经被确认的用户。它将被客户机验证的ID替换。
                * OPEN_ACL_UNSAFE : 这是⼀个完全开放的ACL(常⽤)-->
                * CREATOR_ALL_ACL : 此ACL授予创建者身份验证ID的所有权限
          * mode :创建节点的类型(4种类型)
                * PERSISTENT:           持久节点
                * PERSISTENT_SEQUENTIAL:持久顺序节点
                * EPHEMERAL:            临时节点
                * EPHEMERAL_SEQUENTIAL: 临时顺序节点
          * this.zookeeper.create(path,data,acl,mode);
          */ 
        this.zooKeeper.create(path,data.getBytes(),acl,mode);
    }

    @Override
    public List<String> getChildrenNode(String path) throws KeeperException, InterruptedException {
        /**
         * 获取子节点
         * path:  节点路径
         * watch: 是否监听 当节点数据发生变华会触发监听
         *     true:  表示开启监听 
         *     false: 不开启监听
         * zooKeeper.getChildren(path, true);
         */
        List<String> childNodes = zooKeeper.getChildren(path, true); 
        return childNodes== null ? Collections.EMPTY_LIST : childNodes;
    }

    @Override
    public void setData(String path, String data) throws KeeperException, InterruptedException {
        zooKeeper.setData(path,data.getBytes(),-1); // -1表示使用最新的数据
    }

    @Override
    public String getData(String path) throws KeeperException, InterruptedException {
        /**
         * 获取节点数据 
         * path:  节点路径
         * watch: 是否监听 true 表示开启监听 false 不开启监听
         * stat:  节点状态
         *     null:  表示获取最新数据
         * zooKeeper.getData(path,false,null);
         */
        byte[] data = zooKeeper.getData(path, false, null);
        return new String(data,"utf-8");
    }

    @Override
    public void delete(String path) throws KeeperException, InterruptedException {
        /**
         * 判断节点是否存在
         * path:  节点路径
         * watch: 是否监听 true 表示开启监听 false 不开启监听
         * zooKeeper.exists(path, true);
         */
        Stat exists = this.zooKeeper.exists(path, false);
        if (exists == null){
            throw new ZookpException("Node Path " + path + " is Not Exist");
        }
        this.zooKeeper.delete(path,-1);
    }
}

(3) ZookpClient类

定义一个zookeeper客户端用以对外提供服务,实际上只是对zookeeper api的简单封装。
另外,ZooKeeper 客户端和服务端会话的建⽴是⼀个异步的过程,也就是说在程序中,构造⽅法会在处理完客户端初始化⼯作后⽴即返回,在⼤多数情况下,此时并没有真正建⽴好⼀个可⽤的会话,在会话的⽣命周期中处于“CONNECTING”的状态。 当该会话真正创建完毕后ZooKeeper服务端会向会话对应的客户端发送⼀个事件通知Watcher,以告知客户端,客户端只有在获取这个通知之后,才算真正建⽴了会话。

/**
 * @Author 笔墨画诗
 * @Version 1.0.0
 * @Create 2022/9/27 18:25
 * @Desc zookeeper 自定义客户端
 * 
 */
public class ZookpClient implements Watcher{

    private IZookpConnection connection;  // 连接zookeeper服务
    private static String serverAddr;     // 服务端地址
    private static int timeout;           // 超时时间
    // 通过读取配置文件初始化参数
    static {
        InputStream rs = null;
        try {
            rs = ZookpClient.class.getClassLoader().getResourceAsStream("zookp_cfg.properties");
            Properties properties = new Properties();
            properties.load(rs);
            serverAddr = properties.getProperty("serverAddr");
            timeout    = Integer.parseInt(properties.getProperty("timeout"));
        } catch (IOException e) {
            throw new ZookpException(e.getMessage(),e);
        }finally {
            try {
                rs.close();
            } catch (IOException e) {
                throw new ZookpException(e.getMessage(),e);
            }
        }
    }

    public ZookpClient(){
        if (serverAddr == null || "".equals(serverAddr)){
            throw new ZookpException("Client Create Failed,URL is Empty or NULL!");
        }
        this.connection = new DefaultZookpConnectionImpl(serverAddr,this,timeout);
    }

    /**
     * 创建节点, 当flag为true时,递归创建其父节点
     * @param path
     * @param data
     * @param mode
     * @param flag
     */
   public void  createNode(String path, String data, CreateMode mode,boolean flag){

       System.out.println("Create Node Start!");
       try {
           this.connection.create(path,data,mode);
       } catch (KeeperException e) {
           String parentNode = path.substring(0, path.lastIndexOf(47));
           if (!flag){
               throw new ZookpException("Create Node Failed, Parent Node:" + parentNode + "is Not Exist",e);
           }
           createNode(parentNode,"",mode,flag);
           createNode(path,data,mode,flag);
       } catch (InterruptedException e) {
           throw new ZookpException(e.getMessage(),e);
       }
       System.out.println("Create Node Done!");
   }

    /**
     * 删除节点, 当flag为true时,遍历删除子节点
     * @param path
     * @param flag
     * @throws KeeperException
     * @throws InterruptedException
     */
    public void deleteNode(String path,boolean flag) throws KeeperException, InterruptedException {

        try {
            connection.delete(path);
        } catch (KeeperException e) {
            if (!flag){
                throw new ZookpException("Node Delete Failed,Path is" + path,e);
            }
            List<String> childrenNode = connection.getChildrenNode(path);
            childrenNode.forEach(node->{
                String nodePath = path + "/" + node;
                try {
                    deleteNode(nodePath,flag);
                } catch (KeeperException ex) {
                    throw new ZookpException("Node Delete Failed,Path is /" + node,ex);
                } catch (InterruptedException ex) {
                    throw new ZookpException("Node Delete Failed,Path is /" + node,ex);
                }
            });
            // 子节点删除完后,再执行删除父节点,还是递归
            deleteNode(path,flag);
        } catch (InterruptedException e) {
            throw new ZookpException("Node Delete Failed,Path is" + path,e);
        }
    }

    public void setNodeData(String path,String data) throws KeeperException, InterruptedException {
        connection.setData(path,data);
    }

    public void getNodeData(String path,String data) throws KeeperException, InterruptedException {
        connection.getData(path);
    }

    public List<String> getChildrenNode(String path) throws KeeperException, InterruptedException {
        return connection.getChildrenNode(path);
    }
	
    @Override
    public void process(WatchedEvent watchedEvent) {
    	// 当这个方法被触发时,才证明客户端会话连接成功
        if (watchedEvent.getState() == Event.KeeperState.SyncConnected){
            System.out.println("Zookeeper Server is Connected!");
        }
    }
}

(4) 配置文件

在resources 目录下创建文件 zookp_cfg_properties,写入超时时间和服务器地址。

serverAddr=192.168.135.80:2181,192.168.135.80:2182,192.168.135.80:2183
timeout=300000

(5) 异常统一处理

/**
 * @Author 笔墨画诗
 * @Version 1.0.0
 * @Create 2022/9/29 19:06
 * @Desc 异常类
 */
public class ZookpException extends RuntimeException{
    private static final long serialVersionUID = 8242584020618974429L;

    public ZookpException() {
    }

    public ZookpException(String message, Throwable cause) {
        super(message, cause);
    }

    public ZookpException(String message) {
        super(message);
    }

    public ZookpException(Throwable cause) {
        super(cause);
    }
}

3 测试

接下来对测试一下自定义的zookeeper客户端。
编写测试类

/**
 * @Author 笔墨画诗
 * @Version 1.0.0
 * @Create 2022/9/29 19:39
 * @Desc
 */
public class ZookpTest {

    private ZookpClient zookpClient = new ZookpClient();
    private String testPath = "/ZookpClient/Test/Version/V1.0/Data";

    @Test
    public void createNodeTest() throws InterruptedException {
        zookpClient.createNode(testPath,"test", CreateMode.PERSISTENT,true);
        zookpClient.createNode("/ZookpClient/Demo","demo", CreateMode.PERSISTENT,false);
    }

    @Test
    public void deleteNodeTest() throws InterruptedException, KeeperException {
        zookpClient.deleteNode("/ZookpClient",true);
    }

    @Test
    public void getChildrenNode() throws KeeperException, InterruptedException {
        List<String> childrenNode = zookpClient.getChildrenNode("/ZookpClient");
        childrenNode.forEach(node-> System.out.println("/ZookpClient 的直系子节点" + node));
    }

    @Test
    public void getNodeDateTest() throws InterruptedException, KeeperException {
        String data = zookpClient.getNodeData(testPath);
        System.out.println(testPath + "节点的数据为: " + data);
    }

    @Test
    public void setNodeDataTest() throws KeeperException, InterruptedException {
        String data = zookpClient.getNodeData(testPath);
        System.out.println(testPath + "节点的数据为: " + data);
        zookpClient.setNodeData("/ZookpClient/Test/Version/V1.0/Data","Data");
        String dataNew = zookpClient.getNodeData(testPath);
        System.out.println(testPath + "更新后节点的数据为: " + dataNew);
    }

}

(1) 测试创建节点

因为zookeeper在创建节点时,该节点的父节点必须存在,父节点不存在则会创建失败,具体如下:

[zk: localhost:2181(CONNECTED) 0] create /ZookpClient/Test/Version/V1.0/Data 000
Node does not exist: /ZookpClient/Test/Version/V1.0/Data
[zk: localhost:2181(CONNECTED) 1] 

所以当flag参数为false时则会抛出异常,为true时就会使用递归的方式,去创建节点,即当发现父节点不存在时先去创建父节点

zookpClient.createNode(testPath,"test", CreateMode.PERSISTENT,true);

创建结果
在这里插入图片描述
在客户端查看创建结果,如下:

[zk: localhost:2181(CONNECTED) 1] ls /
[Seq_Demo0000000012, ZookpClient, zookeeper]
[zk: localhost:2181(CONNECTED) 2] ls -s /ZookpClient 
[Demo, Test]cZxid = 0xd00000021
ctime = Fri Sep 30 11:12:35 CST 2022
mZxid = 0xd00000021
mtime = Fri Sep 30 11:12:35 CST 2022
pZxid = 0xd00000026
cversion = 2
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 0
numChildren = 2

[zk: localhost:2181(CONNECTED) 3] get /ZookpClient/Test/Version/V1.0/Data 
test
[zk: localhost:2181(CONNECTED) 4] get /ZookpClient/Demo 
demo
[zk: localhost:2181(CONNECTED) 5] 

(2) 测试删除节点

zookeeper删除节点刚好和创建节点相反,必须删除所有的子节点,父节点才能被删除,如下:

[zk: localhost:2181(CONNECTED) 5] delete /ZookpClient 
Node not empty: /ZookpClient
[zk: localhost:2181(CONNECTED) 6] ls /
[Seq_Demo0000000012, ZookpClient, zookeeper]
[zk: localhost:2181(CONNECTED) 7] 

所以我们先遍历删除最后 一层的子节点,然后递归,同样的flag为false时还有子节点未被删除则抛出异常,为true时,则正常删除。

zookpClient.deleteNode("/ZookpClient",true);

删除结果
在这里插入图片描述

[zk: localhost:2181(CONNECTED) 7] ls /
[Seq_Demo0000000012, zookeeper]
[zk: localhost:2181(CONNECTED) 8] 

(3) others

剩余的就不再赘述,直接看结果,因为刚刚创建的节点已经删除了,所以需要重新创建一次~
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

笔墨画诗

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值