Zookeeper-开源客户端

ZkClient

ZkClient是Github上⼀个开源的zookeeper客户端,在Zookeeper原⽣API接⼝之上进⾏了包装,是⼀个更易⽤的Zookeeper客户端,同时,zkClient在内部还实现了诸如Session超时重连、Watcher反复注册等功能。
接下来,还是从创建会话、创建节点、读取数据、更新数据、删除节点等⽅⾯来介绍如何使⽤zkClient这个zookeeper客户端。

添加依赖:

在pom.xml⽂件中添加如下内容

<dependency>
 <groupId>com.101tec</groupId>
 <artifactId>zkclient</artifactId>
 <version>0.2</version>
</dependency>

创建会话:

使⽤ZkClient可以轻松的创建会话,连接到服务端。

package com.hust.grid.leesf.zkclient.examples;
import java.io.IOException;
import org.I0Itec.zkclient.ZkClient;
public class CreateSession {
 /*
 创建⼀个zkClient实例来进⾏连接
 注意:zkClient通过对zookeeperAPI内部包装,将这个异步的会话创建过程同步化了
 */
 public static void main(String[] args) {
 ZkClient zkClient = new ZkClient("127.0.0.1:2181");
 System.out.println("ZooKeeper session established.");
 }
}

运⾏结果:ZooKeeper session established.
结果表明已经成功创建会话。

创建节点

ZkClient提供了递归创建节点的接⼝,即其帮助开发者先完成⽗节点的创建,再创建⼦节点。

package com.hust.grid.leesf.zkclient.examples;
import org.I0Itec.zkclient.ZkClient;
public class Create_Node_Sample {
 public static void main(String[] args) {
 ZkClient zkClient = new ZkClient("127.0.0.1:2181");
 System.out.println("ZooKeeper session established.");
 //createParents的值设置为true,可以递归创建节点
 zkClient.createPersistent("/lg-zkClient/lg-c1",true);
 System.out.println("success create znode.");
 }
}

运⾏结果:success create znode.
结果表明已经成功创建了节点,值得注意的是,在原⽣态接⼝中是⽆法创建成功的(⽗节点不存在),但是通过ZkClient通过设置createParents参数为true可以递归的先创建⽗节点,再创建⼦节点。

删除节点

ZkClient提供了递归删除节点的接⼝,即其帮助开发者先删除所有⼦节点(存在),再删除⽗节点。

package com.hust.grid.leesf.zkclient.examples;
import org.I0Itec.zkclient.ZkClient;
public class Del_Data_Sample {
 public static void main(String[] args) throws Exception {
 String path = "/lg-zkClient/lg-c1";
 ZkClient zkClient = new ZkClient("127.0.0.1:2181", 5000);
 zkClient.deleteRecursive(path);
 System.out.println("success delete znode.");
 }
}

运⾏结果: success delete znode.
结果表明ZkClient可直接删除带⼦节点的⽗节点,因为其底层先删除其所有⼦节点,然后再删除⽗节点。

获取子节点

