zookeeper的客户端介绍:zkclient和curator的基本使用

zkclient

ZkClient是一个开源客户端,在ZooKeeper原生API接口的基础上进行了包装,更便于开发人员使用。ZkClient客户端在一些著名的互联网开源项目中得到了应用,例如,阿里的分布式Dubbo框架对它进行了无缝集成。 ZkClient解决了ZooKeeper原生API接口的很多问题。例如,ZkClient提供了更加简洁的API,实现了会话超时重连、反复注册Watcher等问题。虽然ZkClient对原生API进行了封装,但也有它自身的不足之处,具体如下:

·ZkClient社区不活跃,文档不够完善,几乎没有参考文档。
·异常处理简化(抛出RuntimeException)。
·重试机制比较难用。
·没有提供各种使用场景的参考实现。

curator

Curator是Netflix公司开源的一套ZooKeeper客户端框架,和ZkClient一样它解决了非常底层的细节开发工作,包括连接、重连、反复注册Watcher的问题以及NodeExistsException异常等。 Curator是Apache基金会的顶级项目之一,Curator具有更加完善的文档,另外还提供了一套易用性和可读性更强的Fluent风格的客户端API框架。 Curator还为ZooKeeper客户端框架提供了一些比较普遍的、开箱即用的、分布式开发用的解决方案,例如Recipe、共享锁服务、Master选举机制和分布式计算器等,帮助开发者避免了“重复造轮子”的无效开发工作。 另外,Curator还提供了一套非常优雅的链式调用API,与ZkClient客户端API相比,Curator的API优雅太多了

二者的基本使用

使用zkclient:

使用zklient只需要引入一个jar包:

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

创建客户端实例:直接在初始化方法中创建zookeeper实例

@Slf4j
public abstract class  StandaloneBase {

    private static final  String  CONNECT_STR="192.168.109.200:2181";

    private static final  int SESSION_TIMEOUT=30 * 1000;

    private static ZooKeeper  zooKeeper =null;

    private static  CountDownLatch countDownLatch = new CountDownLatch(1);

    private Watcher watcher =new Watcher() {
        @Override
        public void process(WatchedEvent event) {
                    if (event.getState() == Event.KeeperState.SyncConnected
                            && event.getType()== Event.EventType.None){
                        countDownLatch.countDown();
                        log.info("连接建立");
                    }
        }
    };


