Java操作zookeeper

Java操作zookeeper总共有三种方式:

1.原生的Java API

2.zkclient

3.curator

 第一种实现代码:

pom.xml

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

 示例的java代码如下:

package zook;

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

import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.Watcher.Event.KeeperState;
import org.apache.zookeeper.ZooDefs;

public class App {

    public static void main(String[] args) throws IOException, InterruptedException, KeeperException {
        String connStr = "192.168.126.128:2181";
        CountDownLatch countDown = new CountDownLatch(1);
        
        Watcher watcher=new Watcher() {
            @Override
            public void process(WatchedEvent event) {
                if (event.getState() == KeeperState.SyncConnected) {
                    System.err.println("eventType:"+event.getType());
                    if(event.getType()==Event.EventType.None){
                        countDown.countDown();
                    }else if(event.getType()==Event.EventType.NodeCreated){
                        System.out.println("listen:节点创建");
                    }else if(event.getType()==Event.EventType.NodeChildrenChanged){
                        System.out.println("listen:子节点修改");
                    }
                }
            }
        };
        
        ZooKeeper zookeeper = new ZooKeeper(connStr, 5000,watcher );
        countDown.await();

        //注册监听,每次都要重新注册,否则监听不到        
        zookeeper.exists("/top/jinyong", watcher);
        
        // 创建节点
        String result = zookeeper.create("/top/jinyong", "一生一世".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
        System.out.println(result);
        
        Thread.sleep(10);

        // 获取节点
        byte[] bs = zookeeper.getData("/top/jinyong", true, null);
        result = new String(bs);
        System.out.println("创建节点后的数据是:" + result);

        // 修改节点
        zookeeper.setData("/top/jinyong", "I love you".getBytes(), -1);
        
        Thread.sleep(10);

        bs = zookeeper.getData("/top/jinyong", true, null);
        result = new String(bs);
        System.out.println("修改节点后的数据是:" + result);

        // 删除节点
        zookeeper.delete("/top/jinyong", -1);
        System.out.println("节点删除成功");
    }

}
View Code

 说明:

1.会话连接是异步的,需要自己去处理。此处用的CountDownLatch

2.Watch需要重复注册,不然就不能生效,比如开始的zookeeper.exists("/top/jinyong", watcher);就是为了注册监听

3.开发的复杂性还是比较高的

4.不支持多节点删除和创建。需要自己去递归。后面有一个关于递归的示例。

第二种实现:

pom.xml

<dependency>
    <groupId>com.101tec</groupId>
    <artifactId>zkclient</artifactId>
    <version>0.10</version>
</dependency>

 示例的Java代码如下:

package zook;

import java.util.List;

import org.I0Itec.zkclient.IZkChildListener;
import org.I0Itec.zkclient.IZkDataListener;
import org.I0Itec.zkclient.IZkStateListener;
import org.I0Itec.zkclient.ZkClient;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.Watcher.Event.KeeperState;

public class Client {

    public static void main(String[] args) throws InterruptedException {
        String connStr = "192.168.126.128:2181";
        ZkClient zk = new ZkClient(connStr);

        // 注册【数据】事件
        zk.subscribeDataChanges("/top/zhuzhu", new IZkDataListener() {

            @Override
            public void handleDataDeleted(String arg0) throws Exception {
                System.err.println("数据删除:" + arg0);

            }

            @Override
            public void handleDataChange(String arg0, Object arg1) throws Exception {
                System.err.println("数据修改:" + arg0 + "------" + arg1);

            }
        });

        zk.subscribeChildChanges("/top", new IZkChildListener() {

            @Override
            public void handleChildChange(String arg0, List<String> arg1) throws Exception {
                System.err.println("子节点发生变化:" + arg0);
                arg1.forEach(f -> {
                    System.out.println("content:" + f);
                });
            }
        });

        List<String> list = zk.getChildren("/");
        list.forEach(e -> {
            System.out.println(e);
        });

        String res = zk.create("/top/zhuzhu", "I love you", CreateMode.PERSISTENT);
        System.out.println("创建节点/top/zhuzhu成功:" + res);

        zk.writeData("/top/zhuzhu", "forerver");
        System.out.println("修改节点/top/zhuzhu数据成功");

        res = zk.readData("/top/zhuzhu");
        System.out.println("节点数据:" + res);

        Thread.sleep(1000);

        zk.delete("/top/zhuzhu");
        System.out.println("删除节点/top/zhuzhu成功");
        
        Thread.sleep(1000);
        
        System.out.println("------------------------------------------------");
        
        for (int i = 0; i < 10; i++) {
            zk.create("/top/zhuzhu", "I love you", CreateMode.PERSISTENT);
            Thread.sleep(1000);
            zk.delete("/top/zhuzhu");
            Thread.sleep(1000);
        }

    }

}
View Code