package com.hust.grid.leesf.zkclient.examples;
import java.util.List;
import org.I0Itec.zkclient.IZkChildListener;
import org.I0Itec.zkclient.ZkClient;
public class Get_Children_Sample {
 public static void main(String[] args) throws Exception {
 ZkClient zkClient = new ZkClient("127.0.0.1:2181", 5000);
 List<String> children = zkClient.getChildren("/lg-zkClient");
 System.out.println(children);
 
 //注册监听事件
 zkClient.subscribeChildChanges(path, new IZkChildListener() {
 public void handleChildChange(String parentPath, List<String>
currentChilds) throws Exception {
 System.out.println(parentPath + " 's child changed,
currentChilds:" + currentChilds);
 }
 });
 zkClient.createPersistent("/lg-zkClient");
 Thread.sleep(1000);
 zkClient.createPersistent("/lg-zkClient/c1");
 Thread.sleep(1000);
 zkClient.delete("/lg-zkClient/c1");
 Thread.sleep(1000);
 zkClient.delete(path);
 Thread.sleep(Integer.MAX_VALUE);
 }
}

运⾏结果:

/zk-book 's child changed, currentChilds:[]
/zk-book 's child changed, currentChilds:[c1]
/zk-book 's child changed, currentChilds:[]
/zk-book 's child changed, currentChilds:null

结果表明:
客户端可以对⼀个不存在的节点进⾏⼦节点变更的监听。⼀旦客户端对⼀个节点注册了⼦节点列表变更监听之后,那么当该节点的⼦节点列表发⽣变更时,服务端都会通知客户端,并将最新的⼦节点列表发送给客户端。该节点本身的创建或删除也会通知到客户端。

获取数据(节点是否存在、更新、删除)

public class Get_Data_Sample {
 public static void main(String[] args) throws InterruptedException {
 String path = "/lg-zkClient-Ep";
 ZkClient zkClient = new ZkClient("127.0.0.1:2181");
 //判断节点是否存在
 boolean exists = zkClient.exists(path);
 if (!exists){
 zkClient.createEphemeral(path, "123");
 }
 
 //注册监听
 zkClient.subscribeDataChanges(path, new IZkDataListener() {
 public void handleDataChange(String path, Object data) throws
Exception {
 System.out.println(path+"该节点内容被更新,更新后的内容"+data);
 }
 public void handleDataDeleted(String s) throws Exception {
 System.out.println(s+" 该节点被删除");
 }
 });
 //获取节点内容
 Object o = zkClient.readData(path);
 System.out.println(o);
 //更新
 zkClient.writeData(path,"4567");
 Thread.sleep(1000);
 //删除
 zkClient.delete(path);
 Thread.sleep(1000);
 }
}

运⾏结果:

123
/lg-zkClient-Ep该节点内容被更新,更新后的内容4567
/lg-zkClient-Ep 该节点被删除

结果表明可以成功监听节点数据变化或删除事件。

Curator客户端

curator是Netflix公司开源的⼀套Zookeeper客户端框架,和ZKClient⼀样,Curator解决了很多Zookeeper客户端⾮常底层的细节开发⼯作,包括连接重连,反复注册Watcher和NodeExistsException异常等,是最流⾏的Zookeeper客户端之⼀。从编码⻛格上来讲,它提供了基于Fluent的编程⻛格⽀持。

添加依赖

<dependency>
 <groupId>org.apache.curator</groupId>
 <artifactId>curator-framework</artifactId>
 <version>2.12.0</version>
 </dependency>

创建会话

Curator的创建会话⽅式与原⽣的API和ZkClient的创建⽅式区别很⼤。Curator创建客户端是通过CuratorFrameworkFactory⼯⼚类来实现的。具体如下:

1.使⽤CuratorFramework这个⼯⼚类的两个静态⽅法来创建⼀个客户端

public static CuratorFramework newClient(String connectString, RetryPolicy
retryPolicy)
public static CuratorFramework newClient(String connectString, int
sessionTimeoutMs, int connectionTimeoutMs, RetryPolicy retryPolicy)

其中参数RetryPolicy提供重试策略的接⼝,可以让⽤户实现⾃定义的重试策略,默认提供了以下实现,分别为ExponentialBackoffRetry(基于backoff的重连策略)、RetryNTimes(重连N次策略)、RetryForever(永远重试策略)。

2.通过调⽤CuratorFramework中的start()⽅法来启动会话

RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000,3);
CuratorFramework client =
CuratorFrameworkFactory.newClient("127.0.0.1:2181",retryPolicy);
client.start();
RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000,3);
CuratorFramework client = CuratorFrameworkFactory.newClient("127.0.0.1:2181",
 5000,1000,retryPolicy);
client.start();

其实进⼀步查看源代码可以得知,其实这两种⽅法内部实现⼀样,只是对外包装成不同的⽅法。它们的底层都是通过第三个⽅法builder来实现的。

RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000,3);
private static CuratorFramework Client = CuratorFrameworkFactory.builder()
 .connectString("server1:2181,server2:2181,server3:2181")
 .sessionTimeoutMs(50000)
 .connectionTimeoutMs(30000)
 .retryPolicy(retryPolicy)
 .build();
client.start();

参数:
connectString:zk的server地址,多个server之间使⽤英⽂逗号分隔开
connectionTimeoutMs:连接超时时间,如上是30s,默认是15s
sessionTimeoutMs:会话超时时间,如上是50s,默认是60s
retryPolicy:失败重试策略
ExponentialBackoffRetry:构造器含有三个参数 ExponentialBackoffRetry(int
baseSleepTimeMs, int maxRetries, int maxSleepMs)
baseSleepTimeMs:初始的sleep时间,⽤于计算之后的每次重试的sleep时间,计算公式:当前sleep时间=baseSleepTimeMs*Math.max(1,random.nextInt(1<<(retryCount+1)))
maxRetries:最⼤重试次数
maxSleepMs:最⼤sleep时间,如果上述的当前sleep计算出来⽐这个⼤,那么sleep⽤这个时间,默认的最⼤时间是Integer.MAX_VALUE毫秒。其他,查看org.apache.curator.RetryPolicy接⼝的实现类。
start():完成会话的创建

