Java操作Zookeeper

前言

本篇主要讲解Java中如何操作Zookeeper,包括同步阻塞式的API和异步非阻塞式的API两大类。完整代码请点击【源代码】目录。

前置阅读

  • zookeeper从入门到放弃 这篇文章讲解了zookeeper基础知识,以及本文所用到的zookeeper集群的搭建详细过程。

实验环境

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

<!-- 本篇使用的工具类,如无需要可以自行编写相关代码 -->
<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
    <version>27.1-jre</version>
</dependency>

同步阻塞

Java操作zookeeper无非就是两个步骤:连接操作

连接

首先看下连接部分

// 常量/静态变量部分
private static final String[] ZK_SERVER = {
        "192.168.1.101:2181", "192.168.1.102:2181",
        "192.168.1.103:2181", "192.168.1.104:2181",
};
private static final int SESSION_TIMEOUT = 3000;
private static final CountDownLatch latch = new CountDownLatch(1);

private static ZooKeeper zooKeeper;

// 连接部分
/**
 * 初始化连接
 */
private static void init(String[] socketArr) throws InterruptedException, IOException {
    zooKeeper = new ZooKeeper(Joiner.on(",").join(socketArr), SESSION_TIMEOUT, new Watcher() {
        @Override
        public void process(WatchedEvent event) {
            Event.KeeperState state = event.getState();
            /**
             * 此处并不是所有的case
             * @see Watcher.Event.KeeperState
             */
            switch (state) {
                case SyncConnected:
                    System.out.println("ZooKeeper SyncConnected");
                    latch.countDown();
                    break;
                default:
                    System.out.println("ZooKeeper default");
                    break;
            }
        }
    });
    // 等Zookeeper连接完成后再获取
    latch.await();
}

创建Zookeeper实例的时候有三个参数,方法定义如下

public ZooKeeper(String connectString, int sessionTimeout, Watcher watcher)

除了要连接的ZK server、超时时间,还有一个Watcher,这个对象用来处理一系列监听事件。但是,此处传入的Watchersession级别的,跟path/Znode无关。也就是说不能监听path/Znode的变化,只能监听连接。

Zookeeper对象的创建和连接是异步的,也就是通过new ZooKeeper的方式拿到的Zookeeper对象,可能是还没有连接上ZK server的对象。所以需要CountDownLatch工具类结合Watcher上的SyncConnected事件,在连接完成后再返回Zookeeper对象。

操作

有了zookeeper对象之后,就可以操作zookeeper了。

/**
 * 创建持久非序列节点
 */
public static String create(String path, byte[] data) throws KeeperException, InterruptedException, IOException {
    if (zooKeeper == null) {
        init(ZK_SERVER);
    }
    return zooKeeper.create(path, data, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
}

create方法定义如下

public String create(final String path, byte data[], List<ACL> acl, CreateMode createMode)

有四个参数,分别代表创建的Znode、数据、权限acl、Znode类型。ZooDefs.Ids.OPEN_ACL_UNSAFE表示不需要权限,CreateMode.PERSISTENT表示创建持久非序列节点

/**
 * 删除节点
 */
public static void delete(String path) throws KeeperException, InterruptedException, IOException {
    if (zooKeeper == null) {
        init(ZK_SERVER);
    }
    zooKeeper.delete(path, -1);
}

delete方法定义如下

public void delete(final String path, int version)

path表示要删除的Znode,version表示数据版本,如果给定的版本为-1,则它匹配任何节点的版本

/**
 * 更新数据
 */
public static Stat set(String path, byte[] data) throws KeeperException, InterruptedException, IOException {
    if (zooKeeper == null) {
        init(ZK_SERVER);
    }
    return zooKeeper.setData(path, data, -1);
}

setData方法定义如下

public Stat setData(final String path, byte data[], int version)

其中path表示要修改的Znode,data[]表示更新的数据,version表示数据版本,如果给定的版本为-1,则它匹配任何节点的版本

/**
 * 获取数据
 */
public static byte[] get(String path) throws KeeperException, InterruptedException, IOException {
    if (zooKeeper == null) {
        init(ZK_SERVER);
    }
    Stat stat = new Stat();
    byte[] data = zooKeeper.getData(path, null, stat);
    System.out.println("Zxid is: 0x" + Long.toHexString(stat.getCzxid()));
    return data;
}

getData方法定义如下

public byte[] getData(String path, boolean watch, Stat stat)
public byte[] getData(final String path, Watcher watcher, Stat stat)

path表示要获取的Znode,Stat表示节点元数据metadata)。第二个参数如果是boolean类型,表示是否需要监听连接事件(和New Zookeeper时一样的事件),而传入Watcher对象也表示事件监听器,这里监听的事件是Znode节点被修改的事件,不同于new Zookeeper时的监听。并且这个监听器只能在查询时传入(getDataexist),并且是一次性的,想要继续监听,需要再次传入监听器。

