Zookeeper实战-使用Curator开发

Curator是Netflix公司开源的一套Zookeeper客户端框架,是Apache的顶级项目,是全世界范围内使用最广泛的Zookeeper客户端之一。“Guava is to Java what Curator is to ZooKeeper”,可见Curator之重要,本文对Curator的创建,查看,更改,删除等操作进行详细的描述并配以案例,后续利用Curator来实现zookeeper的几个功能:leader选举,分布式锁,分布式队列等。

maven依赖

本文基于curator 2.12进行各个易用api的介绍和程序实战:

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

创建session

api

Curator使用两个静态方法进行客户端创建,然后通过start方法启动客户端,另外还可以通过fluent风格进行client的创建,实质是newClient这两个方法也是通过fluent方式创建的client。

public static CuratorFramework newClient(String connectString, RetryPolicy retryPolicy)

public static CuratorFramework newClient(String connectString, int sessionTimeoutMs, int connectionTimeoutMs, RetryPolicy retryPolicy) {
    return builder().
            connectString(connectString).
            sessionTimeoutMs(sessionTimeoutMs).
            connectionTimeoutMs(connectionTimeoutMs).
            retryPolicy(retryPolicy).
            build();
}

参数解析:

  1. connnectString,sessionTimeoutMs,connectionTimeoutMs: 同原生api一样,分别是zk的服务器地址,用,分割;会话超时时间以及连接超时时间
  2. retryPolicy:重试策略,可以自定义重试策略,也可以用系统提供的几种重试策略: ExponentialBackoffRetry, RetryNTimes,RetryOneTime,BoundedExponentialBackoffRetry.

当然可以自定义实现重试策略,只需要实现RetryPolicy接口:

// retryCount:重试的次数
// elapsedTimeMs:从第一次尝试已经花费的时间
// sleeper: 用于sleep的时间
public boolean  allowRetry(int retryCount, long elapsedTimeMs, RetrySleeper sleeper);

实战

  1. 程序实例

    import org.apache.curator.RetryPolicy;
    import org.apache.curator.framework.CuratorFramework;
    import org.apache.curator.framework.CuratorFrameworkFactory;
    import org.apache.curator.retry.ExponentialBackoffRetry;
    
    public class CuratorZKClient {
        public static void main(String[] args) throws InterruptedException {
            RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 2);
            // fluent风格创建client
            CuratorFramework fluentClient = CuratorFrameworkFactory
                    .builder()
                    .connectString("localhost:2181")
                    .sessionTimeoutMs(5000)
                    .retryPolicy(retryPolicy)
                    .build();
            fluentClient.start();
            System.out.println(fluentClient.getState());
    
            // 非fluent风格创建client
            CuratorFramework client = CuratorFrameworkFactory.
                    newClient("localhost:2181", 5000, 5000, retryPolicy);
            client.start();
            System.out.println(fluentClient.getState());
    
            Thread.sleep(Integer.MAX_VALUE);
        }
    }
    
  2. 运行结果

    STARTED
    STARTED
    

 此外,为了实现不同zk业务之间的隔离,会给每个业务分配一个独立的命名空间,即指定一个根路径,可以在builder时候进行namespace的指定即可。

创建&删除节点

api

节点的创建时候需要指定的属性有节点类型,节点权限,节点数据,节点path,同样curator使用fluent的方式进行创建, 如果节点已经存在,是不会报节点存在的错的,常见函数如下:

public CreateBuilder create();  // client调用create方法得到Builder
// 十分好用的递归创建,父节点节点属性默认是PERSISTENT
public ACLCreateModePathAndBytesable<String> creatingParentsIfNeeded();
public ACLBackgroundPathAndBytesable<String> withMode(CreateMode mode)
public String forPath(String path) throws Exception
public String forPath(final String givenPath, byte[] data) throws Exception    

同样节点的删除也是遵循fluent风格:

public DeleteBuilder delete();
public ChildrenDeletable guaranteed();
// 递归删除所有子节点
public BackgroundVersionable deletingChildrenIfNeeded();
// 指定要删除的version
public BackgroundPathable<Void> withVersion(int version);
// 要删除的path
public Void forPath(String path) throws Exception;

