Zookeeper ---- Curator 框架应用


CuratorFramework

     Curator框架提供了一套高级的API, 简化了ZooKeeper的操作。 它增加了很多使用ZooKeeper开发的特性,可以处理ZooKeeper集群复杂的连接管理和重试机制。 这些特性包括:

     1. 自动化的连接管理: 重新建立到ZooKeeper的连接和重试机制存在一些潜在的错误case。 Curator帮助你处理这些事情,对你来说是透明的。
     2. 清理API:
* 简化了原生的ZooKeeper的方法,事件等
* 提供了一个现代的流式接口

     3. 提供了Recipes实现,基于这些Recipes可以创建很多复杂的分布式应用

     Zookeeper在实际生产环境中应用比较广泛, 比如SOA的服务监控系统, hadoop, spark的分布式调度系统. Curator框架提供的优秀特性可以使得我们更加便捷的开发zookeeper应用.


CuratorFramework 实例创建

     Curator框架通过CuratorFrameworkFactory以工厂模式和builder模式创建CuratorFramework实例.
CuratorFramework实例都是线程安全的, 我们应该当在Zookeeper Cluster中共享同一个CuratorFramework实例. 工厂方法newClient()提供了简便创建client实例方式,而Builder提供了更多的参数控制。一旦你创建了一个CuratorFramework实例,你必须调用它的start()启动,在应用退出时调用close()方法关闭.

本实例所执行代码有maven进行包依赖管理.
pom.xml如下
	


    org.apache.curator
    curator-recipes
    2.8.0



    org.apache.curator
    curator-client
    2.8.0



    org.apache.curator
    curator-framework
    2.8.0

	


下面演示了两种创建Curator的方法:
//第一种方法: 工厂模式
//arg1: 重试时间间隔, arg2: 重试最大次数
RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000,3);
CuratorFramework client = CuratorFrameworkFactory.newClient("localhost:2181",retryPolicy);
client.start();

//第二种方法: builder模式
CuratorFramework client =  CuratorFrameworkFactory.builder().connectString(zkAddress)
        .retryPolicy(new ExponentialBackoffRetry(1000, 3))
        .connectionTimeoutMs(1000)
        .sessionTimeoutMs(1000)
        // etc. etc.
        .build();
	    client.start();

完整代码
package com.nxcjh.hadoop.examples.zookeeper.curator;

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.curator.utils.CloseableUtils;

public class CreateClientExample {
    private static final String PATH = "/example/basic";
    private static final String ZKADDRESS = "10.17.110.25:2181,10.17.110.32:2181,10.17.110.36:2181";

    public static void main(String[] args) throws Exception {

        CuratorFramework client = null;
        try {
        	//1. 工厂模式
            client = createSimple(ZKADDRESS);
            client.start();
            client.create().creatingParentsIfNeeded().forPath(PATH, "test".getBytes());
            CloseableUtils.closeQuietly(client);

            //2. builer模式
            client = createWithOptions(ZKADDRESS, new ExponentialBackoffRetry(1000, 3), 1000, 1000);
            client.start();
            System.out.println(new String(client.getData().forPath(PATH)));
            
        } catch (Exception ex) {
            ex.printStackTrace();
        } finally {
            CloseableUtils.closeQuietly(client);
            CloseableUtils.closeQuietly(client);
        }

    }

    /**
     * 工厂方法
     * @param connectionString
     * @return
     */
    public static CuratorFramework createSimple(String connectionString) {
        ExponentialBackoffRetry retryPolicy = new ExponentialBackoffRetry(1000, 3);
        return CuratorFrameworkFactory.newClient(connectionString, retryPolicy);
    }
    
    
    /**
     * builder 模式
     * @param connectionString
     * @param retryPolicy
     * @param connectionTimeoutMs
     * @param sessionTimeoutMs
     * @return
     */
    public static CuratorFramework createWithOptions(String connectionString, RetryPolicy retryPolicy, int connectionTimeoutMs, int sessionTimeoutMs) {
        return CuratorFrameworkFactory.builder().connectString(connectionString)
                .retryPolicy(retryPolicy)
                .connectionTimeoutMs(connectionTimeoutMs)
                .sessionTimeoutMs(sessionTimeoutMs)
                // etc. etc.
                .build();
    }
}

操作数据节点

      zookeeper中, 节点的组织类似于linux的文件系统, 使用path来标识每个节点(znode), znode作为保存数据的容器, 数据量限制在1M以内, 这部分介绍如何使用curator框架创建, 获取, 更新一集删除节点.Curator框架提供了一种流式接口. 操作通过builder串联起来, 这样方法调用类似语句如下:
client.create().forPath("/head", new byte[0]);
client.delete().inBackground().forPath("/head");
client.create().withMode(CreateMode.EPHEMERAL_SEQUENTIAL).forPath("/head/child", new byte[0]);
client.getData().watched().inBackground().forPath("/test");


CuratorFramework提供的方法

方法名 描述
create() 开始创建操作, 可以调用额外的方法(比如方式mode 或者后台执行background) 并在最后调用forPath()指定要操作的ZNode
delete() 开始删除操作. 可以调用额外的方法(版本或者后台处理version or background)并在最后调用 forPath()指定要操作的ZNode
checkExists()                                   开始检查ZNode是否存在的操作. 可以调用额外的方法(监控或者后台处理)并在最后调用forPath()指定要操作的ZNode
getData() 开始获得ZNode节点数据的操作. 可以调用额外的方法(监控、后台处理或者获取状态watch, background or get stat) 并在最后调用forPath()指定要操作的ZNode
setData() 开始设置ZNode节点数据的操作. 可以调用额外的方法(版本或者后台处理) 并在最后调用forPath()指定要操作的ZNode
getChildren()  开始获得ZNode的子节点列表。 以调用额外的方法(监控、后台处理或者获取状态watch, background or get stat) 并在最后调用forPath()指定要操作的ZNode
inTransaction()                  开始是原子ZooKeeper事务. 可以复合create, setData, check, and/or delete 等操作然后调用commit()作为一个原子操作提交


具体实现代码如下
package com.nxcjh.hadoop.examples.zookeeper.curator;


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;

/**
 * 
 * zk 客户端框架测试
 * @author 
 *
 */
public class CuratorTest {

	 private static final String zkAddress = "10.17.110.25:2181,10.17.110.32:2181,10.17.110.36:2181";
	 private static CuratorFramework client = null;

	 
	 /**
	  * 初始化客户端连接
	  */
	 public void setup(){
		 RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 3);
		 client = CuratorFrameworkFactory.newClient(zkAddress, retryPolicy);
		 client.start();
		 System.out.println("客户端连接成功...");
	 }
	 
	 public void init(){
        client =  CuratorFrameworkFactory.builder().connectString(zkAddress)
            .retryPolicy(new ExponentialBackoffRetry(1000, 3))
            .connectionTimeoutMs(1000)
            .sessionTimeoutMs(1000)
            // etc. etc.
            .build();
        client.start();
	 }
	 
	 /**
	  * 创建节点
	  * @param client
	  * @param path
	  * @param createMode
	  * @param data
	  */
	 public void createNode(String path,CreateMode createMode,String data){
		 try {
			String res = client.create().withMode(createMode).forPath(path,data.getBytes());
			System.out.println("创建'"+res+"'节点成功!");
		} catch (Exception e) {
			System.out.println("创建节点失败,elog="+e.getMessage());
		}
	 }
	 
	 /**
	  * 获取节点数据
	  * @param client
	  * @param path
	  * @return
	  */
	 public String getNodeData(String path){
		String res = null;
		try {
			res =  new String(client.getData().forPath(path));
			System.out.println("获取节点数据成功:"+res);
		} catch (Exception e) {
			System.out.println("获取数据失败,elog="+e.getMessage());
		}
		return res;
	 }
	 
	 
	 /**
	  * 更新节点数据
	  * @param path
	  * @param data
	  */
	 public void updateNodeDate(String path,String data){
		 try {
			client.setData().forPath(path,data.getBytes());
			System.out.println("更新节点数据成功!");
		} catch (Exception e) {
			System.out.println("更新数据失败,elog="+e.getMessage());
		}
	 }
	 
	 
	 /**
	  * 删除节点
	  * @param client
	  * @param path
	  */
	  public void delNode(String path){
		  try {
			client.delete().forPath(path);
			System.out.println("删除节点成功!");
		} catch (Exception e) {
			System.out.println("删除节点失败,elog="+e.getMessage());
		}
	  }
	  
	  /**
	   * 关闭客户端
	   */
	  public void close(){
		  client.close();
		  System.out.println("客户端关闭....");
	  }
	  
	  
	  public static void main(String[] args) throws Exception {
		  CuratorTest curator = new CuratorTest();
		  //工厂模式创建
		  curator.setup();
		  //builder模式创建
//		  curator.init();
//		  //创建节点
//		  curator.createNode("/my/zktest", CreateMode.PERSISTENT, "hello curator");
//		  //获取节点
//		  System.out.println(curator.getNodeData("/my/zktest"));
//		  //更新节点
//		  curator.updateNodeDate("/my/zktest", "hello tx");
//		  System.out.println(curator.getNodeData("/my/zktest"));
//		  //删除节点
		  curator.delNode("/my/zktest");
		  curator.close();
		  
	  }
}


zookeeper中节点有两种类型, 临时节点和永久节点, CreateMode类用于指定创建节点的类型, 用户可以选择以下几个参数:
    1. CreateMode.PERSISTENT: 创建节点后, 不删除就永久存在;
    2. CreateMode.PERSISTENT_SEQUENTIAL: 节点path末尾会追加一个10位数的单调递增的序列;
    3. CreateMode.EPHEMERAL: 创建后, 会话结束后会自动删除;
    4. CreateMode.EPHEMERAL_EQUENTAIL: 节点path末尾会追加一个10位数的单调递增的序列.

    forPath函数指定创建节点的path和保存的数据,path的指定遵循linux文件path格式,创建node时指定的path,父path节点需要存在,否则创建节点失败,比如创建"/parent/child"节点,若不存在节点"parent",那么创建节点会失败。在znode中保存的数据需要进行序列化,用户可以选择使用JSON,XML,java内置的序列化机制,或者Hession以及Google的protocal Buffer等,为方便讲解,节点存储字符串数据。

    后台操作的通知和监控可以通过ClientListener接口发布, 你可以在CuratorFramework实例上通过addListener()注册listener, Listener实现了下面的方法:
    * eventReceived(): 一个后台操作完成或者一个监控被触发.
事件类型以及事件的方法如下
Event Type Event Methods
CREATE getResultCode() and getPath()
DELETE getResultCode() and getPath()
EXISTS getResultCode(), getPath() and getStat()
GETDATA getResultCode(), getPath(), getStat() and getData()
SETDATA getResultCode(), getPath() and getStat()
CHILDREN getResultCode(), getPath(), getStat(), getChildren()
WATCHED getWatchedEvent()

还可以通过ConnectionStationStateListener接口监控连接的状态. 强烈推荐增加这个监控器.

你还可以使用命名空间NameSpace避免多个应用的节点的名称冲突. CuratorFramework提供了命名空间的概念, 这样CuratorFramework会为它的API调用的path加上命名空间.
CuratorFramework    client = CuratorFrameworkFactory.builder().namespace("MyApp") ... build();
 ...
client.create().forPath("/test", data);
// node was actually written to: "/MyApp/test"

Curator还提供了临时的CuratorFramework: CuratorTempFramework, 一定时间不活动后连接会被关闭.
创建builder时不是调用build(), 而是调用buildTemp(). 3分钟不活动连接就被关闭, 你也可以指定不活动的时间, 它提供了下面几个方法:
public void close();
public CuratorTransaction inTransaction() throws Exception;
public TempGetDataBuilder getData() throws Exception;

事物

CuratorFramework提供了事务的概念, 可以将一组操作放在一个原子事物中. 什么叫事物? 事物是原子的, 一组操作要么都成功, 要么都失败.
下面的例子演示了事物的操作:
package com.nxcjh.hadoop.examples.zookeeper.curator;

import java.util.Collection;

import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.api.transaction.CuratorTransaction;
import org.apache.curator.framework.api.transaction.CuratorTransactionFinal;
import org.apache.curator.framework.api.transaction.CuratorTransactionResult;

public class TransactionExample {

    public static void main(String[] args) {

    }

    public static Collection transaction(CuratorFramework client) throws Exception {
        Collection results = client.inTransaction().create().forPath("/a/path", "some data".getBytes())
                .and().setData().forPath("/another/path", "other data".getBytes())
                .and().delete().forPath("/yet/another/path")
                .and().commit(); // IMPORTANT!

        for (CuratorTransactionResult result : results) {
            System.out.println(result.getForPath() + " - " + result.getType());
        }
        return results;
    }

    public static CuratorTransaction startTransaction(CuratorFramework client) {
        // start the transaction builder
        return client.inTransaction();
    }

    public static CuratorTransactionFinal addCreateToTransaction(CuratorTransaction transaction) throws Exception {
        // add a create operation
        return transaction.create().forPath("/a/path", "some data".getBytes()).and();
    }

    public static CuratorTransactionFinal addDeleteToTransaction(CuratorTransaction transaction) throws Exception {
        // add a delete operation
        return transaction.delete().forPath("/another/path").and();
    }

    public static void commitTransaction(CuratorTransactionFinal transaction) throws Exception {
        // commit the transaction
        transaction.commit();
    }
}





你可能感兴趣的:(zookeeper)