zookeeper原生及第三方JavaAPI使用

版权声明:本文为博主原创文章,未经博主允许不得转载哦。 https://blog.csdn.net/a464700300/article/details/80349448

5.1.3 运行服务

常见异常

  1. 磁盘没有剩余空间
    No space left on device
    通常需要清理磁盘,再加上对ZK机器的磁盘使用量监控和ZK日志的自动清理。
  2. 集群中其他机器leader选举端口未开
    Cannot open channel to 2 at election address /192.168.137.3:3888
    只要快速启动集群中的其他机器即可。

5.2 客户端脚本

$ sh zkCli.sh 是默认连接本地的ZK服务器地址
如果想指定ZK服务器,可以通过如下方式实现:
$ sh zkCli.sh -server ip:port

5.2.1 创建

create [-s] [-e] path data acl
  其中,-s和-e分别指定节点特性:顺序或临时节点,默认不填这两个,创建的是持久节点
  参数acl,它是用来进行权限控制的,缺省情况,不做任何权限控制

5.3 Java客户端api使用

构造方法
ZooKeeper(String connectString,int sessionTimeout,Watcher watcher,long sessionId,byte[] sessionPasswd,boolean canBeReadOnly);

参数名 说明
connectString ZK服务器列表,也可以指定操作的根目录,192.168.137.1:2181,192.168.137.2:2181,192.168.137.3:2181/root,这样就会在/root的目录上进行操作
sessionTimeout 会话的超时时间,以毫秒为单位的整型值,一旦该时间内没有进行有效心跳检测
watcher 可以为null。也可以传入接口是Watcher的实现类对象来作为默认的Watcher事件通知处理器
canBeReadOnly 是否支持只读模式,开启只读,在某些场景下,即使一个机器或集群中过半机器失去连接,我们依然希望ZK服务器能提供读服务
sessionId和sessionPasswd 代表会话ID和会话密钥。这个两个参数能确定唯一的会话,客户端使用这两个参数可实现会话复用

实现Watcher时,会重写process()
该方法负责处理ZK服务端的Watcher通知,在收到服务端发来的SyncConnected事件后,解除主程序在countdownlatch的阻塞。

5.3.2 创建节点

同步创建
create(final String path,byte data[],List acl,CreateMode createMode);
异步创建
create(final String path,byte data[],List acl,CreateMode createMode,StringCallback cb,Object ctx);

参数名 说明
path 创建节点路径
data[] 创建节点的相应内容,字节数组形式
acl 节点的ACL策略
createMode 节点类型,是一个枚举类型,1.持久(PERSISTENT) 2.顺序持久(PERSISTENT_SEQUENTIAL) 3.临时(EPHEMERAL) 4.顺序临时(EPHEMERAL_SEQUENTIAL)
cb 注册一个异步回调函数。需要实现该接口,对以下方法重写void ==processResult()==,服务端节点创建完毕后,ZK客户端就自动调用这个方法,这样就可以处理相关业务逻辑
ctx 用于传递一个对象,可在回调方法执行的时候使用,通常放一个上下文信息

acl参数Ids.OPEN_ACL_UNSAFE表明这个节点的任何操作都不受权限控制

异步方式创建节点只需要实现AsyncCallback.StringCallback()接口即可。
AsyncCallback包含了StatCallback、DataCallback、ACLCallback、ChildrenCallback、Children2Callback、StringCallback和VoidCallback七种不同回调接口。
和同步接口方法最大区别在于,节点创建是异步的。同步接口调用中我们需要关注抛出异常的可能,而异步接口本身不抛出异常,异常都会在回调函数中Result Code(响应码)来实现
processResult方法参数说明

参数名 说明
rc Result Code客户端可以从该码调出结果
path 接口调入时传入API的数据节点的节点路径参数值
ctx 接口调用时传入API的ctx参数值
name 实际在服务端创建的节点名
//测试ZK的getData()方法
public class GetData_API_Sync implements Watcher {
    private static CountDownLatch countDownLatch = new CountDownLatch(1);
    private static ZooKeeper zk = null;
    private static Stat stat = new Stat();
    private static final String CONNECTION_ADDR = "192.168.137.3:2181,192.168.137.4:2181,192.168.137.5:2181";