 说明:

1.subscribe开头的为注册监听的一些方法

2.addAuthInfo和setAcl为权限相关控制

3.普通使用这种方式还是值得推荐的

第三种实现:

pom.xml

<dependency>
    <groupId>org.apache.curator</groupId>
    <artifactId>curator-recipes</artifactId>
    <version>2.11.0</version>
</dependency>

 示例的Java代码如下:

package zook;

import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.framework.recipes.cache.PathChildrenCache;
import org.apache.curator.framework.recipes.cache.PathChildrenCacheEvent;
import org.apache.curator.framework.recipes.cache.PathChildrenCacheListener;
import org.apache.curator.retry.ExponentialBackoffRetry;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.data.Stat;

public class Curator {

    public static void main(String[] args) throws Exception {
        String connStr = "192.168.23.24:2181";
        CuratorFramework cur=CuratorFrameworkFactory.builder()
            .connectString(connStr)
            .connectionTimeoutMs(5000)
            .retryPolicy(new ExponentialBackoffRetry(1000,3))
            .build();
        cur.start();//连接
        
        //创建监听
        PathChildrenCache cache=new PathChildrenCache(cur,"/top",true);
        cache.start();
        cache.rebuild();
        cache.getListenable().addListener(new PathChildrenCacheListener() {
            @Override
            public void childEvent(CuratorFramework framwork, PathChildrenCacheEvent event) throws Exception {
                System.err.println("节点发生变化:"+event.getType());
            }
        });                
        
        Stat stat=cur.checkExists().forPath("/top/zhuzhu");
        if(stat!=null){
            System.out.println("【/top/zhuzhu】节点存在,直接删除");
            cur.delete().forPath("/top/zhuzhu");
        }
        cur.delete().forPath("/top/zhuzhu");
        
        System.in.read();
        
        System.out.println("准备创建【/top/zhuzhu】");
        cur.create().withMode(CreateMode.PERSISTENT)
            .forPath("/top/zhuzhu", "love forever".getBytes());
        System.out.println("节点【/top/zhuzhu】创建成功");
        
        Thread.sleep(1000);
        
        byte[] bs=cur.getData().forPath("/top/zhuzhu");
        System.out.println("数据:"+new String(bs));
        
        Thread.sleep(1000);
        
        cur.delete().forPath("/top/zhuzhu");
        
        Thread.sleep(1000);

    }

    
    /**
     * 三种watcher来做节点的监听
     * pathcache   监视一个路径下子节点的创建、删除、节点数据更新
     * NodeCache   监视一个节点的创建、更新、删除
     * TreeCache   pathcaceh+nodecache 的合体(监视路径下的创建、更新、删除事件),
     * 缓存路径下的所有子节点的数据
     */

    public static void main1(String[] args) throws Exception {        
        String connStr = "192.168.23.24:2181";
        CuratorFramework curatorFramework=CuratorFrameworkFactory.builder()
            .connectString(connStr)
            .connectionTimeoutMs(5000)
            .retryPolicy(new ExponentialBackoffRetry(1000,3))
            .build();
        curatorFramework.start();

        /**
         * 节点变化NodeCache
         */
       /* NodeCache cache=new NodeCache(curatorFramework,"/curator",false);
        cache.start(true);

        cache.getListenable().addListener(()-> System.out.println("节点数据发生变化,变化后的结果" +
                ":"+new String(cache.getCurrentData().getData())));

        curatorFramework.setData().forPath("/curator","菲菲".getBytes());*/


        /**
         * PatchChildrenCache
         */

        PathChildrenCache cache=new PathChildrenCache(curatorFramework,"/event",true);
        cache.start(PathChildrenCache.StartMode.POST_INITIALIZED_EVENT);
        cache.rebuild();
        // Normal / BUILD_INITIAL_CACHE /POST_INITIALIZED_EVENT

        cache.getListenable().addListener((curatorFramework1,pathChildrenCacheEvent)->{
            switch (pathChildrenCacheEvent.getType()){
                case CHILD_ADDED:
                    System.out.println("增加子节点");
                    break;
                case CHILD_REMOVED:
                    System.out.println("删除子节点");
                    break;
                case CHILD_UPDATED:
                    System.out.println("更新子节点");
                    break;
                default:break;
            }
        });

      //  curatorFramework.create().withMode(CreateMode.PERSISTENT).forPath("/event","event".getBytes());
       // TimeUnit.SECONDS.sleep(1);
       // System.out.println("1");
//        curatorFramework.create().withMode(CreateMode.PERSISTENT).forPath("/event/event1","1".getBytes());
//        TimeUnit.SECONDS.sleep(1);
//        System.out.println("2");
//
//        curatorFramework.setData().forPath("/event/event1","222".getBytes());
//        TimeUnit.SECONDS.sleep(1);
//        System.out.println("3");

        curatorFramework.delete().forPath("/event/event1");
        System.out.println("4");




        System.in.read();

    }

}
View Code

