- zkclient
- curator
- 二者的基本使用
- 使用zkclient:
- 使用Curator单结点操作
- 使用curator集群操作
zkclient
ZkClient是一个开源客户端,在ZooKeeper原生API接口的基础上进行了包装,更便于开发人员使用。ZkClient客户端在一些著名的互联网开源项目中得到了应用,例如,阿里的分布式Dubbo框架对它进行了无缝集成。 ZkClient解决了ZooKeeper原生API接口的很多问题。例如,ZkClient提供了更加简洁的API,实现了会话超时重连、反复注册Watcher等问题。虽然ZkClient对原生API进行了封装,但也有它自身的不足之处,具体如下:
·ZkClient社区不活跃,文档不够完善,几乎没有参考文档。
·异常处理简化(抛出RuntimeException)。
·重试机制比较难用。
·没有提供各种使用场景的参考实现。
curator
Curator是Netflix公司开源的一套ZooKeeper客户端框架,和ZkClient一样它解决了非常底层的细节开发工作,包括连接、重连、反复注册Watcher的问题以及NodeExistsException异常等。 Curator是Apache基金会的顶级项目之一,Curator具有更加完善的文档,另外还提供了一套易用性和可读性更强的Fluent风格的客户端API框架。 Curator还为ZooKeeper客户端框架提供了一些比较普遍的、开箱即用的、分布式开发用的解决方案,例如Recipe、共享锁服务、Master选举机制和分布式计算器等,帮助开发者避免了“重复造轮子”的无效开发工作。 另外,Curator还提供了一套非常优雅的链式调用API,与ZkClient客户端API相比,Curator的API优雅太多了
二者的基本使用
使用zkclient:
使用zklient只需要引入一个jar包:
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.5.8</version>
</dependency>
创建客户端实例:直接在初始化方法中创建zookeeper实例
@Slf4j
public abstract class StandaloneBase {
private static final String CONNECT_STR="192.168.109.200:2181";
private static final int SESSION_TIMEOUT=30 * 1000;
private static ZooKeeper zooKeeper =null;
private static CountDownLatch countDownLatch = new CountDownLatch(1);
private Watcher watcher =new Watcher() {
@Override
public void process(WatchedEvent event) {
if (event.getState() == Event.KeeperState.SyncConnected
&& event.getType()== Event.EventType.None){
countDownLatch.countDown();
log.info("连接建立");
}
}
};
@Before
public void init(){
try {
log.info(" start to connect to zookeeper server: {}",getConnectStr());
zooKeeper=new ZooKeeper(getConnectStr(), getSessionTimeout(), watcher);
log.info(" 连接中...");
countDownLatch.await();
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static ZooKeeper getZooKeeper() {
return zooKeeper;
}
@After
public void test(){
try {
TimeUnit.SECONDS.sleep(Integer.MAX_VALUE);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
protected String getConnectStr(){
return CONNECT_STR;
}
protected int getSessionTimeout() {
return SESSION_TIMEOUT;
}
}
使用zkclient的基本操作:
@Slf4j
public class BaseOperations extends StandaloneBase{
private String first_node = "/first-node";
@Test
public void testCreate() throws KeeperException, InterruptedException {
ZooKeeper zooKeeper = getZooKeeper();
String s = zooKeeper.create(first_node, "first".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL);
log.info("Create:{}",s);
}
@Test
public void testGetData(){
Watcher watcher = new Watcher() {
@Override
public void process(WatchedEvent event) {
if (event.getPath()!=null && event.getPath().equals(first_node)
&& event.getType()== Event.EventType.NodeDataChanged){
log.info(" PATH: {} 发现变化",first_node);
try {
byte[] data = getZooKeeper().getData(first_node, this, null);
log.info(" data: {}",new String(data));
} catch (KeeperException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
try {
byte[] data = getZooKeeper().getData(first_node, watcher, null); //
log.info(" data: {}",new String(data));
} catch (KeeperException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Test
public void testSetData() throws KeeperException, InterruptedException {
ZooKeeper zooKeeper = getZooKeeper();
Stat stat = new Stat();
byte[] data = zooKeeper.getData(first_node, false, stat);
// int version = stat.getVersion();
zooKeeper.setData(first_node, "third".getBytes(), 0);
}
@Test
public void testDelete() throws KeeperException, InterruptedException {
// -1 代表匹配所有版本,直接删除
// 任意大于 -1 的代表可以指定数据版本删除
getZooKeeper().delete("/config",-1);
}
@Test
public void asyncTest(){
String userId="xxx";
getZooKeeper().getData("/test", false, (rc, path, ctx, data, stat) -> {
Thread thread = Thread.currentThread();
log.info(" Thread Name: {}, rc:{}, path:{}, ctx:{}, data:{}, stat:{}",thread.getName(),rc, path, ctx, data, stat);
},"test");
log.info(" over .");
}
}
使用Curator单结点操作
先要引入多个jar包:
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-client</artifactId>
<version>4.0.0</version>
<exclusions>
<exclusion>
<groupId>org.apache.ZooKeeper</groupId>
<artifactId>ZooKeeper</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-framework</artifactId>
<version>4.0.0</version>
<exclusions>
<exclusion>
<groupId>org.apache.ZooKeeper</groupId>
<artifactId>ZooKeeper</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
<version>4.0.0</version>
<exclusions>
<exclusion>
<groupId>org.apache.ZooKeeper</groupId>
<artifactId>ZooKeeper</artifactId>
</exclusion>
</exclusions>
</dependency>
如果Curator与ZooKeeper的版本不是相互匹配的,就会有兼容性问题,很有可能导致节点操作的失败。如何确保Curator与ZooKeeper的具体版本是匹配的呢?可以去Curator的官网查看。
创建会话:
要进行客户端服务器交互,第一步就要创建会话
Curator 提供了多种方式创建会话,比如用静态工厂方式创建:
// 重试策略
RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 3)
CuratorFramework client = CuratorFrameworkFactory.newClient(zookeeperConnectionString, retryPolicy);
client.start();
或者使用 fluent 风格创建
RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 3);
CuratorFramework client = CuratorFrameworkFactory.builder()
.connectString("192.168.128.129:2181")
.sessionTimeoutMs(5000) // 会话超时时间
.connectionTimeoutMs(5000) // 连接超时时间
.retryPolicy(retryPolicy)
.namespace("base") // 包含隔离名称
.build();
client.start();
上面这段代码的编码风格采用了流式方式,
最核心的类是 CuratorFramework 类,该类的作用是定义一个 ZooKeeper 客户端对象,并在之后的上下文中使用。在定义 CuratorFramework 对象实例的时候,我们使用了 CuratorFrameworkFactory 工厂方法,并指定了 connectionString 服务器地址列表、retryPolicy 重试策略 、sessionTimeoutMs 会话超时时间、connectionTimeoutMs 会话创建超时时间。
关于重试策略:当客户端异常退出或者与服务端失去连接的时候,可以通过设置客户端重新连接 ZooKeeper 服务端。而 Curator 提供了 一次重试、多次重试等不同种类的实现方式。在 Curator 内部,可以通过判断服务器返回的 keeperException 的状态代码来判断是否进行重试处理,如果返回的是 OK 表示一切操作都没有问题,而 SYSTEMERROR 表示系统或服务端错误。
创建单节点基类:
@Slf4j
public abstract class CuratorStandaloneBase {
private static final String CONNECT_STR = "192.168.109.200: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) {
log.info("连接成功!");
}
});
log.info("连接中......");
curatorFramework.start();
}
public void createIfNeed(String path) throws Exception {
Stat stat = curatorFramework.checkExists().forPath(path);
if (stat==null){
String s = curatorFramework.create().forPath(path);
log.info("path {} created! ",s);
}
}
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;
}
}
curator单节点的基本操作:
@Slf4j
public class CuratorBaseOperations extends CuratorStandaloneBase {
// 递归创建子节点
@Test
public void testCreateWithParent() throws Exception {
CuratorFramework curatorFramework = getCuratorFramework();
String pathWithParent = "/node-parent/sub-node-1";
String path = curatorFramework.create().creatingParentsIfNeeded().forPath(pathWithParent);
log.info("curator create node :{} successfully.", path);
}
// protection 模式,防止由于异常原因,导致僵尸节点
@Test
public void testCreate() throws Exception {
CuratorFramework curatorFramework = getCuratorFramework();
String forPath = curatorFramework
.create()
.withProtection()
.withMode(CreateMode.EPHEMERAL_SEQUENTIAL).
forPath("/curator-node", "some-data".getBytes());
log.info("curator create node :{} successfully.", forPath);
}
@Test
public void testGetData() throws Exception {
CuratorFramework curatorFramework = getCuratorFramework();
byte[] bytes = curatorFramework.getData().forPath("/curator-node");
log.info("get data from node :{} successfully.", new String(bytes));
}
@Test
public void testSetData() throws Exception {
CuratorFramework curatorFramework = getCuratorFramework();
curatorFramework.setData().forPath("/curator-node", "changed!".getBytes());
byte[] bytes = curatorFramework.getData().forPath("/curator-node");
log.info("get data from node /curator-node :{} successfully.", new String(bytes));
}
@Test
public void testDelete() throws Exception {
CuratorFramework curatorFramework = getCuratorFramework();
String pathWithParent = "/node-parent";
curatorFramework.delete().guaranteed().deletingChildrenIfNeeded().forPath(pathWithParent);
}
@Test
public void testListChildren() throws Exception {
CuratorFramework curatorFramework = getCuratorFramework();
String pathWithParent = "/discovery/example";
List<String> strings = curatorFramework.getChildren().forPath(pathWithParent);
strings.forEach(System.out::println);
}
@Test
public void testThreadPool() throws Exception {
CuratorFramework curatorFramework = getCuratorFramework();
ExecutorService executorService = Executors.newSingleThreadExecutor();
String ZK_NODE="/zk-node";
curatorFramework.getData().inBackground((client, event) -> {
log.info(" background: {}", event);
},executorService).forPath(ZK_NODE);
}
}
使用curator集群操作
public class CuratorClusterBase extends CuratorStandaloneBase {
private final static String CLUSTER_CONNECT_STR="192.168.109.200:2181,192.168.109.200:2182,192.168.109.200:2183,192.168.109.200:2184";
public String getConnectStr() {
return CLUSTER_CONNECT_STR;
}
}
@Slf4j
public class CuratorClusterBaseOperations extends CuratorClusterBase {
@Test
public void testCluster() throws Exception {
CuratorFramework curatorFramework = getCuratorFramework();
String pathWithParent = "/test";
byte[] bytes = curatorFramework.getData().forPath(pathWithParent);
System.out.println(new String(bytes));
while (true) {
try {
byte[] bytes2 = curatorFramework.getData().forPath(pathWithParent);
System.out.println(new String(bytes2));
TimeUnit.SECONDS.sleep(5);
} catch (Exception e) {
e.printStackTrace();
testCluster();
}
}
}
public static void main(String[] args) throws Exception {
RetryPolicy retryPolicy=new ExponentialBackoffRetry( 5*1000, 10 );
String connectStr = "192.168.109.200:2181,192.168.109.200:2182,192.168.109.200:2183,192.168.109.200:2184";
CuratorFramework curatorFramework = CuratorFrameworkFactory.newClient(connectStr, retryPolicy);
curatorFramework.start();
String pathWithParent = "/test";
byte[] bytes = curatorFramework.getData().forPath(pathWithParent);
System.out.println(new String(bytes));
while (true) {
try {
byte[] bytes2 = curatorFramework.getData().forPath(pathWithParent);
System.out.println(new String(bytes2));
TimeUnit.SECONDS.sleep(5);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}