    public static void main(String[] args) throws Exception {
        String path = "/zk-book";
        zk = new ZooKeeper(CONNECTION_ADDR,5000,new GetData_API_Sync());
        countDownLatch.await();
        zk.create(path,"123".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL);

        //watch设为TRUE就表示监听该节点及子节点,一旦发生变化,就在事件event里进行通知
        //传入stat变量,客户端可以从服务端响应中获取到数据节点的最新状态信息
        System.out.println(new String(zk.getData(path,true,stat)));
        System.out.println(stat.getCzxid()+","+stat.getMzxid()+","+stat.getVersion());

        zk.setData(path,"123".getBytes(),-1);
        Thread.sleep(1000);
    }

    public void process(WatchedEvent event) {
        if(Event.KeeperState.SyncConnected == event.getState()){
            if(Event.EventType.None == event.getType() && null == event.getPath()){
                countDownLatch.countDown();
                //客户端接收到服务端发过来的"数据变更"通知,再重新获取一下节点数据信息
            } else if(event.getType() == Event.EventType.NodeDataChanged){
                try{
                    System.out.println(new String(zk.getData(event.getPath(),true,stat)));
                    System.out.println(stat.getCzxid()+","+stat.getMzxid()+","+stat.getVersion());
                }catch (Exception e){
                }
            }
        }
    }
}

5.3.5 更新数据

同步
Stat setData(final String path,byte[] data,int version);
异步
void setData(final String path,byte[] data,int version,StatCallback cb,Object ctx);

参数名 说明
path 创建节点路径
data[] 创建节点的相应内容,字节数组形式
version 指定节点数据版本,本次更新操作是针对该数据版本进行的
cb 注册一个异步回调函数
ctx 用于传递一个对象,可在回调方法执行的时候使用,通常放一个上下文信息
//异步更新节点数据
public class SetData_API_Async implements Watcher {
    private static CountDownLatch connectedSemaphore = new CountDownLatch(1);
    private static ZooKeeper zk;
    private static final String CONNECTION_ADDR = "192.168.137.3:2181,192.168.137.4:2181,192.168.137.5:2181";

    public static void main(String[] args) throws Exception {
        String path = "/zk-book";
        zk = new ZooKeeper(CONNECTION_ADDR,5000,new SetData_API_Async());
        connectedSemaphore.await();

        zk.create(path,"123".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL);
        //-1代表若果客户端对于ZK数据节点的更新操作没有原子性要求,那就可以用-1
        zk.setData(path,"456".getBytes(),-1,new IStatCallback(),null);
        Thread.sleep(Integer.MAX_VALUE);
    }

    public void process(WatchedEvent event) {
        if(Event.KeeperState.SyncConnected == event.getState()){
            if(Event.EventType.None == event.getType() && null == event.getPath()){
                connectedSemaphore.countDown();
            }
        }
    }
}
class IStatCallback implements AsyncCallback.StatCallback{
    public void processResult(int rc, String path, Object ctx, Stat stat) {
        if(rc == 0){
            System.out.println("SUCCESS");
        }
    }
}

5.3.6 检测节点存在

接口参数有path watcher boolean(watch) cb ctx 其中watcher可以监听三类事件,节点被删除,被创建或是更新,都会通知客户端。

//测试判断节点存在的同步方法
public class Exist_API_Sync implements Watcher {
    private static CountDownLatch countDownLatch = new CountDownLatch(1);
    private static ZooKeeper zk = null;
    private static Stat stat = new Stat();
    private static final String CONNECTION_ADDR = "192.168.137.3:2181,192.168.137.4:2181,192.168.137.5:2181";

