what is Curator ?

zookeeper 清华 zookeeper curator_Curator API

Curator是zookeeper分布式协调服务的java客户端库,它包装了一系列操作zk的高级API和实用库,是的操作zk变得更加容易和可靠。例如使用原生zk的API实现分布式锁的话,代码量多,复杂,使用Curator后就相对简单的多,很多底层的api都直接封装好了,开箱即用,学习成本低。

 

Getting Started

1、使用Curator之前,你需要引入maven依赖

<dependency>
   <groupId>org.apache.curator</groupId>
   <artifactId>curator-framework</artifactId>
   <version>2.8.0</version>
</dependency>

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

2、实例化Curator,你可以通过CuratorFrameworkFactory类提供的来产生一个CuratorFramework对象

CuratorFrameworkFactory.newClient(zookeeperConnectionString, retryPolicy)

zookeeperConnectionString就是连接的ip:端口信息,retryPolicy是重试策略,Curator提供了三种常用的重试策略,这里不详述

zookeeper 清华 zookeeper curator_Zookeeper客户端Curator_02

 

RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 3)
CuratorFramework client = CuratorFrameworkFactory.newClient(zookeeperConnectionString, retryPolicy);
client.start();

或者你也可以使用链式调用来实例化curator

CuratorFramework curatorFramework = CuratorFrameworkFactory.builder()
        .connectString(zookeeperConnectionString)
        .sessionTimeoutMs(5000)
        .connectionTimeoutMs(5000)
        .retryPolicy(retryPolicy)
        .build();

curatorFramework.start();

How to use Curator API operate ZK ?

zookeeper 清华 zookeeper curator_zookeeper 清华_03

curator操作zk的api主要包括 节点的增删改查、节点判断、节点监听等,下面的代码演示了如何使用基本的curator api

import org.apache.curator.RetryPolicy;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.framework.api.BackgroundCallback;
import org.apache.curator.framework.api.CuratorEvent;
import org.apache.curator.framework.recipes.cache.*;
import org.apache.curator.retry.RetryUntilElapsed;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.data.Stat;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
* 类描述:zookeeper客户端curator使用demo
*/
public class CuratorDemo {

    private static final Logger logger = LoggerFactory.getLogger(CuratorDemo.class);

    private static final String NODE_PATH = "/node_8";
    private static final String CONNECT_TOSTRING = "10.200.121.46:2181";

    /*创建线程池,供给异步使用curator时调用*/
    public static ExecutorService executorService = Executors.newCachedThreadPool();

    public static void main(String[] args) throws Exception {
        try {
        
/*重试策略一:重试三次,每重试一次,重试的间隔时间会越来越大

        RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 3);
*/
/*重试策略二:最多重试三次,每次重试间隔1s

        RetryPolicy retryPolicy1 = new RetryNTimes(3,1000);
*/

/*重试策略三:最大重试时间总和不超过5s,每次重试间隔为1s*/
            RetryPolicy retryPolicy2 = new RetryUntilElapsed(5000, 1000);

/*
        
/*      方式一建立zookeeper连接
        CuratorFramework curatorFramework = CuratorFrameworkFactory.newClient(CONNECT_TOSTRING,5000,5000,retryPolicy2);
*/

            /*方式二建立zookeeper连接*/
            CuratorFramework curatorFramework = CuratorFrameworkFactory.builder()
                    .connectString(CONNECT_TOSTRING)
                    .sessionTimeoutMs(5000)
                    .connectionTimeoutMs(5000)
                    .retryPolicy(retryPolicy2)
                    .build();

            curatorFramework.start();

        /*创建节点数据*/        
 curatorFramework.create().creatingParentsIfNeeded().withMode(CreateMode.PERSISTENT).forPath(NODE_PATH, "456".getBytes());

        /*删除节点(包含子节点)*/            curatorFramework.delete().guaranteed().deletingChildrenIfNeeded().withVersion(-1).forPath(NODE_PATH);

        /*获取子节点*/
        List<String> strings = curatorFramework.getChildren().forPath(NODE_PATH);

        /*获取节点数据内容*/
        byte[] bytes = curatorFramework.getData().forPath(NODE_PATH);
        System.out.println(new String(bytes));
        
       /*获取节点数据内容+状态信息*/
       Stat stat = new Stat();
        byte[] result = curatorFramework.getData().storingStatIn(stat).forPath(NODE_PATH);
        System.out.println(new String(result));

        /*修改节点数据内容*/
        curatorFramework.setData().forPath(NODE_PATH, "123".getBytes());

       /*判断节点是否存在*/
        Stat stat1 = curatorFramework.checkExists().forPath(NODE_PATH);

        /*异步操作,以判断节点是否存在为例,注意使用线程池以便节省单个线程的创建销毁开销,及最后线程的关闭*/
        curatorFramework.checkExists().inBackground(new BackgroundCallback() {
                @Override
                public void processResult(CuratorFramework curatorFramework, CuratorEvent curatorEvent) throws Exception {

                    Object context = curatorEvent.getContext();   //这里的上下文就是 传递进去的"123456"

                }
            }, "12345", executorService).forPath(NODE_PATH);

          /*设置节点事件监听*/
          final NodeCache nodeCache = new NodeCache(curatorFramework, NODE_PATH);
            nodeCache.start();
            nodeCache.getListenable().addListener(new NodeCacheListener() {
                @Override
                public void nodeChanged() throws Exception {
                    byte[] result = nodeCache.getCurrentData().getData();
                    logger.info("事件监听result=" + new String(result));
                }
            });

            /*设置子节点事件监听*/
            final PathChildrenCache childrenCache = new PathChildrenCache(curatorFramework, NODE_PATH, true);
            childrenCache.start();
            childrenCache.getListenable().addListener(new PathChildrenCacheListener() {
                @Override
                public void childEvent(CuratorFramework curatorFramework, PathChildrenCacheEvent pathChildrenCacheEvent) throws Exception {
                    PathChildrenCacheEvent.Type type = pathChildrenCacheEvent.getType();
                    switch (type) {
                        case CHILD_ADDED:
                            logger.info("");
                        case CHILD_UPDATED:
                            logger.info("");
                        case CHILD_REMOVED:
                            logger.info("");
                        default:
                            break;
                    }
                }
            });
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            executorService.shutdown();
        }
    }

}