package com.hust.grid.leesf.curator.examples;
import org.apache.curator.RetryPolicy;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.retry.ExponentialBackoffRetry;
public class Create_Session_Sample {
 public static void main(String[] args) throws Exception {
 RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 3);
 CuratorFramework client =
CuratorFrameworkFactory.newClient("127.0.0.1:2181", 5000, 3000, retryPolicy);
 client.start();
 System.out.println("Zookeeper session1 established. ");
 CuratorFramework client1 = CuratorFrameworkFactory.builder()
 .connectString("127.0.0.1:2181") //server地址
 .sessionTimeoutMs(5000) // 会话超时时间
 .connectionTimeoutMs(3000) // 连接超时时间
 .retryPolicy(retryPolicy) // 重试策略
 .namespace("base") // ᇿ⽴命名空间/base
 .build(); //
 client1.start();
 System.out.println("Zookeeper session2 established. ");
 }
 }

运⾏结果:Zookeeper session1 established. Zookeeper session2 established需要注意的是session2会话含有隔离命名空间,即客户端对Zookeeper上数据节点的任何操作都是相对/base⽬录进⾏的,这有利于实现不同的Zookeeper的业务之间的隔离。

创建节点

curator提供了⼀系列Fluent⻛格的接⼝,通过使⽤Fluent编程⻛格的接⼝,开发⼈员可以进⾏⾃由组合来完成各种类型节点的创建。下⾯简单介绍⼀下常⽤的⼏个节点创建场景。
(1)创建⼀个初始内容为空的节点

client.create().forPath(path);

Curator默认创建的是持久节点,内容为空。
(2)创建⼀个包含内容的节点

client.create().forPath(path,"我是内容".getBytes());

Curator和ZkClient不同的是依旧采⽤Zookeeper原⽣API的⻛格,内容使⽤byte[]作为⽅法参数。
(3)递归创建⽗节点,并选择节点类型

client.create().creatingParentsIfNeeded().withMode(CreateMode.EPHEMERAL).forPath(path);

creatingParentsIfNeeded这个接⼝⾮常有⽤,在使⽤ZooKeeper 的过程中,开发⼈员经常会碰到NoNodeException 异常,其中⼀个可能的原因就是试图对⼀个不存在的⽗节点创建⼦节点。因此,开发⼈员不得不在每次创建节点之前,都判断⼀下该⽗节点是否存在——这个处理通常⽐较麻烦。在使⽤Curator 之后,通过调⽤creatingParentsIfNeeded 接⼝,Curator 就能够⾃动地递归创建所有需要的⽗节点。

判断节点是否存在

checkExists方法返回Stat,如果返回为Null,我们就可以认为是该节点不存在。

		Stat stat = client.checkExists().forPath(path);
            if (stat == null)
            {
                return false;
			}
			else
			{
			return true;
			}

删除节点

删除节点的⽅法也是基于Fluent⽅式来进⾏操作,不同类型的操作调⽤ 新增不同的⽅法调⽤即可。
(1)删除⼀个⼦节点

client.delete().forPath(path);

(2)删除节点并递归删除其⼦节点

client.delete().deletingChildrenIfNeeded().forPath(path);

(3)指定版本进⾏删除

client.delete().withVersion(1).forPath(path);

如果此版本已经不存在,则删除异常,异常信息如下。

org.apache.zookeeper.KeeperException$BadVersionException: KeeperErrorCode =
BadVersion for

(4)强制保证删除⼀个节点

client.delete().guaranteed().forPath(path);

只要客户端会话有效,那么Curator会在后台持续进⾏删除操作,直到节点删除成功。⽐如遇到⼀些⽹
络异常的情况,此guaranteed的强制删除就会很有效果。

获取数据

获取节点数据内容API相当简单,同时Curator提供了传⼊⼀个Stat变量的⽅式来存储服务器端返回的最
新的节点状态信息

// 普通查询
client.getData().forPath(path);
// 包含状态查询
Stat stat = new Stat();
client.getData().storingStatIn(stat).forPath(path);

获取子节点

  List<String> children=client.getChildren().forPath(path)

更新数据