    public static void main(String[] args) throws Exception {
        String path = "/zk-book";
        zk = new ZooKeeper(CONNECTION_ADDR, 5000, new Exist_API_Sync());
        countDownLatch.await();

        zk.exists(path, true);
        zk.create(path, "".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);

        zk.setData(path, "123".getBytes(), -1);
        zk.create(path + "/c1", "".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
        zk.delete(path + "/c1", -1);
        zk.delete(path, -1);
        Thread.sleep(Integer.MAX_VALUE);
    }

    public void process(WatchedEvent event) {
        try{
            if(Event.KeeperState.SyncConnected == event.getState()){
                if(Event.EventType.None == event.getType() && null == event.getPath()){
                    countDownLatch.countDown();
                }else if(Event.EventType.NodeCreated == event.getType()){
                    System.out.println("Node("+event.getPath()+")Created.");
                    zk.exists(event.getPath(),true);
                }else if(Event.EventType.NodeDeleted == event.getType()){
                    System.out.println("Node("+event.getPath()+")Deleted..");
                    zk.exists(event.getPath(),true);
                }else if(Event.EventType.NodeDataChanged == event.getType()){
                    System.out.println("Node("+event.getPath()+")DataChanged..");
                    zk.exists(event.getPath(),true);
                }
            }
        }catch (Exception e){}
    }
}

5.4 开源客户端

5.4.1 ZkClient

ZKClient在内部实现Session超时重连,Watcher反复注册等功能。
创建节点
create cerateEphemeral(创建临时节点) createPersistent(创建持久节点)三个创建方法

  1. String create(final String path,Object data,final CreateMode mode)
  2. String create(final String path,Object data,final List acl,final CreateMode mode)
  3. void create(final String path,Object data,final CreateMode mode,final AsyncCallback.StringCallback callback,final Object context)
  4. void createEphemeral(final String path)
  5. void createEphemeral(final String path,final Object context)
  6. void createPersistent(String path)
  7. void createPersistent(String path,boolean createParents)
  8. void createPersistent(String path,Object data)
  9. void createPersistent(String path,List acl,Object data)
  10. String createPersistentSequential(String path,Object data)
  11. String createPersistentSequential(final String path,final Object data)
    ZKClient create API参数说明
参数名 说明
path 指定数据节点路径
data 节点初始数据内容,可传null
mode 节点类型,枚举类,4种可选节点类型
acl 节点的ACL策略
callback 注册一个异步回调函数
context 传递一个对象,执行回调函数时候用
createParents 是否创建父节点

删除节点
boolean deleteRecursive(String path)
可以逐层遍历删除节点

读取数据
List getChildren(String path)
获取指定节点的子节点列表

代替API的Watcher功能:
List subscribeChildChanges(String path,IZkChildListener listener)
该API是对子节点列表变更的监听,一旦子节点变更,ZK服务端就会对客户端发出事件通知,由这个listener处理

5.4.2 Curator

创建会话
使用CuratorFrameworkFactory该工厂类的静态方法来创建一个客户端:

static CuratorFramework newClient(String connectString,RetryPolicy retryPolicy);
static CuratorFramework newClient(String connectString,int sessionTimeoutMs,int connectionTimeoutMs,RetryPolicy retryPolicy);

通过CuratorFramework中的start()方法来启动会话

参数名 说明
connectString ZK服务器列表
retryPolicy 重试策略默认有四种实现ExponentialBackoffRetry/RetryNTimes/RetryOneTime/RetryUntilElapsed
sessionTimeoutMs 会话超时时间
connectionTimeoutMs 连接创建超时时间

重试策略上,Curator通过一个接口RetryPolicy让用户实现自定义重试策略,接口只定义一个方法:

boolean allowRetry(int retryCount,long elapsedTimeMs,RetrySleeper sleeper)

RetryPolicy接口参数说明

参数名 说明
retryCount 已重试的次数,如果是第一次重试,那参数为0
elapsedTimeMs 从第一次重试开始已花费的时间
sleeper 用于sleep指定时间,Curator不要使用Thread.sleep来进行sleep操作

创建会话*

public class CreateSessionSimple{
    public static void main(String[] args){
        RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000,3);
        CuratorFramework client = CuratorFrameworkFactory.newClient("192.168.0.1:2181",5000,3000,retryPolicy);
        client.start();
        Thread.sleep(Integer.MAX_VALUE);
    }
}

使用Fluent风格的API接口来创建会话

public class CreateSampleFluent{
    public static void main(String[] args){
        RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000,3);
        CuratorFramework client = 
        CuratorFrameworkFactory.builder().
            .connectString("192.168.137.1:2181")
            .sessionTimeoutMs(5000)
            .retryPolicy(retryPolicy)
            .build();
        client.start();
        Thread.sleep(Integer.MAX_VALUE);
    }
}

创建隔离命名空间的会话
可能会因为业务分配独立的命名空间,即指定ZK根路径。例如定义独立命名空间/base,那么

CuratorFrameworkFactory.builder().
            .connectString("192.168.137.1:2181")
            .sessionTimeoutMs(5000)
            .retryPolicy(retryPolicy)
            .namespace("base")
            .build();

创建节点

  1. 创建一个节点,内容为空
client.create().forPath(path);  

如果没有设置节点属性,那么默认创建持久节点,内容默认是空

  1. 创建一个节点,附带内容
client.create().forPath(path,"init".getBytes())
  1. 创建一个临时节点
client.create().withMode(CreateMode.EPHEMERAL).forPath(path);
  1. 创建一个临时节点,自动递归创建父节点
client.create().creatingParentsIfNeeded().withMode(CreateMode.EPHEMERAL).forPath(path);

删除节点

  1. 删除节点
client.delete().forPath(path);
  1. 删除节点,递归删除子节点
client.delete().deletingChildrenIfNeeded().forPath(path);
  1. 删除节点,强制指定版本删除
client.delete().withVersion(version).forPath(path);
  1. 删除节点,强制保证删除
    只要客户端会话有效,那么会持续进行删除操作,指导节点删除成功
client.delete().guaranteed().forPath(path);

读取数据

  1. 读取一个节点的数据内容
    该接口调用后的返回值是byte[]
client.getData().forPath(path);
  1. 读取一个节点的数据,同时获取该节点的stat
    Curator通过传入一个旧的stat变量来存储服务端返回的新的节点状态
client.getData().storingStatIn(stat).forPath(path);

更新数据

  1. 更新节点内容
    返回一个stat对象
client.setData().forPath(path);
  1. 更新节点内容,强制指定版本更新
    version通过从一个旧的stat对象获取到
client.setData().withVersion(version).forPath(path);

更新代码

public class SetData{
    static String path = "/zk-book";
    static CuratorFramework client = CuratorFramework.builder()
        .connectString("192.168.137.7:2181")
        .sessionTimeoutMs(5000)
        .retryPolicy(new ExponentialBackoffRetry(1000,3))
        .build();

    public static void main(String[] args) throws Exception{
        client.start();
        client.create().creatingParentsIfNeeded().withMode(CreateMode.EPHEMERAL).forPath(path,"init".getBytes());
        Stat stat = new Stat();
        client.getData().storingStatIn(stat).forPath(path);
        String newVersion = client.setData().withVersion(stat.getVersion()).forPath(path).getVersion();
    }
}

异步接口
引入BackgroundCallback接口,用来处理异步接口调用后服务端返回的结果信息。其中只有一个方法:
public void processResult(CuratorFramework client,CuratorEvent event);

参数名 说明
client 当前客户端实例
event 服务端事件

CuratorEvent定义了Zookeeper服务端发送到客户端的一系列事件参数,比较重要的有==事件类型和响应码==
事件类型
getType()代表本次事件的类型,主要有CREATE,DELETE,EXISTS,GET_DATA,SET_DATA,CHILDREN,SYNC,GET_ACL,WATCHED,CLOSING
响应码
用于标识事件的结果状态,响应码都被定义在org.apache.zookeeper.KeeperException.Code中,常见的响应码如0(OK),-4(ConnectionLoss),-110(NodeExists),-112(SessionExpired会话过期)
Curator异步化API
Backgroundable
public T inBackground(…Executor exe)
inBackground允许把一些复杂的事件处理放到一个专门的线程池中,例如:Executors.newFixedThreadPool(2)

public class CreateNodeBackGround{
    static String path = "/zk-book";

    static CuratorFramework client = 
        CuratorFrameworkFactory.builder()
            .connectString("192.168.137.6:2181")
            .sessionTimeoutMs(5000)
            .retryPolicy(new ExponentialBackoffRetry(1000,3))
            .build();
    static CountDownLatch semaphore = new CountDownLatch(2);
    static ExecutorService tp = Executors.newFixedThreadPool(2);

    public static void main(String[] args){
        client.start();
        //此处传入自定义的Executor
        client.create().creatingParentsIfNeeded().withMode(CreateMode.EPHEMERAL).inBackground(new BackgroundCallback(){
            @Override
            public void processResult(CuratorFramework client,CuratorEvent event) throws Exception{

               //用来获取状态码和事件类型
                System.out.println(event.getResultCode()+"---"+event.getType()));
                System.out.println(Thread.currentThread().getName());
                semaphore.countDown();
            }
        }).forPath(path,"init".getBytes());

        //此处没有传入自定义的Executor
        client.create().creatingParentsIfNeeded().withMode(CreateMode.EPHEMERAL).inBackground(new BackgroundCallback(){
            @Override
            public void processResult(CuratorFramework client,CuratorEvent event) throws Exception{

               //用来获取状态码和事件类型
                System.out.println(event.getResultCode()+"---"+event.getType()));
                System.out.println(Thread.currentThread().getName());
                semaphore.countDown();
            }
        }).forPath(path,"init".getBytes());
        semaphore.await();
        tp.shutdown();
    }
}

代码第一次运行返回0,第二次返回-110说明节点已经存在,不能重复创建。
Master选举
对于一个任务,有时候仅需从集群中选出一台机器处理即可,借助ZK可以很方便的实现。
大体思路如下:
选择一个根节点如/master,多台机器同时向该节点创建一个子节点/master/lock,最终只有一台机器创建成功,这台机器就是Master

public class RecipesMaster{
    static String master_path = "/master_path";
    static CuratorFramework client = 
        CuratorFrameworkFactory.builder()
            .connectString("192.168.137.5:2181")
            .retryPolicy(new ExponentialBackoffRetry(1000,3))
            .build();
    public static void main(String[] args){
        client.start();
        LeaderSelector selector = new LeaderSelector(client,master_path,new LeaderSelectorListenerAdapter(){
            public void takeLeadership(CuratorFramework client) throws Exception{
                System.out.println("成为Master角色");
                Thread.sleep(3000);
                System.out.println("完成Master操作,释放Master权利");
            }
        });
        selector.autoRequeue();
        selector.start();
        Thread.sleep(Integer.MAX_VALUE);
    }
}

分布式锁
分布式环境中为了保证数据的一致性,经常在程序某个运行点(减库存或流水号生成)需要同进控制。
Curator实现分布式锁功能
“`java
public class RecipesLock{
static String lock_path = “/path”;

static CuratorFramework client = 
    CuratorFrameworkFactory.builder()
        .connectString("192.168.137.5:2181")
        .retryPolicy(new ExponentialBackoffRetry(1000,3)).build();

public static void main(String[] args){
    client.start();
    final InterProcessMutex lock = new InterProcessMutex(client,lock_path);
    final CountDownLatch down = new CountDownLatch(1);
    for(int i=0;i<30;i++){
        new Thread(new Runnable(){
            public void run(){
                try{
                    down.await();
                    lock.acquire();
                }catch(Exception e){}
                SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss|SSS");
                String orderNo = sdf.format(new Date());
                System.out.println("生成订单号:"+orderNo);
                try{
                    lock.release();
                }catch(Exception e){}
        }).start();
    }
    down.countDown();
}

}

acquire()和release()这两个方法分别用来实现分布式锁的获取与释放过程
**分布式计数器**
典型场景是统计系统的在线人数,实现原理就是:
指定一个ZK数据节点作为计数器,多个应用实例在分布式锁控制下,通过更新该数据节点的内容来实现技术功能。
java
public class RecipesDistAtomInt{
static String lock_path = “/path”;

static CuratorFramework client = 
    CuratorFrameworkFactory.builder()
        .connectString("192.168.137.5:2181")
        .retryPolicy(new ExponentialBackoffRetry(1000,3)).build();

public static void main(String[] args){
    client.start();
    DistributedAtomicInteger atomicInteger = new DistributedAtomicInteger(client,lock_path,new RetryNTimes(3,1000));
    AtomicValue<Integer> rc = atomicInteger.add(8);
    System.out.println("Result:"+rc.succeeded());
}

}
“`
分布式Barrier
Barrier是一种用来控制多线程之间同步的经典方式。Java中自带了CyclicBarrier实现,下面通过模拟赛跑比赛来演示CyclicBarrier
Java自带的在同一个JVM下模拟

