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()方法关闭.
CuratorFramework实例都是线程安全的, 我们应该当在Zookeeper Cluster中共享同一个CuratorFramework实例. 工厂方法newClient()提供了简便创建client实例方式,而Builder提供了更多的参数控制。一旦你创建了一个CuratorFramework实例,你必须调用它的start()启动,在应用退出时调用close()方法关闭.
本实例所执行代码有maven进行包依赖管理.
pom.xml如下
<!-- zk framework curator start-->
<!-- https://mvnrepository.com/artifact/org.apache.curator/curator-recipes -->
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
<version>2.8.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.curator/curator-client -->
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-client</artifactId>
<version>2.8.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.curator/curator-framework -->
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-framework</artifactId>
<version>2.8.0</version>
</dependency>
<!-- zk framework curator end-->
下面演示了两种创建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<CuratorTransactionResult> transaction(CuratorFramework client) throws Exception {
Collection<CuratorTransactionResult> 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();
}
}