更新数据,如果未传⼊version参数,那么更新当前最新版本,如果传⼊version则更新指定version,如
果version已经变更,则抛出异常。

// 普通更新
client.setData().forPath(path,"新内容".getBytes());
// 指定版本更新
client.setData().withVersion(1).forPath(path);

版本不⼀致异常信息:

org.apache.zookeeper.KeeperException$BadVersionException: KeeperErrorCode =
BadVersion for
package com.hust.grid.leesf.curator.examples;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.retry.ExponentialBackoffRetry;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.data.Stat;
public class Set_Node_Sample {
 public static void main(String[] args) throws Exception {
 CuratorFramework client = CuratorFrameworkFactory.builder()
 .connectString("127.0.0.1:2181") //server地址
 .sessionTimeoutMs(5000) // 会话超时时间
 .connectionTimeoutMs(3000) // 连接超时时间
 .retryPolicy(new ExponentialBackoffRetry(1000,5)) //
重试策略
 .build(); //
 client.start();
 System.out.println("Zookeeper session established. ");
 String path = "/lg-curator/c1";
 //获取节点数据
 Stat stat = new Stat();
 byte[] bytes = client.getData().storingStatIn(stat).forPath(path);
 System.out.println(new String(bytes));
 //更新节点数据
 int version =
client.setData().withVersion(stat.getVersion()).forPath(path).getVersion();
 System.out.println("Success set node for : " + path + ", new
version: "+version);
 
client.setData().withVersion(stat.getVersion()).forPath(path).getVersion();
 }
 }

运⾏结果:

Zookeeper session established.
init
Success set node for : /lg-curator/c1, new version: 1
Exception in thread "main"
org.apache.zookeeper.KeeperException$BadVersionException: KeeperErrorCode =
BadVersion for /lg-curator/c1

结果表明当携带数据版本不⼀致时,⽆法完成更新操作。

Curator 事件监听

Curator 事件有两种模式,一种是标准的观察模式,一种是缓存监听模式。标准的监听模式是使用Watcher 监听器。第二种缓存监听模式引入了一种本地缓存视图的Cache机制,来实现对Zookeeper服务端事件监听。

Cache事件监听可以理解为一个本地缓存视图与远程Zookeeper视图的对比过程。Cache提供了反复注册的功能。Cache是一种缓存机制,可以借助Cache实现监听。简单来说,Cache在客户端缓存了znode的各种状态,当感知到zk集群的znode状态变化,会触发event事件,注册的监听器会处理这些事件。

Watcher 监听器比较简单,只有一种。Cache事件监听的种类有3种Path Cache,Node Cache,Tree Cache。

Path Cache

Path Cache用来观察ZNode的子节点并缓存状态,如果ZNode的子节点被创建,更新或者删除,那么Path Cache会更新缓存,并且触发事件给注册的监听器。

Path Cache是通过PathChildrenCache类来实现的,监听器注册是通过PathChildrenCacheListener。

Node Cache

Node Cache用来观察ZNode自身,如果ZNode节点本身被创建,更新或者删除,那么Node Cache会更新缓存,并触发事件给注册的监听器。

Node Cache是通过NodeCache类来实现的,监听器对应的接口为NodeCacheListener。

Tree Cache

可以看做是上两种的合体,Tree Cache观察的是ZNode及子节点。

Path Cache Demo
 		//创建PathChildrenCache
        //参数:true代表缓存数据到本地
        PathChildrenCache pathChildrenCache = new PathChildrenCache(client,path,true);
        //BUILD_INITIAL_CACHE 代表使用同步的方式进行缓存初始化。
        pathChildrenCache.start(PathChildrenCache.StartMode.BUILD_INITIAL_CACHE);
        pathChildrenCache.getListenable().addListener((cf, event) -> {
            PathChildrenCacheEvent.Type eventType = event.getType();
            //获取子路径
            String childPath=event.getData().getPath();
            switch (eventType) {
                case CONNECTION_RECONNECTED:
                    pathChildrenCache.rebuild();
                    break;
                case CONNECTION_SUSPENDED:
                    break;
                case CONNECTION_LOST:
                    System.out.println("Connection lost");
                    break;
                case CHILD_ADDED:
                    System.out.println("Child ADDED");
                    break;
                case CHILD_UPDATED:
                    System.out.println("Child updated");
                    break;
                case CHILD_REMOVED:         
                    System.out.println("Child REMOVED");             
                    break;
                default:
            }
        });
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值