实战

  1. 程序实例

    import org.apache.curator.RetryPolicy;
    import org.apache.curator.framework.CuratorFramework;
    import org.apache.curator.framework.CuratorFrameworkFactory;
    import org.apache.curator.retry.ExponentialBackoffRetry;
    import org.apache.zookeeper.CreateMode;
    
    public class CuratorZKCreateAndDeleteNode {
        public static void main(String[] args) throws Exception {
            RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 2);
            // fluent风格创建client
            CuratorFramework fluentClient = CuratorFrameworkFactory
                    .builder()
                    .connectString("localhost:2181")
                    .sessionTimeoutMs(5000)
                    .retryPolicy(retryPolicy)
                    .build();
            fluentClient.start();
    
            // fluent风格创建节点,返回只是节点path
            String result = fluentClient.create().
                    withMode(CreateMode.EPHEMERAL).
                    forPath("/zk-test", "init".getBytes());
            System.out.println(result);
    
            // 删除
            fluentClient.delete().
                    guaranteed().
                    withVersion(0).
                    forPath("/zk-test");
    
            Thread.sleep(Integer.MAX_VALUE);
        }
    }
    
  2. 运行结果

    /zk-test
    

获取&更新节点数据

api

Curator同样使用fluent风格进行节点数据获取:

public GetDataBuilder getData();
// 读取节点内容并且获取该节点的stat
public WatchPathable<byte[]> storingStatIn(Stat stat);
public byte[] forPath(String path) throws Exception;

设置节点数据方式:

public SetDataBuilder setData();
// 在某个版本上设置
public BackgroundPathAndBytesable<Stat> withVersion(int version)
public Stat forPath(String path, byte[] data) throws Exception
public Stat forPath(String path) throws Exception

实战

  1. 程序实例

    import org.apache.curator.RetryPolicy;
    import org.apache.curator.framework.CuratorFramework;
    import org.apache.curator.framework.CuratorFrameworkFactory;
    import org.apache.curator.retry.ExponentialBackoffRetry;
    import org.apache.zookeeper.CreateMode;
    import org.apache.zookeeper.data.Stat;
    
    public class CuratorZKGetAndUpdateDate {
        public static void main(String[] args) throws Exception {
            RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 2);
            // fluent风格创建client
            CuratorFramework fluentClient = CuratorFrameworkFactory
                    .builder()
                    .connectString("localhost:2181")
                    .sessionTimeoutMs(5000)
                    .retryPolicy(retryPolicy)
                    .build();
            fluentClient.start();
    
            // fluent风格创建节点,返回只是节点path
            String result = fluentClient.create().
                    withMode(CreateMode.EPHEMERAL).
                    forPath("/zk-test", "init".getBytes());
            System.out.println(result);
    
            // get data
            Stat stat = new Stat();
            String data = new String(fluentClient.
                    getData().
                    storingStatIn(stat).
                    forPath("/zk-test"));
            System.out.println("data:" + data + ";version=" + stat.getVersion());
    
            // set data
            fluentClient.setData().
                    withVersion(stat.getVersion()).
                    forPath("/zk-test", "update".getBytes());
    
            System.out.println(new String(fluentClient.getData().forPath("/zk-test")));
    
            fluentClient.setData().
                    withVersion(stat.getVersion()).
                    forPath("/zk-test", "update-again".getBytes());
    
            Thread.sleep(Integer.MAX_VALUE);
        }
    }
    
  2. 运行结果

    程序先通过获取data接口获取version信息,根据version信息更新结果,从新获取,这时候version已经改变,如果再次使用上一次的version进行更新数据就会报错:BadVersion

    zk-test
    data:init;version=0
    update
    Exception in thread "main" org.apache.zookeeper.KeeperException$BadVersionException: KeeperErrorCode = BadVersion for /zk-test
    

总结

本文从实战角度介绍了Curator操作zk的各种方法,可以看出Curator在原生api基础上提供了fluent风格的各种接口,使用更加方便,接下来我会使用Curator来实现几种zk常见的应用场景: Master选举,分布式锁,分布式队列等,尽情期待吧~

参考

  1. 从Paxo到Zookeeper: 分布式一致性原理与实战
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值