单机环境下使用Curator连接zk服务端
1、pom文件与基础类
1.1、pom文件引入依赖
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.7.0</version>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
<version>5.0.0</version>
<exclusions>
<exclusion>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13</version>
</dependency>
1.2、基础类
后面每个api的操作都可以通过这个类来获取zookeeper的客户端。
public abstract class CuratorStandaloneBase {
private static final String CONNECT_STR = "192.168.231.131:2181";
private static final int sessionTimeoutMs = 60 * 1000;
private static final int connectionTimeoutMs = 5000;
private static CuratorFramework curatorFramework;
@Before
public void init() {
RetryPolicy retryPolicy = new ExponentialBackoffRetry(5000, 30);
curatorFramework = CuratorFrameworkFactory.builder().connectString(getConnectStr())
.retryPolicy(retryPolicy)
.sessionTimeoutMs(sessionTimeoutMs)
.connectionTimeoutMs(connectionTimeoutMs)
.canBeReadOnly(true)
.build();
curatorFramework.getConnectionStateListenable().addListener((client, newState) -> {
if (newState == ConnectionState.CONNECTED) {
System.out.println("连接成功!");
}
});
System.out.println("连接中......");
curatorFramework.start();
}
public void createIfNeed(String path) throws Exception {
Stat stat = curatorFramework.checkExists().forPath(path);
if (stat==null){
String s = curatorFramework.create().forPath(path);
System.out.println("path " + s + " created! ");
}
}
public static CuratorFramework getCuratorFramework() {
return curatorFramework;
}
@After
public void test(){
try {
TimeUnit.SECONDS.sleep(Integer.MAX_VALUE);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
protected String getConnectStr(){
return CONNECT_STR;
}
}
2、创建节点
2.1、创建递归节点
Curator内部封装会给我们循环的创建传入路径中的节点。
@Test
public void testCreateWithParent() throws Exception {
CuratorFramework curatorFramework = getCuratorFramework();
String pathWithParent = "/node-parent/sub-node-1";
String path = curatorFramework.create().creatingParentsIfNeeded().forPath(pathWithParent);
System.out.println("curator create node :" + path + " successfully.");
}
2.2、一般情况创建节点
@Test
public void testCreate() throws Exception {
CuratorFramework curatorFramework = getCuratorFramework();
String forPath = curatorFramework
.create()
// protection 模式,防止由于异常原因,导致僵尸节点
// 在创建节点的时候会去设置一个 UUID.randomUUID().toString() 序号,唯一的
// 防止在客户端出现网络问题的时候导致节点的重复创建
.withProtection()
.withMode(CreateMode.EPHEMERAL_SEQUENTIAL).
forPath("/curator-node", "some-data".getBytes());
System.out.println("curator create node :" + forPath + " successfully.");
}
这里有一个小的知识点,就是代码中的 .withProtection(),在这个方法中Curator会给节点添加一个UUID,目的是为了防止僵尸节点,什么意思呢,就是当我们从客户端发了一个请求去创建节点,当服务端刚收到了请求然后网络波动了,这个时候客户端就接收不到这个节点被创建成功的信息了,如果是普通节点还好,不能重复创建,但是如果是临时节点,被创建成功了,但是客户端不知道,那么这个节点就会永远存在于服务端,不会被清理掉,这就叫做僵尸节点。点进代码之后就可以看到如下所示:
void resetProtectedId() {
this.protectedId = UUID.randomUUID().toString();
}
运行截图如下,发现会自动添上一个 UUID。
3、获取节点数据
@Test
public void testGetData() throws Exception {
CuratorFramework curatorFramework = getCuratorFramework();
byte[] bytes = curatorFramework.getData().forPath("/test");
System.out.println("get data from node :" + new String(bytes) + " successfully.");
}
4、修改节点的数据
@Test
public void testSetData() throws Exception {
CuratorFramework curatorFramework = getCuratorFramework();
curatorFramework.setData().forPath("/test", "changed!".getBytes());
byte[] bytes = curatorFramework.getData().forPath("/test");
System.out.println("get data from node /test :" + new String(bytes) + " successfully.");
}
5、删除节点
@Test
public void testDelete() throws Exception {
CuratorFramework curatorFramework = getCuratorFramework();
String pathWithParent = "/test";
curatorFramework.delete().guaranteed().deletingChildrenIfNeeded().forPath(pathWithParent);
}
6、获取该路径下所有的节点
@Test
public void testListChildren() throws Exception {
CuratorFramework curatorFramework = getCuratorFramework();
String pathWithParent = "/";
List<String> strings = curatorFramework.getChildren().forPath(pathWithParent);
strings.forEach(System.out::println);
}
运行截图如下:
7、自定义线程池去异步获取数据
在我们原装的连接zk的包里,如果异步获取数据的时候是用的最开始初始化客户端的时候所创建的 EventThread 线程,如果异步的命令很多,或者数据量很大,就会阻塞接收服务端的响应,所以 Curator 给我们提供了可以自定义线程池去执行这些异步操作。
@Test
public void testThreadPool() throws Exception {
CuratorFramework curatorFramework = getCuratorFramework();
ExecutorService executorService = Executors.newSingleThreadExecutor();
String ZK_NODE="/test";
curatorFramework.getData().inBackground((client, event) -> {
System.out.println(" background: " + event);
System.out.println(" data: " + new String(event.getData()) );
},executorService).forPath(ZK_NODE);
}
运行截图如下: