`
QING____
  • 浏览: 2251617 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

Apache curator-client详解

 
阅读更多

    Apache curator框架中curator-client组件可以作为zookeeper client来使用,它提供了zk实例创建/重连机制等,简单便捷.不过直接使用curator-client并不能减少太多的开发量,因为它相对比较底层,稍后我们继续了解curator-framework组件提供的更多的便捷特性.

 

一.核心API

    1. CuratorZookeeperClient: zookeeper客户端,根据指定的配置信息创建zookeeper实例.

    2. RetryPolicy接口: 重连策略,当zookeeper失去链接时使用的"重连策略": 

        <> RetryOneTime: 只重连一次.

        <> RetryNTime: 指定重连的次数N.

        <> RetryUtilElapsed: 指定最大重连超时时间和重连时间间隔,间歇性重连直到超时或者链接成功.

        <> ExponentialBackoffRetry: 基于"backoff"方式重连,和RetryUtilElapsed的区别是重连的时间间隔是动态的.

 

时间间隔 = baseSleepTimeMs * Math.max(1, random.nextInt(1 << (retryCount + 1))).

 

        <> BoundedExponentialBackoffRetry: 同ExponentialBackoffRetry,增加了最大重试次数的控制.

    3. RetryLoop: 操作重试,如果在执行一个操作时,遇到了zk链接异常,怎么办?RetryLoop可以不断重试,直到网络正常且操作执行成功为止.SessionFailRetryLoop类是一个特列,可以兼容当session失效时,如何进行操作重试.

    4. EnsembleProvider: 配置提供者,创建zk客户端时,需要指定connectionString(例如:127.0.0.1:2181),在zookeeper API中只能显式的指定,curator在这个方面提供了更加灵活性的方式,你可以通过任何方式获取或者构建connectionString.

        <> FixedEnsembleProvider: 使用固定的字符串作为connectionString.

        <> ExhibitorEnsembleProvider: 动态的获取connectionString,可以指定一个URL用来提供connectionString的输出服务.此后此Provider将会间歇性的获取最新的connectionString字符串,并保存.事实上,ExhibitorEnsembleProvider只是一个样例,展示了一种动态获取connectionString的方式,如果在真正的开发中,你可能需要参考它,来定制自己的Provider.

 

二. 通用客户端代码示例

public class ZooKeeperClient extends Thread{

    protected final CuratorZookeeperClient zkClient;
    protected String parent;

    public static final Charset charset = Charset.forName("utf-8");

    private ZNodeWatcher zNodeWatcher = new ZNodeWatcher();//自定义watcher

    public ZooKeeperClient(String connectString, int sessionTimeout, String parent) throws Exception {
        this.parent = parent;
        zkClient = new CuratorZookeeperClient(connectString, sessionTimeout, sessionTimeout, zNodeWatcher, new ExponentialBackoffRetry(1000, Integer.MAX_VALUE));
        zkClient.start();//must,but anytime before zookeeper operation
        zkClient.blockUntilConnectedOrTimedOut(); //first connection should be successful
    }


    public boolean exist(String path,boolean watched) throws Exception{
        return zkClient.getZooKeeper().exists(path,watched) == null ? false : true;
    }

    /**
     * 此path必须存在,如果不存在则立即创建
     * @param path
     * @return
     */
    public boolean ensurePath(final String path) throws Exception{
        PathUtils.validatePath(path);
        return RetryLoop.callWithRetry(zkClient, new Callable<Boolean>(){
            @Override
            public Boolean call() throws Exception {
                EnsurePath ensure = new EnsurePath(path);
                ensure.ensure(zkClient);
                return true;
            }
        });
    }

    /**
     *
     * @param path
     * @param data
     * @return   如果path已经存在或者创建成功,则返回true,否则返回false。
     * @throws Exception
     */
    public boolean create(final String path, final String data) throws Exception {
        PathUtils.validatePath(path);//if bad format,here will throw some Exception;
        return RetryLoop.callWithRetry(zkClient, new Callable<Boolean>() {

            @Override
            public Boolean call() throws Exception {
                int _current = 0;
                while (_current < 3) {
                    _current++;
                    try {
                        //zkClient.blockUntilConnectedOrTimedOut();
                        //确保父节点存在
                        EnsurePath ensure = new EnsurePath(path).excludingLast();
                        //parent path should be existed.
                        //EnsurePath: retry + block
                        ensure.ensure(zkClient); //ugly API
                        zkClient.getZooKeeper().create(path, data.getBytes(charset), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
                        return true;
                    } catch (KeeperException.NodeExistsException e) {
                        return true;
                    }
                    //retry only for KeeperException,not for other runtimeException。
                    //other exception will be thrown,and stop retry!!
                    //if no Exception thrown,retry will be stopped and return successfully.
                }
                return false;
            }
        }) ;
    }
	
	
    public  class ZNodeWatcher implements Watcher{
        @Override
        public void process(WatchedEvent event) {
            Event.EventType eventType = event.getType();
            Event.KeeperState keeperState =  event.getState();
            String path = event.getPath();
            switch(event.getType()) {
                case None:
                    //connection Error:会自动重连
                    logger.info("[Watcher],Connecting...");
                    if(keeperState == Event.KeeperState.SyncConnected){
                        logger.info("[Watcher],Connected...");
						//检测临时节点是否失效等。
                    }
                    break;
                case NodeCreated:
                    logger.info("[Watcher],NodeCreated:" + path);
                    break;
                case NodeDeleted:
                    logger.info("[Watcher],NodeDeleted:" + path);
                    break;
                default:
                    //
            }
        }
    }
}

 

 

三. Provider代码实例

    本实例展示了如何使用curator-client开发简单的API,展示了RetryPolicy,RetryLoop的使用方式;实例中使用Curator自带的ExhibitorEnsembleProvider动态获取zookeeper服务器列表信息.

    1. pom.xml

<dependency>
	<groupId>org.apache.zookeeper</groupId>
	<artifactId>zookeeper</artifactId>
	<version>3.4.5</version>
	
</dependency>
<dependency>
	<groupId>org.apache.curator</groupId>
	<artifactId>curator-recipes</artifactId>
	<version>2.3.0</version>
</dependency>

    2. curator-config.propeties 

host.rest.servers=127.0.0.1,localhost
host.rest.port=8080
host.backup=127.0.0.1:2181
host.rest.path=/servers/zk
host.rest.period=180000

    3. IZkClient.java  

package com.test.demo.curator;

import java.util.*;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;

import org.apache.curator.CuratorZookeeperClient;
import org.apache.curator.RetryLoop;
import org.apache.curator.RetryPolicy;
import org.apache.curator.TimeTrace;
import org.apache.curator.drivers.TracerDriver;
import org.apache.curator.ensemble.EnsembleProvider;
import org.apache.curator.ensemble.exhibitor.DefaultExhibitorRestClient;
import org.apache.curator.ensemble.exhibitor.ExhibitorEnsembleProvider;
import org.apache.curator.ensemble.exhibitor.ExhibitorRestClient;
import org.apache.curator.ensemble.exhibitor.Exhibitors;
import org.apache.curator.retry.ExponentialBackoffRetry;
import org.apache.curator.retry.RetryNTimes;
import org.apache.curator.utils.EnsurePath;
import org.apache.curator.utils.PathUtils;
import org.apache.curator.utils.ZKPaths;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.Transaction;
import org.apache.zookeeper.ZooDefs;
import org.apache.zookeeper.data.ACL;


public class IZkClient {

    private final CuratorZookeeperClient zkClient;

    public IZkClient(String configLocation) throws Exception {

        Properties properties = new Properties();
        properties.load(ClassLoader.getSystemResourceAsStream(configLocation));
        EnsembleProvider provider = buildProvider(properties);
        String pTimeout = properties.getProperty("zk.timeout");
        Integer timeout = 30000;
        if (pTimeout != null) {
            timeout = Integer.valueOf(pTimeout);
        }
        zkClient = new CuratorZookeeperClient(provider, timeout, timeout, null, new ExponentialBackoffRetry(1000, Integer.MAX_VALUE));
        zkClient.setTracerDriver(new PrintTraceDrive());
        zkClient.start();//must,but anytime before zookeeper operation
        zkClient.blockUntilConnectedOrTimedOut(); //first connection should be successful
    }

    /**
     * build provider,all of params from config-file
     * @param properties
     * @return
     */
    private EnsembleProvider buildProvider(Properties properties) {
        String servers = properties.getProperty("host.rest.servers");   //hosts.servers = 127.0.0.1,127.0.0.2
        if (servers == null || servers.isEmpty()) {
            throw new IllegalArgumentException("host.servers cant be empty");
        }
        List<String> hostnames = Arrays.asList(servers.split(","));
        String port = properties.getProperty("host.rest.port");
        Integer restPort = 80;   //default
        if (port != null) {
            restPort = Integer.valueOf(port);
        }
        final String backupAddress = properties.getProperty("host.backup");//127.0.0.1:2181
        //if network is error,you should sepcify a backup zk-connectString
        Exhibitors exhibitors = new Exhibitors(hostnames, restPort, new Exhibitors.BackupConnectionStringProvider() {
            @Override
            public String getBackupConnectionString() throws Exception {
                return backupAddress;
            }
        });
        //rest,as meaning of getting fresh zk-connectString list.
        ExhibitorRestClient restClient = new DefaultExhibitorRestClient();
        String restUriPath = properties.getProperty("host.rest.path");
        String period = properties.getProperty("host.rest.period");
        Integer pollingMs = 180000; //3 min
        if (period != null) {
            pollingMs = Integer.valueOf(period);
        }
        return new ExhibitorEnsembleProvider(exhibitors, restClient, restUriPath, pollingMs, new RetryNTimes(10, 1000));
    }

    public CuratorZookeeperClient getZkClient() {
        return zkClient;
    }

    /**
     * how to use RtryLoop ,another style
     * if Znode has been existed,will delete it,and create it again.
     *
     */
    public boolean replace(final String path,final byte[] value){
        PathUtils.validatePath(path);
        boolean result = false;
        try{
            result = RetryLoop.callWithRetry(zkClient,new Callable<Boolean>() {

                @Override
                public Boolean call() throws Exception {
                    int _current = 0;
                    while(_current < 3){
                        _current++;
                        try{
                            zkClient.blockUntilConnectedOrTimedOut();
                            Transaction tx = zkClient.getZooKeeper().transaction();
                            tx.delete(path, -1);
                            tx.create(path,value,ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
                            tx.commit();
                            return true;
                        } catch (KeeperException.NoNodeException e){
                             //
                        } catch (KeeperException.NodeExistsException e){
                            //
                        }
                    }
                    return false;  //To change body of implemented methods use File | Settings | File Templates.
                }
            }) ;
        }catch (Exception e){
            e.printStackTrace();
        }
        return result;
    }

    //API : on for test
    public String createPath(String path, byte[] value) throws Exception {
        PathUtils.validatePath(path);//if bad format,here will throw some Exception;
        EnsurePath ensure = new EnsurePath(path).excludingLast();
        //parent path should be existed.
        //EnsurePath: retry + block
        ensure.ensure(zkClient); //ugly API
        return zkClient.getZooKeeper().create(path, value, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
    }

    //API: on for test
    public boolean createPath(String path, byte[] value,int blockTimes){
		if (!zkClient.isConnected() && blockTimes == 0) {
			return false;
		}
        TimeTrace trace = zkClient.startTracer("createPath:" + path);//log message
        try{
            EnsurePath ensure = new EnsurePath(path).excludingLast();
            ensure.ensure(zkClient);//only for persistent node
            RetryLoop loop = zkClient.newRetryLoop();
            int _current = 0;
            while(loop.shouldContinue()){
                 try{
                     if(_current >= blockTimes){
                         loop.markComplete(); //stop here.
                         continue;
                     }
                     //blocking
                     boolean isConnected = zkClient.blockUntilConnectedOrTimedOut();
                     if(!isConnected){
                         _current++;
                         continue;
                     }
                     zkClient.getZooKeeper().create(path, value, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
                     loop.markComplete();
                 } catch (KeeperException.NodeExistsException e){
                     loop.markComplete();//exist ,stop here
                 } catch (Exception e){
                     loop.takeException(e);
                 }
            }
        } catch (Exception e){
            e.printStackTrace();
            return false;  //cant create path
        } finally{
            trace.commit();
        }
        return true;
    }

    public byte[] getData(String path) throws Exception{
        PathUtils.validatePath(path);
        return zkClient.getZooKeeper().getData(path,false,null);
    }
	public void close(){
		zkClient.close();
	}
    class PrintTraceDrive implements TracerDriver {
        @Override
        public void addTrace(String name, long time, TimeUnit unit) {
            System.out.println("<Trace>" + name + ";time:" + TimeUnit.MILLISECONDS.convert(time, unit) + " ms");
        }

        @Override
        public void addCount(String name, int increment) {
        }
    }
}

    4. IZkClientMain.java(for testing) 

public class IZkClientMain {


    public static void main(String[] args) throws Exception {
        String configLocation = "curator-config.properties";
        IZkClient iZkClient = new IZkClient(configLocation);
        String value = "curator-demo";
        String path = "/curator/child/0";

        iZkClient.replace(path, value.getBytes("utf-8"));
        //simple method;
        String nodeName = ZKPaths.getNodeFromPath(path);
        System.out.print(nodeName);
        //value
        byte[] bytes = iZkClient.getData(path);
        System.out.println(new String(bytes, "utf-8"));
        Thread.sleep(180000 * 2);
		iZkClient.close();
    }
}

    5. ExhibitorEnsembleProvider需要使用远端的一个REST风格的Url来提供zookeeper服务器列表,如下为Spring方式:

@Controller
@RequestMapping("/servers")
public class ServersController {

    @RequestMapping(value = "/zk",headers="Accept=application/x-www-form-urlencoded")
    public void zk(HttpServletResponse response) throws  Exception{
        FormHttpMessageConverter converter = new FormHttpMessageConverter();
        converter.setCharset(Charset.forName("utf-8"));
        HttpOutputMessage output = new ServletServerHttpResponse(response);
        converter.write(buildServers(), MediaType.APPLICATION_FORM_URLENCODED,output);
        //String servers = "count=2&port=2181&server0=127.0.0.1&server1=localhost";
    }


    private MultiValueMap<String,Object> buildServers(){
        MultiValueMap<String,Object> map = new LinkedMultiValueMap<String, Object>();
        map.add("count","2");
        map.add("port","2181");
        map.add("server0","127.0.0.1");
        map.add("server1","localhost");
        return map;
    }

}

  

   备注:Curator-client可以帮助我们进行链接重连操作,重连过程中,我们不需要关注太多,不过你仍然可以通过注册Watcher的手段来活的通知.如果在操作过程中,zk的链接有异常,你可以通过RetryLoop的方式实现阻塞. 

分享到:
评论

相关推荐

    curator-client-2.6.0-API文档-中文版.zip

    Maven坐标:org.apache.curator:curator-client:2.6.0; 标签:curator、apache、client、jar包、java、中文文档; 使用方法:解压翻译后的API文档,用浏览器打开“index.html”文件,即可纵览文档内容。 人性化翻译...

    curator-client-2.7.1-API文档-中英对照版.zip

    Maven坐标:org.apache.curator:curator-client:2.7.1; 标签:curator、apache、client、jar包、java、API文档、中英对照版; 使用方法:解压翻译后的API文档,用浏览器打开“index.html”文件,即可纵览文档内容。...

    curator-client-4.0.1-API文档-中英对照版.zip

    Maven坐标:org.apache.curator:curator-client:4.0.1; 标签:apache、curator、client、中英对照文档、jar包、java; 使用方法:解压翻译后的API文档,用浏览器打开“index.html”文件,即可纵览文档内容。 人性化...

    curator-client-4.0.1-API文档-中文版.zip

    Maven坐标:org.apache.curator:curator-client:4.0.1; 标签:apache、curator、client、中文文档、jar包、java; 使用方法:解压翻译后的API文档,用浏览器打开“index.html”文件,即可纵览文档内容。 人性化翻译...

    curator-client-2.7.1-API文档-中文版.zip

    Maven坐标:org.apache.curator:curator-client:2.7.1; 标签:curator、apache、client、jar包、java、中文文档; 使用方法:解压翻译后的API文档,用浏览器打开“index.html”文件,即可纵览文档内容。 人性化翻译...

    curator-client-4.3.0-API文档-中英对照版.zip

    Maven坐标:org.apache.curator:curator-client:4.3.0; 标签:apache、curator、client、中英对照文档、jar包、java; 使用方法:解压翻译后的API文档,用浏览器打开“index.html”文件,即可纵览文档内容。 人性化...

    curator-client-2.6.0-API文档-中英对照版.zip

    Maven坐标:org.apache.curator:curator-client:2.6.0; 标签:curator、apache、client、jar包、java、API文档、中英对照版; 使用方法:解压翻译后的API文档,用浏览器打开“index.html”文件,即可纵览文档内容。...

    curator-client-2.4.2.zip

    【标题】"curator-client-2.4.2.zip" 是一个包含Apache Curator客户端的压缩包,版本为2.4.2。Apache Curator是一个针对Apache ZooKeeper的高级客户端库,ZooKeeper是一个分布式协调服务,常用于管理分布式应用程序...

    curator-client

    2. `curator-client-1.1.10.jar`和`curator-client-1.1.16.jar`: 这两个版本属于Curator的1.x系列,相比于1.0.1版本,它们引入了更多的优化和新特性,比如增强的连接管理、更好的错误处理机制、以及对Zookeeper新...

    curator-client-1.0.1.jar

    java运行依赖jar包

    curator-client-2.10.0.jar

    java运行依赖jar包

    curator-client-2.4.2.jar

    zookeeper的封装的框架,比zookeeper原生的api好用很多,比如分布式事务,分布式计数器等

    curator-framework-4.0.1-API文档-中文版.zip

    Maven坐标:org.apache.curator:curator-framework:4.0.1; 标签:apache、curator、framework、中文文档、jar包、java; 使用方法:解压翻译后的API文档,用浏览器打开“index.html”文件,即可纵览文档内容。 人性...

    curator-recipes-2.6.0-API文档-中文版.zip

    Maven坐标:org.apache.curator:curator-recipes:2.6.0; 标签:recipes、curator、apache、jar包、java、中文文档; 使用方法:解压翻译后的API文档,用浏览器打开“index.html”文件,即可纵览文档内容。 人性化...

    curator-framework-4.3.0-API文档-中英对照版.zip

    Maven坐标:org.apache.curator:curator-framework:4.3.0; 标签:apache、curator、framework、中英对照文档、jar包、java; 使用方法:解压翻译后的API文档,用浏览器打开“index.html”文件,即可纵览文档内容。 ...

    curator-client-5.1.0.jar

    curator-client-5.1.0.jar

    curator-test单元测试zookeeper

    使用apache curator-test单元测试zookeeper

    apache-curator-3.2.0

    在 `apache-curator-3.2.0` 这个压缩包中,我们可以找到 Curator 库的源代码,这对于理解其内部工作原理和进行定制化开发非常有帮助。以下是这个库中的主要组件和概念: 1. **Curator Framework**:这是 Curator 的...

    curator-framework-4.0.1-API文档-中英对照版.zip

    Maven坐标:org.apache.curator:curator-framework:4.0.1; 标签:apache、curator、framework、中英对照文档、jar包、java; 使用方法:解压翻译后的API文档,用浏览器打开“index.html”文件,即可纵览文档内容。 ...

Global site tag (gtag.js) - Google Analytics