    @Before
    public void init(){
        try {
            log.info(" start to connect to zookeeper server: {}",getConnectStr());
             zooKeeper=new ZooKeeper(getConnectStr(), getSessionTimeout(), watcher);
            log.info(" 连接中...");
            countDownLatch.await();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }





    public static ZooKeeper getZooKeeper() {
        return zooKeeper;
    }


    @After
    public void   test(){
        try {
            TimeUnit.SECONDS.sleep(Integer.MAX_VALUE);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }


    protected     String getConnectStr(){
        return CONNECT_STR;
    }

    protected   int getSessionTimeout() {
        return SESSION_TIMEOUT;
    }
}

使用zkclient的基本操作:


@Slf4j
public class BaseOperations  extends StandaloneBase{

    private String first_node = "/first-node";

    @Test
    public void testCreate() throws KeeperException, InterruptedException {
        ZooKeeper zooKeeper = getZooKeeper();

        String s = zooKeeper.create(first_node, "first".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL);
        log.info("Create:{}",s);
    }

    @Test
    public void testGetData(){

        Watcher watcher = new Watcher() {
            @Override
            public void process(WatchedEvent event) {
                      if (event.getPath()!=null && event.getPath().equals(first_node)
                              && event.getType()== Event.EventType.NodeDataChanged){
                          log.info(" PATH: {}  发现变化",first_node);
                          try {
                              byte[] data = getZooKeeper().getData(first_node, this, null);
                              log.info(" data: {}",new String(data));
                          } catch (KeeperException e) {
                              e.printStackTrace();
                          } catch (InterruptedException e) {
                              e.printStackTrace();
                          }
                      }
            }
        };
        try {
            byte[] data = getZooKeeper().getData(first_node, watcher, null);  //
            log.info(" data: {}",new String(data));
        } catch (KeeperException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }

    @Test
    public void testSetData() throws KeeperException, InterruptedException {
        ZooKeeper zooKeeper = getZooKeeper();


        Stat stat = new Stat();
        byte[] data = zooKeeper.getData(first_node, false, stat);
       // int version = stat.getVersion();
        zooKeeper.setData(first_node, "third".getBytes(), 0);


    }



    @Test
    public void testDelete() throws KeeperException, InterruptedException {
         // -1 代表匹配所有版本,直接删除
         // 任意大于 -1 的代表可以指定数据版本删除
        getZooKeeper().delete("/config",-1);

    }
    @Test
    public void  asyncTest(){
          String userId="xxx";
          getZooKeeper().getData("/test", false, (rc, path, ctx, data, stat) -> {
              Thread thread = Thread.currentThread();

              log.info(" Thread Name: {},   rc:{}, path:{}, ctx:{}, data:{}, stat:{}",thread.getName(),rc, path, ctx, data, stat);
        },"test");
        log.info(" over .");

    }


}

使用Curator单结点操作

先要引入多个jar包:

<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-client</artifactId>
<version>4.0.0</version>
<exclusions>
<exclusion>
<groupId>org.apache.ZooKeeper</groupId>
<artifactId>ZooKeeper</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-framework</artifactId>
<version>4.0.0</version>
<exclusions>
<exclusion>
<groupId>org.apache.ZooKeeper</groupId>
<artifactId>ZooKeeper</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
<version>4.0.0</version>
<exclusions>
<exclusion>
<groupId>org.apache.ZooKeeper</groupId>
<artifactId>ZooKeeper</artifactId>
</exclusion>
</exclusions>
</dependency>
 

如果Curator与ZooKeeper的版本不是相互匹配的,就会有兼容性问题,很有可能导致节点操作的失败。如何确保Curator与ZooKeeper的具体版本是匹配的呢?可以去Curator的官网查看。

创建会话:
要进行客户端服务器交互,第一步就要创建会话
Curator 提供了多种方式创建会话,比如用静态工厂方式创建:

// 重试策略 
RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 3)
CuratorFramework client = CuratorFrameworkFactory.newClient(zookeeperConnectionString, retryPolicy);
client.start();

或者使用 fluent 风格创建

RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 3);
CuratorFramework client = CuratorFrameworkFactory.builder()
                .connectString("192.168.128.129:2181")
                .sessionTimeoutMs(5000)  // 会话超时时间
                .connectionTimeoutMs(5000) // 连接超时时间
                .retryPolicy(retryPolicy)
                .namespace("base") // 包含隔离名称
                .build();
client.start();

上面这段代码的编码风格采用了流式方式,
最核心的类是 CuratorFramework 类,该类的作用是定义一个 ZooKeeper 客户端对象,并在之后的上下文中使用。在定义 CuratorFramework 对象实例的时候,我们使用了 CuratorFrameworkFactory 工厂方法,并指定了 connectionString 服务器地址列表、retryPolicy 重试策略 、sessionTimeoutMs 会话超时时间、connectionTimeoutMs 会话创建超时时间。

关于重试策略:当客户端异常退出或者与服务端失去连接的时候,可以通过设置客户端重新连接 ZooKeeper 服务端。而 Curator 提供了 一次重试、多次重试等不同种类的实现方式。在 Curator 内部,可以通过判断服务器返回的 keeperException 的状态代码来判断是否进行重试处理,如果返回的是 OK 表示一切操作都没有问题,而 SYSTEMERROR 表示系统或服务端错误。

在这里插入图片描述

创建单节点基类:


@Slf4j
public abstract  class CuratorStandaloneBase {

    private static final String CONNECT_STR = "192.168.109.200:2181";
    private static final int sessionTimeoutMs = 60*1000;
    private static final int connectionTimeoutMs = 5000;
    private static CuratorFramework curatorFramework;

    @Before
    public void init() {
        RetryPolicy retryPolicy = new ExponentialBackoffRetry(5000, 30);
        curatorFramework = CuratorFrameworkFactory.builder().connectString(getConnectStr())
                .retryPolicy(retryPolicy)
                .sessionTimeoutMs(sessionTimeoutMs)
                .connectionTimeoutMs(connectionTimeoutMs)
                .canBeReadOnly(true)
                .build();
        curatorFramework.getConnectionStateListenable().addListener((client, newState) -> {
            if (newState == ConnectionState.CONNECTED) {
                log.info("连接成功!");
            }

        });
        log.info("连接中......");
        curatorFramework.start();
    }

    public void createIfNeed(String path) throws Exception {
        Stat stat = curatorFramework.checkExists().forPath(path);
        if (stat==null){
            String s = curatorFramework.create().forPath(path);
            log.info("path {} created! ",s);
        }
    }

    public static CuratorFramework getCuratorFramework() {
        return curatorFramework;
    }