public class JVMCyclicBarrier{
    public static CyclicBarrier barrier = new CyclicBarrier(3);
    public static void main(String[] args) throws IOException,InterruptedException{
        ExecutorService executor = Executors.newFixedThreadPool(3);
        executor.submit(new Thread(new Runner("1号")));
        executor.submit(new Thread(new Runner("2号")));
        executor.submit(new Thread(new Runner("3号")));
    }
}

class Runner implements Runnable{
    private String name;
    public Runner(String name){
        this.name=name;
    }
    public void run(){
        System.out.println(name+"准备好了");
        try{
            JVMCyclicBarrier.barrier.await();
        }catch(Exception e){}
        System.out.println(name + "起跑!")
    }
}

Curator实现Barrier

public class RecipesBarrier{
    static String path = "/path";

    static DistributedBarrier barrier;
    public static void main(String[] args) throws Exception{
        for(int i=0;i<5;i++){
            new Thread(new Runnable(){
                public void run(){
                    try{
                        CuratorFramework client = CuratorFrameworkFactory.builder().connectString("192.168.137.5:2181").retryPolicy(new ExponentialBackoffRetry(1000,3)).build();
                        client.start();
                        barrier = new DistributedBarrier(client,path);
                        System.out.println(Thread.currentThread().getName() + "号Barrier设置");
                        barrier.setBarrier();
                        barrier.waitOnBarrier();
                        System.err.println("启动...");
                    }catch(Exception e){}
                }
            }).start();
        }
        Thread.sleep(2000);
        barrier.removeBarrier();
    }
}

上面这个程序,我们模拟5个线程,通过DistributedBarrier.setBarrier()方法完成Barrier的设置,并通过DistributedBarrier.waitOnBarrier()方法来等待Barrier的释放。然后在主线程中通过DistributedBarrier.removeBarrier()来释放Barrier,触发所有等待该Barrier的5个线程同时进行各自的业务逻辑。
Curator提供的自发触发Barrier

public class RecipesBarrier2{
    static String path = "/path";

    public static void main(String[] args){
        for(){
            new Thread(new Runnable(){
                public void run(){
                    try{
                        CuratorFramework client = CuratorFrameworkFactory.builder().connectString("192.168.137.5:2181").retryPolicy(new ExponentialBackoffRetry(1000,3)).build();
                        client.start();
                        DistributedDoubleBarrier barrier = new DistributedDoubleBarrier(client,path,5);
                        Thread.sleep(Math.round(Math.random()*3000));
                        System.out.println(Thread.currentThread().getName()+"号进入barrier");
                        barrier.enter();
                        System.out.println("启动...");
                        Thread.sleep(Math.round(Math.random()*3000));
                        barrier.leave();
                        System.out.println("退出...");
                    }catch(Exception e){}
                }
            }).start();
        }
    }
}

指定了Barrier的成员数阈值,如果上面写5,每个barrier参与者会调用enter()之后进行等待,一旦准备进入的barrier成员达到5个,所有成员同是触发退出。
ZKPaths
提供简单的API构建ZNode路径、递归创建、删除节点。