使用Curator实现Master选举

Curator中包装的master选举包含两种,Leader Latch和Leader Election,原理采用的是zk的节点特性,即多个客户端同时创建同一节点,zk保证只有一个客户端能创建成功,成功的客户端即为master节点,再master节点的机器上执行业务。Leader  Latcher里面即包装了zk创建节点、设置监听,对应的操作均包装在LeaderLatch类中。LeaderLatch将随机选出一个master

import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.framework.recipes.leader.LeaderLatch;
import org.apache.curator.retry.ExponentialBackoffRetry;
import org.springframework.context.annotation.Lazy;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

/**
* 类描述:基于Curator的Leader Latch实现的master选举
* 创建人:simonsfan
*/
@Component
public class CuratorLeaderLatch {

    private static CuratorFramework curatorFramework;

    private static LeaderLatch leaderLatch;

    private static final String path = "/root/leaderlatch";

    private static final String connectStr = "10.200.121.46:2181,10.200.121.159:2181,10.200.121.168:2181";

    static {
        curatorFramework = CuratorFrameworkFactory
                .builder()
                .connectString(connectStr)
                .retryPolicy(new ExponentialBackoffRetry(1000, 3))
                .connectionTimeoutMs(5000)
                .sessionTimeoutMs(5000)
                .build();

        curatorFramework.start();
        leaderLatch = new LeaderLatch(curatorFramework, path.concat("/testtast"));
    }

    @Lazy
    @Scheduled(cron = "")
    public void testTask() {
        //是master节点的执行业务流程,使用leaderLatch.hasLeadership()方法判断是否为leader,true表示是master节点
        if (!leaderLatch.hasLeadership()) return;
        //TODO something

    }
    
}

使用Curator实现分布式锁

这里讲的的是Shared Reentrant Lock(共享可重入锁,推荐使用,Curator还封装了其他类型的锁:共享不可重入锁之类的):全局同步的、公平的分布式共享重入式锁,可保证在任意同一时刻,只有一个客户端持有锁。使用到的类是InterProcessMutex

import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.framework.api.BackgroundCallback;
import org.apache.curator.framework.api.CuratorEvent;
import org.apache.curator.framework.recipes.locks.InterProcessMutex;
import org.apache.curator.retry.ExponentialBackoffRetry;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

/**
* 类描述:Curator实现的分布式锁
* 创建人:simonsfan
*/
public class DistributedLock {

    private static CuratorFramework curatorFramework;

    private static InterProcessMutex interProcessMutex;

    private static final String connectString = "10.200.121.46:2181,10.200.121.43:2181,10.200.121.167:2181";

    private static final String root = "/root";

    private static ExecutorService executorService;

    private String lockName;

    public String getLockName() {
        return lockName;
    }

    public void setLockName(String lockName) {
        this.lockName = lockName;
    }

    static {
        curatorFramework = CuratorFrameworkFactory.builder().connectString(connectString).connectionTimeoutMs(5000).sessionTimeoutMs(5000).retryPolicy(new ExponentialBackoffRetry(1000, 3)).build();
        executorService = Executors.newCachedThreadPool();
        curatorFramework.start();
    }

    public DistributedLock(String lockName) {
        this.lockName = lockName;
        interProcessMutex = new InterProcessMutex(curatorFramework, root.concat(lockName));
    }

    /*上锁*/
    public void tryLock() {
        int count = 0;
        try {
            while (!interProcessMutex.acquire(1, TimeUnit.SECONDS)) {
                count++;
                if (count > 3) {
                    break;
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /*释放*/
    public void releaseLock() {
        try {
            if (interProcessMutex != null) {
                interProcessMutex.release();
            }
            curatorFramework.delete().inBackground(new BackgroundCallback() {
                @Override
                public void processResult(CuratorFramework curatorFramework, CuratorEvent curatorEvent) throws Exception {

                }
            }, executorService).forPath(root.concat(lockName));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}