craete的时候,如果Znode已经存在,会抛出异常,在deletesetDatagetData的时候,如果Znode不存在,也会抛出异常。

源代码

package com.sicimike.zk;

import com.google.common.base.Joiner;
import org.apache.zookeeper.*;
import org.apache.zookeeper.data.Stat;

import java.io.IOException;
import java.util.concurrent.CountDownLatch;

/**
 * @author sicimike
 * @create 2020-05-03 14:17
 */
public class ZookeeperSyncUtil {

    private static final String[] ZK_SERVER = {
            "192.168.1.101:2181", "192.168.1.102:2181",
            "192.168.1.103:2181", "192.168.1.104:2181",
    };
    private static final int SESSION_TIMEOUT = 3000;
    private static final CountDownLatch latch = new CountDownLatch(1);

    private static ZooKeeper zooKeeper;

    /**
     * 获取zookeeper实例
     *
     * @param socketArr
     * @return
     * @throws InterruptedException
     * @throws IOException
     */
    private static void init(String[] socketArr) throws InterruptedException, IOException {
        zooKeeper = new ZooKeeper(Joiner.on(",").join(socketArr), SESSION_TIMEOUT, new Watcher() {
            @Override
            public void process(WatchedEvent event) {
                Event.KeeperState state = event.getState();
                /**
                 * 此处并不是所有的case
                 * @see Watcher.Event.KeeperState
                 */
                switch (state) {
                    case SyncConnected:
                        System.out.println("ZooKeeper SyncConnected");
                        latch.countDown();
                        break;
                    default:
                        System.out.println("ZooKeeper default");
                        break;
                }
            }
        });
        // 等Zookeeper连接完成后再获取
        latch.await();
    }

    /**
     * 创建持久非序列节点
     *
     * @param path
     * @param data
     * @return
     * @throws KeeperException
     * @throws InterruptedException
     */
    public static String create(String path, byte[] data) throws KeeperException, InterruptedException, IOException {
        if (zooKeeper == null) {
            init(ZK_SERVER);
        }
        return zooKeeper.create(path, data, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
    }


    /**
     * 删除节点
     *
     * @param path
     * @throws KeeperException
     * @throws InterruptedException
     */
    public static void delete(String path) throws KeeperException, InterruptedException, IOException {
        if (zooKeeper == null) {
            init(ZK_SERVER);
        }
        zooKeeper.delete(path, -1);
    }

    /**
     * 更新数据
     *
     * @param path
     * @param data
     * @return
     * @throws KeeperException
     * @throws InterruptedException
     * @throws IOException
     */
    public static Stat set(String path, byte[] data) throws KeeperException, InterruptedException, IOException {
        if (zooKeeper == null) {
            init(ZK_SERVER);
        }
        return zooKeeper.setData(path, data, -1);
    }

    /**
     * 获取数据
     *
     * @param path
     * @return
     * @throws KeeperException
     * @throws InterruptedException
     */
    public static byte[] get(String path) throws KeeperException, InterruptedException, IOException {
        if (zooKeeper == null) {
            init(ZK_SERVER);
        }
        Stat stat = new Stat();
        byte[] data = zooKeeper.getData(path, false, stat);
        System.out.println("Zxid is: 0x" + Long.toHexString(stat.getCzxid()));
        return data;
    }

    /**
     *
     * @param path
     * @param watcher
     * @return
     * @throws KeeperException
     * @throws InterruptedException
     * @throws IOException
     */
    public static byte[] get(String path, Watcher watcher) throws KeeperException, InterruptedException, IOException {
        if (zooKeeper == null) {
            init(ZK_SERVER);
        }
        Stat stat = new Stat();
        return zooKeeper.getData(path, watcher, stat);
    }
}

测试代码

package com.sicimike.zk;

import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;

import java.io.IOException;

/**
 * @author sicimike
 * @create 2020-05-03 12:05
 */
public class ZookeeperDemo {