public class ZKPathSample{
    static String path = "/sample_path";
    static CuratorFramework client = 
        CuratorFrameworkFactory.builder()
            .connectString("192.168.137.5:2181")
            .retryPolicy(new ExponentialBackoffRetry(1000,3)).build();
    public static void main(String[] args){
        client.start();
        Zookeeper zookeeper = client.getZookeeperClient().getZookeeper();
        System.out.println(ZKPaths.fixForNamespace(path,"sub"));
        System.out.println(ZKPaths.makePath(path,"sub"));

        System.out.println(ZKPaths.getNodeFromPath("/sample_path/sub1"));

        PathAndNode pn = ZKPaths.getPathAndNode("/sample_path/sub1");
        System.out.println(pn.getPath());
        System.out.println(pn.getNode());

        String dir1 = path + "/child1";
        String dir2 = path + "/child2";
        ZKPaths.mkdirs(zookeeper,dir1);
        ZKPaths.mkdirs(zookeeper,dir2);
        System.out.println(ZKPaths.getSortedChildren(zookeeper,path));

        ZKPaths.deleteChildren(client.getZookeeperClient().getZookeeper(),path,true);
    }
}

EnsurePath
提供了一种能确保数据节点存在的机制。
在并发环境下,一台机器师徒创建节点,而另一台机器也同时创建这个节点,那么可能会抛出一些异常。
EnsurePath采用静默节点创建,创建指定节点,如果节点已经存在,那么就不进行任何操作,也不对外抛出异常,否则正常创建数据节点。

public class EnsurePathDemo{
    static String path = "/zk-book/c1";
    static CuratorFramework client = 
        CuratorFrameworkFactory.builder()
            .connectString("192.168.137.5:2181")
            .retryPolicy(new ExponentialBackoffRetry(1000,3)).build();
    public static void main(String[] args){
        client.start();
        client.usingNamespace("zk-book");

        EnsurePath ensurePath = new EnsurePath(path);
        ensurePath.ensure(client.getZookeeperClient());
        ensurePath.ensure(client.getZookeeperClient());

        EnsurePath ensurePath2 = client.newNamespaceAwareEnsurePath("/c1");
        ensurePath2 = ensure(client.getZookeeperClient());
    }
}

TestingServer
提供一个启动简易ZK服务器的方法

<dependency>
    <groupId>org.apache.curator</groupId>
    <artifactId>curator-test</artifactId>
    <version>2.4.2</version>
</dependency>

基本使用方法

public class TestingServer{
    static String path = "/zookeeper";

    public static void main(String[] args) throws Exception{
        TestingServer server = new TestingServer(2181,new File("/home/admin/zk-book-data"));

        Curatorwork client = 
            CuratorFrameworkFactory.builder()
                .connectString(server.getConnectString())
                .sessionTimeoutMs(5000)
                .retryPolicy(new ExponentialBackoffRetry(1000,3))
                .build();
        client.start();
        System.out.println(client.getChildren().forPath(path));
        server.close();
    }
}

TestingServer允许自定义端口和dataDir路径,如果没有路径就默认创建一个目录。
TestingCluster
可模拟ZK集群环境的工具,下面模拟一个3台机器组成的ZK集群

public class TestCluster{
    public static void main(String[] args){
        TestingCluster cluster = new TestingCluster(3);
        cluster.start();
        Thread.sleep(3000);

        TestingZooKeeperServer leader = null;
        for(TestingZooKeeperServer zs : cluster.getServers()){
            System.out.println(zs.getInstanceSpec().getServerId()+"-");
            System.out.println(zs.getQuorumPeer().getServerState()+"-");

            System.out.println(zs.getInstanceSpec().getDataDirectory().getAbsolutePath());

            if(zs.getQuorumPeer().getServerState().equals("leading")){
                leader = zs;
            }
        }
        leader.kill();

        System.out.println("--After leader kill:");
        for(TestingZooKeeperServer zs : cluster.getServers()){
            System.out.println(zs.getInstanceSpec().getServerId()+"-");
            System.out.println(zs.getQuorumPeer().getServerState()+"-");

            System.out.println(zs.getInstanceSpec().getDataDirectory().getAbsolutePath());
        }
        cluster.stop();
    }
}

程序中展示了3台机器组成的ZK服务器集群,同时在运行期间,将Leader服务器kill掉,另外两台机器重新进行Leader选举。

阅读更多
想对作者说点什么?

博主推荐

换一批

没有更多推荐了,返回首页