1、简介
1.1 什么是 Apache Curator ?
官方给出的解释:
Apache Curator is a Java/JVM client library for Apache ZooKeeper, a distributed coordination service. It includes a highlevel API framework and utilities to make using Apache ZooKeeper much easier and more reliable. It also includes recipes for common use cases and extensions such as service discovery and a Java 8 asynchronous DSL.
大致意思是, Apache Curator
是 Apache Zookeeper
的一个java
客户端,一个分布式协调服务。它包含了高级的 API 框架和工具类,可以让我们使用 Apache Zookeeper
更加容易、可靠。还包含了常见的用例和解决方案,比如服务发现和 java 8
异步DSL
。
1.2 组成
Apache Curator 可以在 Maven 仓库进行下载。其中组件的功能也各不相同。下面介绍一下:
Group ID/org | Artifact ID/Name | 描述 |
---|---|---|
org.apache.curator | curator-recipes | 所有的解决方案(食谱)。依赖于curator-client、curator-framework 组件(使用 Maven 时会自动引入)。 |
org.apache.curator | curator-async | 具有异步 DSL、 O/R 建模、迁移等功能。 |
org.apache.curator | curator-framework | 高级 API 组件。建立在客户端之上,并且会自动将 curator-client 引入。 |
org.apache.curator | curator-client | 客户端,用来替换 Zookeeper中的类。 |
org.apache.curator | curator-test | 测试:包含TestingServer、Testing群集和其他一些用于测试的工具。 |
org.apache.curator | curator-examples | 示例用法。 |
org.apache.curator | curator-x-discovery | 服务发现的实现。 |
org.apache.curator | curator-x-discovery-server | RESTful 服务器,可以和 Zookeeper 一起使用。 |
1.3 版本兼容
Zookeeper 3.5.x:Apache Curator 4.0
Zookeeper 3.4.x:使用时必须用低版本 2.x 。如果你要使用 4.0 版本(软兼容模式下),使用 Maven如下,需要排除 zookeeper。
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
<version>${curator-version}</version>
<exclusions>
<exclusion>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
</exclusion>
</exclusions>
</dependency>
2、入门案例
1.1 环境准备
本次测试是在 Windows 环境下:
- zookeeper 版本是 3.4.14
- curator 版本是 2.1.1
- 编译环境:jdk 1.8
1.2 pom.xml 文件
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.feng</groupId>
<artifactId>zookeeper-curator</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<curator-version>2.11.1</curator-version>
</properties>
<dependencies>
<!-- https://mvnrepository.com/artifact/org.apache.curator/curator-framework -->
<!--zookeeper 底层 API 的封装-->
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-framework</artifactId>
<version>${curator-version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.curator/curator-recipes -->
<!--
封装了一些高级特性::Cache事件监听、选举、分布式锁、分布式Barrier
此处会自动引入框架、客户端依赖
-->
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
<version>${curator-version}</version>
</dependency>
<!--日志-->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.29</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
</dependencies>
<!-- Maven 设置编译版本为 jdk 1.8 -->
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>8</source>
<target>8</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
1.3 log4j.properties 文件
log4j.rootLogger=WARN, console
log4j.appender.console=org.apache.log4j.ConsoleAppender
log4j.appender.console.layout=org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=%d{HH:mm:ss,SSS} %-5p %-60c %x - %m%n
1.4 java 类
package org.feng.hello;
import org.apache.curator.RetryPolicy;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.retry.ExponentialBackoffRetry;
/**
* Created by Feng on 2020/1/16 20:06
* CurrentProject's name is zookeeper-curator<br>
* 入门案例:使用 API 连接到 Zookeeper 并创建一个持久结点、存储数据、取出数据并打印。<br>
* 注意:不要重复执行(zookeeper不会创建同一个名字的结点,会抛出节点已经存在异常)
* @author Feng
*/
public class CuratorDemo {
public static void main(String[] args) throws Exception {
// 重试策略:重试之间等待的初始时间,最大的重试次数
RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 3);
// 通过工厂建造出连接实例:client
CuratorFramework client = CuratorFrameworkFactory.builder()
// 连接地址和端口(zookeeper)
.connectString("localhost:2181")
// 会话超时时间
.sessionTimeoutMs(5000)
// 连接超时时间
.connectionTimeoutMs(5000)
.retryPolicy(retryPolicy)
.build();
// 客户端必须开始
client.start();
// 在 zookeeper 创建一个结点
client.create().forPath("/feng2020");
// 给目标结点存储数据
client.setData().forPath("/feng2020", "风景搜".getBytes());
// 取出结点上的数据
byte[] bytes = client.getData().forPath("/feng2020");
// 打印数据到控制台
System.out.println(new String(bytes));
// 客户端必须关闭
client.close();
}
}
3、增删改查案例
CuratorFramework 连接对象的相关练习
注意:
- 该对象完全线程安全,可以在集群中共享使用。
- 一般使用工厂产生该实例。
- 该实例需要使用
start()
启动;并在不使用时,使用close()
进行关闭。
此部分参考了官方案例:官方案例
3.1 环境准备
与【2、入门案例】保持一致
3.2 pom.xml 文件 和 log4j.properties 文件
与【2、入门案例】保持一致
3.3 java 类
3.3.1 CuratorOperator.java
package org.feng.hello;
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.CuratorListener;
import org.apache.curator.retry.ExponentialBackoffRetry;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.data.Stat;
import java.util.List;
/**
* Created by Feng on 2020/1/17 9:57
* CurrentProject's name is zookeeper-curator
* 核心操作类
* @author Feng
*/
public class CuratorOperator {
/**
* 客户端连接对象
*/
static CuratorFramework client;
/**
* 初始化数据
*/
private void init(){
// 重试策略
RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 3);
// 通过工厂建造出连接实例:client
client = CuratorFrameworkFactory.builder()
// 连接地址和端口(zookeeper)
.connectString("localhost:2181")
// 会话超时时间
.sessionTimeoutMs(5000)
// 连接超时时间
.connectionTimeoutMs(5000)
.retryPolicy(retryPolicy)
.build();
}
CuratorOperator() {
init();
}
/**
* 创建持久结点
* @param path zookeeper 中的文件路径
* @param data 数据
*/
public void create(String path, String data) throws Exception {
client.create().forPath(path, data.getBytes());
}
/**
* 创建结点:根据结点的模式。如下,
* <ul>
* <li>PERSISTENT:持久结点,客户端断开连接后,不会自动删除</li>
* <li>PERSISTENT_SEQUENTIAL:序列持久结点</li>
* <li>EPHEMERAL:临时结点,客户端断开连接后,会自动删除</li>
* <li>EPHEMERAL_SEQUENTIAL:序列临时结点</li>
* <li>CONTAINER:容器结点,有特殊用途。当其中的最后一个被删除时,它也将会成为被删除的候选项</li>
* <li>PERSISTENT_WITH_TTL:TTL的持久结点。结点没有在给定的 TTL范围内做出修改,在没有子节点时会被删除</li>
* <li>PERSISTENT_SEQUENTIAL_WITH_TTL:TTL的序列化持久结点</li>
* </ul>
*
* @param path zookeeper 中的路径
* @param data 数据
* @param mode {@link CreateMode} 中的静态常量
* @see CreateMode
*/
public String createWithMode(String path, String data, CreateMode mode) throws Exception {
return client.create().withProtection().withMode(mode).forPath(path, data.getBytes());
}
/**
* 给指定路径存数据
* @param path zookeeper 中的路径
* @param data 数据
*/
public void setData(String path, String data) throws Exception {
client.setData().forPath(path, data.getBytes());
}
/**
* 异步设置数据
* @param path zookeeper 中的路径
* @param data 数据
* @see CuratorListener
*/
public void setDataAsync(String path, String data) throws Exception {
// 事件或异步通知:监听、检查事件以获取详细信息
CuratorListener listener = (client1, event) -> System.out.println("事件状态:" + event.getStat());
// 增加监听
client.getCuratorListenable().addListener(listener);
// 设置数据
client.setData().inBackground().forPath(path, data.getBytes());
}
/**
* 回调设置数据
*/
public void setDataAsyncWithCallback(BackgroundCallback callback, String path,
String data) throws Exception {
client.setData().inBackground(callback).forPath(path, data.getBytes());
}
/**
* 删除指定节点(文件)
* @param path zookeeper 中的路径
*/
public void delete(String path) throws Exception {
client.delete().forPath(path);
}
/**
* 删除:保证会执行。<br>
* 若删除失败,会抛出异常信息。但是,会一直尝试删除,直到成功删除。
* @param path zookeeper 中的路径
*/
public void guartanteedDelete(String path) throws Exception {
client.delete().guaranteed().forPath(path);
}
/**
* 获取子节点,并设置 watcher 在节点上。
* @param path zookeeper 上的路径
* @param watcher {@link Watcher}
*/
public List<String> watchedGetChildren(String path, Watcher watcher) throws Exception {
return client.getChildren().usingWatcher(watcher).forPath(path);
}
/**
* 获取子节点,并设置 watcher 在节点上。观察者通知时会通过监听
*/
public List<String> watchedGetChildren(String path) throws Exception {
return client.getChildren().watched().forPath(path);
}
/**
* 获取数据
* @param path 指定一个 zookeeper 的结点
* @return 数据
*/
public String getData(String path) throws Exception {
return new String(client.getData().forPath(path));
}
/**
* 判断节点是否存在
* @param path 节点所在路径
* @return 存在目标节点时返回一个 {@link Stat};否则返回 null
*/
public Stat nodeExist(String path) throws Exception {
return client.checkExists().forPath(path);
}
}
3.3.2 CuratorOperatorTest.java
package org.feng.hello;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import java.util.UUID;
/**
* Created by Feng on 2020/1/17 11:00
* CurrentProject's name is zookeeper-curator
* 测试:增加节点、修改数据、删除节点、获取数据
*/
public class CuratorOperatorTest {
private CuratorOperator operator;
private String path;
@Before
public void init(){
operator = new CuratorOperator();
// 客户端必须开始
CuratorOperator.client.start();
// 获取一个路径
path = "/" + UUID.randomUUID();
}
/**
* 创建 和 删除
*/
@Test
public void createAndDeleteTest() throws Exception {
// 创建一个节点
operator.create(path, "小冯同学");
// 判空:通过测试即节点存在
Assert.assertNotNull(operator.nodeExist(path));
// 获取数据
Assert.assertEquals("小冯同学", operator.getData(path));
// 改值
operator.setData(path, "小王");
// 获取数据
Assert.assertEquals("小王", operator.getData(path));
// 删除
operator.delete(path);
// 判空:通过测试即节点不存在
Assert.assertNull(operator.nodeExist(path));
}
@After
public void close(){
CuratorOperator.client.close();
}
}
3.4 事务操作
请参考:官方案例