    public static void main(String[] args) throws IOException, InterruptedException, KeeperException {

        String path = ZookeeperSyncUtil.create("/sicimike", "hello world".getBytes());

        byte[] bytes = ZookeeperSyncUtil.get(path, new Watcher() {
            @Override
            public void process(WatchedEvent event) {
                /**
                 * @see org.apache.zookeeper.Watcher.Event.EventType
                 */
                switch (event.getType()) {
                    case NodeDataChanged:
                        System.out.println("Zookeeper NodeDataChanged event");
                        break;
                }
            }
        });
        System.out.println(new String(bytes));

        ZookeeperSyncUtil.set(path, "hello sicimike".getBytes());

        bytes = ZookeeperSyncUtil.get(path);
        System.out.println(new String(bytes));

        ZookeeperSyncUtil.delete(path);
    }
}

执行结果

ZooKeeper SyncConnected
hello world
Zookeeper NodeDataChanged event
Zxid is: 0x50000006b
hello sicimike

异步非阻塞

zookeeper还提供了一套异步非阻塞的API,使用方式和阻塞式的差不多,只是之前是通过返回值的形式告诉调用者操作的结果,现在是通过回调的方式,直接给出源代码

源代码

package com.sicimike.zk;

import com.google.common.base.Joiner;
import org.apache.zookeeper.*;

import java.io.IOException;
import java.util.concurrent.CountDownLatch;

/**
 * @author sicimike
 * @create 2020-05-03 16:32
 */
public class ZookeeperAsyncUtil {

    private static final String[] ZK_SERVER = {
            "192.168.1.101:2181", "192.168.1.102:2181",
            "192.168.1.103:2181", "192.168.1.104:2181",
    };
    private static final int SESSION_TIMEOUT = 3000;
    private static final CountDownLatch latch = new CountDownLatch(1);

    private static ZooKeeper zooKeeper;

    /**
     * 获取zookeeper实例
     *
     * @param socketArr
     * @return
     * @throws InterruptedException
     * @throws IOException
     */
    private static void init(String[] socketArr) throws InterruptedException, IOException {
        zooKeeper = new ZooKeeper(Joiner.on(",").join(socketArr), SESSION_TIMEOUT, new Watcher() {
            @Override
            public void process(WatchedEvent event) {
                Event.KeeperState state = event.getState();
                /**
                 * 此处并不是所有的case
                 * @see Event.KeeperState
                 */
                switch (state) {
                    case SyncConnected:
                        System.out.println("ZooKeeper SyncConnected");
                        latch.countDown();
                        break;
                    default:
                        System.out.println("ZooKeeper default");
                        break;
                }
            }
        });
        // 等Zookeeper连接完成后再获取
        latch.await();
    }

    public static void create(String path, byte[] data, AsyncCallback.StringCallback callback, Object ctx) throws InterruptedException, IOException {
        if (zooKeeper == null) {
            init(ZK_SERVER);
        }
        zooKeeper.create(path, data, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL, callback, ctx);
    }


    public static void delete(String path, AsyncCallback.VoidCallback callback, Object ctx) throws InterruptedException, IOException {
        if (zooKeeper == null) {
            init(ZK_SERVER);
        }
        zooKeeper.delete(path, -1, callback, ctx);
    }

    public static void set(String path, byte[] data, AsyncCallback.StatCallback callback, Object ctx) throws InterruptedException, IOException {
        if (zooKeeper == null) {
            init(ZK_SERVER);
        }
        zooKeeper.setData(path, data, -1, callback, ctx);
    }