 说明:

1.支持事务

2.支持Flush写法

3.开始测试多次程序启动就执行删除节点,而监听的结果确实新增,后来加了cache.rebuild();代码就没问题了。跟源码,在cache.start()里面有一个构造函数也是调用了rebuild方法的。

4.功能还是比较强大的。高级功能都会用到这种方式

最后贴一个原生API的递归操作方式:

package zook;

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

import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.data.Stat;
import org.apache.zookeeper.Watcher.Event.EventType;
import org.apache.zookeeper.Watcher.Event.KeeperState;
import org.apache.zookeeper.ZooDefs;

public class ZookManager {
    ZooKeeper zookeeper = null;

    public ZookManager(String connStr) throws IOException, InterruptedException {
        CountDownLatch latch = new CountDownLatch(1);
        zookeeper = new ZooKeeper(connStr, 5000, new Watcher() {
            @Override
            public void process(WatchedEvent event) {
                if (event.getType() == EventType.None) {
                    if (event.getState() == KeeperState.SyncConnected) {
                        latch.countDown();
                    } else {
                        System.out.println("连接失败.");
                        latch.countDown();
                    }
                }
            }

        });
        latch.await();
    }

    /** 创建节点,不存在父节点将新增,如果节点已经存在将抛出异常 **/
    public String create(String path, String val) throws KeeperException, InterruptedException {
        if (!checkPath(path)) {
            return "";
        }

        String p = getParentPath(path);
        cycleCreate(p);

        String url = zookeeper.create(path, val.getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
        return url;
    }

    /** 设置节点的数据,如果节点不存在将新增该节点  **/
    public Stat setData(String path, String val) throws KeeperException, InterruptedException {
        if (!checkPath(path)) {
            return null;
        }

        cycleCreate(path);
        return zookeeper.setData(path, val.getBytes(), -1);
    }

    /** 删除节点,如果存在子节点将递归删除 
     * @throws InterruptedException 
     * @throws KeeperException **/
    public void delete(String path) throws KeeperException, InterruptedException {
        if (!checkPath(path)) {
            return;
        }

        List<String> chidren = zookeeper.getChildren(path, false);
        for (String p : chidren) {
            delete(path + "/" + p);
        }
        zookeeper.delete(path, -1);
    }

    private void cycleCreate(String path) throws KeeperException, InterruptedException {
        Stat stat = zookeeper.exists(path, null);
        if (stat == null) {
            String p = getParentPath(path);
            cycleCreate(p);// 递归
            // 创建
            zookeeper.create(path, "".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
        }
    }

    /**
     * 检查目录是否正确
     * @param path
     * @return
     */
    private boolean checkPath(String path) {
        if (!path.startsWith("/")) {
            System.err.println("路径必须以/开头:" + path);
            return false;
        }
        if (path.endsWith("/")) {
            System.err.println("路径不能以/结尾:" + path);
            return false;
        }
        if (path.contains("//")) {
            System.err.println("路径格式不对,存在连续的/:" + path);
            return false;
        }
        if (path.equals("/")) {
            System.err.println("路径格式不对,只有一个/:" + path);
            return false;
        }
        return true;
    }

    /**
     * 获得父级目录
     * @param path /root/abc
     * @return
     */
    private String getParentPath(String path) {
        int index = path.lastIndexOf("/");
        return path.substring(0, index);
    }

    public static void main(String[] args) throws IOException, InterruptedException, KeeperException {
        ZookManager zoo = new ZookManager("192.168.23.24:2181");
        zoo.setData("/top/enjoy/abc", "abc");
        zoo.setData("/top/enjoy/bbb", "bbb");
        zoo.setData("/top/enjoy/ccc", "ccc");
        System.out.println("成功新增");        
        zoo.delete("/top/enjoy");
        System.out.println("成功删除");
    }
}
原文地址:https://www.cnblogs.com/duanjt/p/9896641.html