前言:
閒來無事,想起把zookeeper的相關知識再補充一下。作爲中間件部門的成員,與zookeeper打交道的機會還是很多的。目前市面上的很多產品,尤其是分佈式相關的,基本都會用上zookeeper。
本文不是一篇介紹zookeeper是什麼和怎麼使用的文章,而是介紹zookeeper的使用框架Curator的。本來筆者還想着把Curator的使用再好好熟悉下,因爲一段時間不用就覺得生疏了,突然發現之前竟然對Curator的使用封裝過,好吧,那就發出來吧,以供大家參考、指正。
寫這篇博客的另一個動機就是爲了下一篇博客,下一篇會寫一下真正的大牛他們是怎麼封裝使用Curator,也算是拋磚引玉了。
準備工作:
老規矩,首先介紹下筆者的使用環境和相關依賴。
1.zookeeper服務,單機版,本地使用,版本爲3.4.11
2.maven依賴如下:
<!-- zookeeper -->
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.4.11</version>
<exclusions>
<exclusion>
<artifactId>log4j</artifactId>
<groupId>log4j</groupId>
</exclusion>
</exclusions>
</dependency>
<!-- curator -->
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-framework</artifactId>
<version>4.0.0</version>
<exclusions>
<exclusion>
<artifactId>zookeeper</artifactId>
<groupId>org.apache.zookeeper</groupId>
</exclusion>
</exclusions>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.curator/curator-recipes -->
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
<version>4.0.0</version>
</dependency>
1.Curator封裝使用
筆者就寫了一個單類,裏面封裝CRUD的操作方法,具體如下:
package com.example.demo.curator;
import org.apache.curator.RetryPolicy;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.framework.api.ACLBackgroundPathAndBytesable;
import org.apache.curator.framework.api.BackgroundCallback;
import org.apache.curator.framework.recipes.cache.*;
import org.apache.curator.retry.ExponentialBackoffRetry;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.data.Stat;
import java.util.concurrent.Executor;
/**
* Curator的簡單封裝
* @author lucky
*/
public class CuratorManager {
/** 默認值 */
private static final String DEFAULT_CONNECT_STRING = "localhost:2181";
private static final int DEFAULT_SESSION_TIME_OUT = 5000;
private static final int DEFAULT_CONNECTION_TIME_OUT = 3000;
private static final RetryPolicy DEFAULT_RETRY_POLICY
= new ExponentialBackoffRetry(1000, 3);
/** zookeeper服務器地址 */
private String connectAddr;
/** session超時時間 */
private int sessionTimeOut;
/** 連接超時時間 */
private int connectionTimeOut;
/** 重試策略 */
private RetryPolicy policy = null;
private CuratorFramework client = null;
/** 構造方法 */
public CuratorManager() {
this(DEFAULT_CONNECT_STRING,DEFAULT_SESSION_TIME_OUT,DEFAULT_CONNECTION_TIME_OUT,DEFAULT_RETRY_POLICY);
}
public CuratorManager(String connectAddr) {
this(connectAddr,DEFAULT_SESSION_TIME_OUT,DEFAULT_CONNECTION_TIME_OUT,DEFAULT_RETRY_POLICY);
}
public CuratorManager(String connectAddr, Integer sessionTimeOut, Integer connectionTimeOut, RetryPolicy policy) {
this.connectAddr = connectAddr == null ? DEFAULT_CONNECT_STRING : connectAddr;
this.sessionTimeOut = sessionTimeOut == null ? DEFAULT_SESSION_TIME_OUT : sessionTimeOut;
this.connectionTimeOut = connectionTimeOut == null ? DEFAULT_CONNECTION_TIME_OUT : connectionTimeOut;
this.policy = policy == null ? DEFAULT_RETRY_POLICY : policy;
this.client = CuratorFrameworkFactory.newClient(connectAddr,sessionTimeOut,connectionTimeOut,policy);
client.start();
}
/** create */
/**
* 只創建路徑,data默認爲空
* @param path 路徑
* @throws Exception
*/
public void create(String path) throws Exception{
this.createWithData(path,"");
}
/**
* 創建路徑和對應data值
* @param path 路徑
* @param data 值
* @throws Exception
*/
public void createWithData(String path, String data) throws Exception{
this.createWithMode(path,data,CreateMode.PERSISTENT);
}
/**
* 創建路徑和對應data值,並指定創建模式
* @param path 路徑
* @param data 值
* @param mode 創建模式
* @throws Exception
*/
public void createWithMode(String path, String data, CreateMode mode) throws Exception{
client.create().creatingParentsIfNeeded().withMode(mode).forPath(path, data.getBytes());
}
/**
* 創建路徑和對應data值,指定創建模式,並創建callback
* @param path 路徑
* @param data 值
* @param mode 創建模式
* @param callback 響應事件
* @param es 使用線程池來執行響應事件
* @throws Exception
*/
public void createWithBackGround(String path, String data, CreateMode mode, BackgroundCallback callback, Executor es) throws Exception {
ACLBackgroundPathAndBytesable<String> pathAndBytesable = client.create().creatingParentsIfNeeded().withMode(mode);
if (null != callback) {
if (null != es) {
pathAndBytesable.inBackground(callback, es);
} else {
pathAndBytesable.inBackground(callback);
}
}
pathAndBytesable.forPath(path, data.getBytes());
}
/** delete */
/**
* 刪除該路徑,包括子節點
* @param path 路徑
* @throws Exception
*/
public void delete(String path) throws Exception {
this.deleteWithVersion(path,-1);
}
/**
* 刪除該路徑指定version,包括子節點
* @param path 路徑
* @param version 版本
* @throws Exception
*/
public void deleteWithVersion(String path, int version) throws Exception {
this.deleteWithAllChildren(path, version, false);
}
/**
* 可自定義是否刪除該path下子節點
* @param path 路徑
* @param version 版本
* @param deleteChildren 是否刪除子節點
* @throws Exception
*/
public void deleteWithAllChildren(String path, int version, boolean deleteChildren) throws Exception {
if (deleteChildren) {
client.delete().guaranteed().deletingChildrenIfNeeded().withVersion(version).forPath(path);
} else {
client.delete().guaranteed().withVersion(version).forPath(path);
}
}
/** get */
/**
* 獲取路徑對應值
* @param path 路徑
* @return 值
* @throws Exception
*/
public String get(String path) throws Exception {
byte[] bytes = client.getData().forPath(path);
return new String(bytes);
}
/**
* 獲取路徑stat信息
* @param path 路徑
* @return stat信息
* @throws Exception
*/
public Stat getWithStat(String path) throws Exception {
Stat stat = new Stat();
client.getData().storingStatIn(stat).forPath(path);
return stat;
}
/** update */
/**
* 修改path對應data值
* @param path 路徑
* @param data 值
* @throws Exception
*/
public void update(String path, String data) throws Exception {
this.updateWithVersion(path ,data, -1);
}
/**
* 修改path對應version的data值
* @param path 路徑
* @param data 值
* @param version 版本
* @throws Exception
*/
public void updateWithVersion(String path, String data, int version) throws Exception {
client.setData().withVersion(version).forPath(path, data.getBytes());
}
/**
* 添加對應path的監聽,當該path的data值變動時,則觸發callback
* @param path 路徑
* @param callback 回調方法
* @param es 使用線程池來執行響應事件
* @throws Exception
*/
public void dataListener(String path, NodeCacheCallback callback, Executor es) throws Exception {
NodeCache nodeCache = new NodeCache(client, path, false);
nodeCache.start(true);
NodeCacheListener listener = new NodeCacheListener() {
@Override
public void nodeChanged() throws Exception {
callback.callback(nodeCache);
}
};
if (null != es) {
nodeCache.getListenable().addListener(listener, es);
} else {
nodeCache.getListenable().addListener(listener);
}
}
/**
* 添加對應path的監聽,當該path下的子節點發生變動時,則觸發callback
* 注意:二級子節點時間無法監聽
* @param path 路徑
* @param callback 回調方法
* @param es 使用線程池來執行響應事件
* @throws Exception
*/
public void childrenListener(String path, NodeChildrenCacheCallback callback, Executor es) throws Exception {
PathChildrenCache pathChildrenCache = new PathChildrenCache(client, path, true);
pathChildrenCache.start(PathChildrenCache.StartMode.POST_INITIALIZED_EVENT);
PathChildrenCacheListener pathChildrenCacheListener = new PathChildrenCacheListener(){
@Override
public void childEvent(CuratorFramework client, PathChildrenCacheEvent event) throws Exception {
callback.callback(pathChildrenCache, client, event);
}
};
if (null != es) {
pathChildrenCache.getListenable().addListener(pathChildrenCacheListener, es);
} else {
pathChildrenCache.getListenable().addListener(pathChildrenCacheListener);
}
}
/**
* 自定義data值變化監聽接口
*/
interface NodeCacheCallback{
void callback(NodeCache cache);
}
/**
* 自定義子節點變動監聽接口
*/
interface NodeChildrenCacheCallback{
void callback(PathChildrenCache cache, CuratorFramework client, PathChildrenCacheEvent event);
}
public static void main(String[] args) {
CuratorManager curatorManager = new CuratorManager();
String path = "/xw/test/create";
String data = "just for test";
try {
// create
curatorManager.createWithData(path ,data);
// update
curatorManager.update(path,"test just");
// get state
Stat withStat = curatorManager.getWithStat(path);
System.out.println(withStat);
// get data
String s = curatorManager.get(path);
System.out.println(s);
// delete
curatorManager.delete(path);
curatorManager.deleteWithAllChildren("/xw",-1,true);
// data change listener
curatorManager.dataListener(path, new NodeCacheCallback() {
@Override
public void callback(NodeCache cache) {
System.out.println("node data update, current data: " + cache.getCurrentData());
}
}, null);
// children change listener
curatorManager.childrenListener(path, new NodeChildrenCacheCallback() {
@Override
public void callback(PathChildrenCache cache, CuratorFramework client, PathChildrenCacheEvent event) {
switch (event.getType()) {
case CHILD_ADDED:
System.out.println("child_add, data:" + event.getData());
case CHILD_UPDATED:
System.out.println("child_update, data:" + event.getData());
case CHILD_REMOVED:
System.out.println("child_removed, data:" + event.getData());
default:
break;
}
}
}, null);
} catch (Exception e) {
e.printStackTrace();
}
}
}
總結:
簡單的封裝使用Curator,看了越來越多的源碼之後,就會發現,寫代碼的時候不自覺的會想往這方面去靠。
會去想着寫更簡潔的、封裝性更好的代碼。
同時設計模式相關的也會不自覺的冒出來。
共勉之!