    public static void get(String path, AsyncCallback.DataCallback callback, Object ctx) throws InterruptedException, IOException {
        if (zooKeeper == null) {
            init(ZK_SERVER);
        }
        zooKeeper.getData(path, false, callback, ctx);
    }

    public static void get(String path, Watcher watcher, AsyncCallback.DataCallback callback, Object ctx) throws InterruptedException, IOException {
        if (zooKeeper == null) {
            init(ZK_SERVER);
        }
        zooKeeper.getData(path, watcher, callback, ctx);
    }
}

其中Zookeeper对象的获取还是阻塞的,有兴趣的同学可以改成异步的。

测试代码

package com.sicimike.zk;

import org.apache.zookeeper.AsyncCallback;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.data.Stat;

import java.io.IOException;

/**
 * @author sicimike
 * @create 2020-05-03 12:05
 */
public class ZookeeperDemo {

    public static void main(String[] args) throws IOException, InterruptedException {

        ZookeeperAsyncUtil.create("/sicimike", "hello".getBytes(), new AsyncCallback.StringCallback() {
            @Override
            public void processResult(int rc, String path, Object ctx, String name) {
                System.out.println("create: " + rc);
                System.out.println("create: " + path);
                System.out.println("create: " + ctx);
                System.out.println("create: " + name);
            }
        }, "I am create ctx");

        ZookeeperAsyncUtil.set("/sicimike", "hello world".getBytes(), new AsyncCallback.StatCallback() {
            @Override
            public void processResult(int rc, String path, Object ctx, Stat stat) {
                System.out.println("set: " + rc);
                System.out.println("set: " + path);
                System.out.println("set: " + ctx);
                System.out.println("set: 0x" + Long.toHexString(stat.getCzxid()));
            }
        }, "I am set ctx");

        ZookeeperAsyncUtil.get("/sicimike", new Watcher() {
            @Override
            public void process(WatchedEvent event) {
                /**
                 * @see org.apache.zookeeper.Watcher.Event.EventType
                 */
                switch (event.getType()) {
                    case NodeDeleted:
                        System.out.println("Zookeeper NodeDeleted event");
                        break;
                }
            }
        }, new AsyncCallback.DataCallback() {
            @Override
            public void processResult(int rc, String path, Object ctx, byte[] data, Stat stat) {
                System.out.println("get: " + rc);
                System.out.println("get: " + path);
                System.out.println("get: " + ctx);
                System.out.println("get: " + new String(data));
                System.out.println("get: 0x" + Long.toHexString(stat.getCzxid()));
            }
        }, "I am get ctx");

        ZookeeperAsyncUtil.delete("/sicimike", new AsyncCallback.VoidCallback() {
            @Override
            public void processResult(int rc, String path, Object ctx) {
                System.out.println("delete: " + rc);
                System.out.println("delete: " + path);
                System.out.println("delete: " + ctx);
            }
        }, "I am delete ctx");

        // 阻塞主线程
        System.in.read();
    }
}

执行结果

ZooKeeper SyncConnected
create: 0
create: /sicimike
create: I am create ctx
create: /sicimike
set: 0
set: /sicimike
set: I am set ctx
set: 0x50000008d
get: 0
get: /sicimike
get: I am get ctx
get: hello world
get: 0x50000008d
Zookeeper NodeDeleted event
delete: 0
delete: /sicimike
delete: I am delete ctx

可以看到改成异步非阻塞的API之后,调用方变得复杂了起来。

回调函数的参数大致有如下几个

  • int rc:表示返回码或调用结果,0表示成功,具体的值参考org.apache.zookeeper.KeeperException.Code
  • String path:传递给异步调用的路径
  • Object ctx:传递给异步调用的任何上下文对象
  • String name:创建的Znode名称,如果创建的是非序列节点,则和path相同
  • byte[] data:Znode数据
  • Stat stat:Znode元数据

总结

本文主要讲解Java对Zookeeper简单的操作,包括同步阻塞的方式和异步回调的方式,以及Zookeeper的事件监听器的使用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值