    @After
    public void   test(){
        try {
            TimeUnit.SECONDS.sleep(Integer.MAX_VALUE);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }


    protected   String getConnectStr(){
        return CONNECT_STR;
    }
}

curator单节点的基本操作:


@Slf4j
public class CuratorBaseOperations extends CuratorStandaloneBase {


   // 递归创建子节点
    @Test
    public void testCreateWithParent() throws Exception {
        CuratorFramework curatorFramework = getCuratorFramework();

        String pathWithParent = "/node-parent/sub-node-1";
        String path = curatorFramework.create().creatingParentsIfNeeded().forPath(pathWithParent);
        log.info("curator create node :{}  successfully.", path);
    }



    // protection 模式,防止由于异常原因,导致僵尸节点
    @Test
    public void testCreate() throws Exception {

        CuratorFramework curatorFramework = getCuratorFramework();
        String forPath = curatorFramework
                .create()
                .withProtection()
                .withMode(CreateMode.EPHEMERAL_SEQUENTIAL).
                        forPath("/curator-node", "some-data".getBytes());
        log.info("curator create node :{}  successfully.", forPath);


    }

    @Test
    public void testGetData() throws Exception {
        CuratorFramework curatorFramework = getCuratorFramework();

        byte[] bytes = curatorFramework.getData().forPath("/curator-node");
        log.info("get data from  node :{}  successfully.", new String(bytes));
    }



    @Test
    public void testSetData() throws Exception {
        CuratorFramework curatorFramework = getCuratorFramework();

        curatorFramework.setData().forPath("/curator-node", "changed!".getBytes());
        byte[] bytes = curatorFramework.getData().forPath("/curator-node");
        log.info("get data from  node /curator-node :{}  successfully.", new String(bytes));
    }

    @Test
    public void testDelete() throws Exception {
        CuratorFramework curatorFramework = getCuratorFramework();

        String pathWithParent = "/node-parent";
        curatorFramework.delete().guaranteed().deletingChildrenIfNeeded().forPath(pathWithParent);
    }

    @Test
    public void testListChildren() throws Exception {
        CuratorFramework curatorFramework = getCuratorFramework();

        String pathWithParent = "/discovery/example";
        List<String> strings = curatorFramework.getChildren().forPath(pathWithParent);
        strings.forEach(System.out::println);
    }

    @Test
    public void testThreadPool() throws Exception {

        CuratorFramework curatorFramework = getCuratorFramework();
        ExecutorService executorService = Executors.newSingleThreadExecutor();
        String ZK_NODE="/zk-node";
        curatorFramework.getData().inBackground((client, event) -> {
            log.info(" background: {}", event);
        },executorService).forPath(ZK_NODE);

     }
}

使用curator集群操作

public  class CuratorClusterBase extends CuratorStandaloneBase {

    private final static  String CLUSTER_CONNECT_STR="192.168.109.200:2181,192.168.109.200:2182,192.168.109.200:2183,192.168.109.200:2184";

    public   String getConnectStr() {
        return CLUSTER_CONNECT_STR;
    }
}


@Slf4j
public class CuratorClusterBaseOperations extends CuratorClusterBase {
    @Test
    public void testCluster() throws Exception {
        CuratorFramework curatorFramework = getCuratorFramework();
        String pathWithParent = "/test";
        byte[] bytes = curatorFramework.getData().forPath(pathWithParent);
        System.out.println(new String(bytes));
        while (true) {
            try {
                byte[] bytes2 = curatorFramework.getData().forPath(pathWithParent);
                System.out.println(new String(bytes2));

                TimeUnit.SECONDS.sleep(5);
            } catch (Exception e) {
                e.printStackTrace();
                testCluster();
            }
        }
    }
    public static void main(String[] args) throws Exception {
        RetryPolicy retryPolicy=new ExponentialBackoffRetry( 5*1000, 10 );
        String connectStr = "192.168.109.200:2181,192.168.109.200:2182,192.168.109.200:2183,192.168.109.200:2184";
        CuratorFramework curatorFramework = CuratorFrameworkFactory.newClient(connectStr, retryPolicy);
        curatorFramework.start();
        String pathWithParent = "/test";
        byte[] bytes = curatorFramework.getData().forPath(pathWithParent);
        System.out.println(new String(bytes));
        while (true) {

            try {
                byte[] bytes2 = curatorFramework.getData().forPath(pathWithParent);
                System.out.println(new String(bytes2));

                TimeUnit.SECONDS.sleep(5);
            } catch (Exception e) {
                e.printStackTrace();
             }
        }
    }
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值