Curator解決了很多Zookeeper客戶端非常底層的細節開發工作,包括連接重連,反復注冊Watcher和NodeExistsException異常等。此外還有zkClient和Zooleeper自帶的Java API。
添加依賴:
在pom.xml文件中添加如下內容即可。
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
<version>2.8.0</version>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-framework</artifactId>
<version>2.8.0</version>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-client</artifactId>
<version>2.8.0</version>
</dependency>
創建會話:
Curator除了使用一般方法創建會話外,還可以使用fluent風格進行創建。
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.builder().connectString("127.0.0.1:2181")
.sessionTimeoutMs(5000).retryPolicy(retryPolicy).namespace("base").build();
client.start();
System.out.println("Zookeeper session established. ");
}
}
運行結果:
Zookeeper session1 established.
Zookeeper session2 established.
session會話含有隔離命名空間,即客戶端對Zookeeper上數據節點的任何操作都是相對/base目錄進行的,這有利于實現不同的Zookeeper的業務之間的隔離。當然也可以不設置。
創建節點:
通過使用Fluent風格的接口,開發人員可以進行自由組合來完成各種類型節點的創建。
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.retry.ExponentialBackoffRetry;
import org.apache.zookeeper.CreateMode;
public class Create_Node_Sample {
public static void main(String[] args) throws Exception {
String path = "/zk-book/c1";
CuratorFramework client = CuratorFrameworkFactory.builder()
.connectString("127.0.0.1:2181")
.sessionTimeoutMs(5000)
.retryPolicy(new ExponentialBackoffRetry(1000, 3))
.namespace("base")
.build();
client.start();
client.create().creatingParentsIfNeeded()
.withMode(CreateMode.EPHEMERAL)
.forPath(path, "i am c1".getBytes());
System.out.println("success create znode: " + path);
}
}
運行結果:
success create znode: /zk-book/c1
其中,也創建了/base/zk-book/c1的父節點/base/zk-book節點。
刪除節點:
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 Del_Data_Sample {
public static void main(String[] args) throws Exception {
String path = "/zk-book/c1";
CuratorFramework client = CuratorFrameworkFactory.builder()
.connectString("127.0.0.1:2181")
.sessionTimeoutMs(5000)
.retryPolicy(new ExponentialBackoffRetry(1000, 3))
.namespace("base")
.build();
client.start();
client.create().creatingParentsIfNeeded()
.withMode(CreateMode.EPHEMERAL)
.forPath(path, "i am c1".getBytes());
System.out.println("success create znode: " + path);
//以上,節點創建完成。
Stat stat = new Stat();
System.out.println(new String(client.getData().storingStatIn(stat).forPath(path)));
client.delete().deletingChildrenIfNeeded().withVersion(stat.getVersion()).forPath(path);
System.out.println("success delete znode " + path);
}
}
運行結果:
i am c1
success delete znode /zk-book/c1
獲取數據:
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 Get_Data_Sample {
public static void main(String[] args) throws Exception {
String path = "/zk-book";
CuratorFramework client = CuratorFrameworkFactory.builder().connectString("127.0.0.1:2181")
.sessionTimeoutMs(5000).retryPolicy(new ExponentialBackoffRetry(1000, 3)).build();
client.start();
client.create().creatingParentsIfNeeded().withMode(CreateMode.EPHEMERAL).forPath(path, "i am c1".getBytes());
Stat stat = new Stat();
byte b[] = client.getData().storingStatIn(stat).forPath(path);
System.out.println(new String(b));
}
}
運行結果:
i am c1
更新數據:
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_Data_Sample {
public static void main(String[] args) throws Exception {
String path = "/zk-book";
CuratorFramework client = CuratorFrameworkFactory.builder().connectString("127.0.0.1:2181")
.sessionTimeoutMs(5000).retryPolicy(new ExponentialBackoffRetry(1000, 3)).build();
client.start();
client.create().creatingParentsIfNeeded().withMode(CreateMode.EPHEMERAL).forPath(path, "i am c1".getBytes());
stat = client.setData().withVersion(stat.getVersion()).forPath(path);
System.out.println("Success set node for : " + path + ", new version: "+ stat.getVersion());
}
}
運行結果:
Success set node for : /zk-book, new version: 1
異步接口:
如同Zookeeper原生API提供了異步接口,Curator也提供了異步接口。在Zookeeper中,所有的異步通知事件處理都是由EventThread這個線程來處理的,EventThread線程用于串行處理所有的事件通知,其可以保證對事件處理的順序性,但是一旦碰上復雜的處理單元,會消耗過長的處理時間,從而影響其他事件的處理,Curator允許用戶傳入Executor實例,這樣可以將比較復雜的事件處理放到一個專門的線程池中去。
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
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.CuratorEvent;
import org.apache.curator.retry.ExponentialBackoffRetry;
import org.apache.zookeeper.CreateMode;
public class Create_Node_Background_Sample {
static String path = "/zk-book";
static CuratorFramework client = CuratorFrameworkFactory.builder().connectString("127.0.0.1:2181")
.sessionTimeoutMs(5000).retryPolicy(new ExponentialBackoffRetry(1000, 3)).build();
static CountDownLatch semaphore = new CountDownLatch(2);
static ExecutorService tp = Executors.newFixedThreadPool(2);
public static void main(String[] args) throws Exception {
client.start();
System.out.println("Main thread: " + Thread.currentThread().getName());
client.create().creatingParentsIfNeeded().withMode(CreateMode.EPHEMERAL)
.inBackground(
new BackgroundCallback(){
public void processResult(CuratorFramework client, CuratorEvent event) throws Exception {
System.out.println("event[code: " + event.getResultCode() + ", type: " + event.getType() + "]" + ", Thread of processResult: " + Thread.currentThread().getName());
semaphore.countDown();
}
}, tp
)
.forPath(path, "init".getBytes());
client.create().creatingParentsIfNeeded().withMode(CreateMode.EPHEMERAL)
.inBackground(
new BackgroundCallback(){
public void processResult(CuratorFramework client, CuratorEvent event) throws Exception {
System.out.println("event[code: " + event.getResultCode() + ", type: " + event.getType() + "]" + ", Thread of processResult: " + Thread.currentThread().getName());
semaphore.countDown();
}
}
)
.forPath(path, "init".getBytes());
semaphore.await();
tp.shutdown();
}
}
運行結果:
Main thread: main
event[code: -110, type: CREATE], Thread of processResult: main-EventThread
event[code: 0, type: CREATE], Thread of processResult: pool-3-thread-1
其中,創建節點的事件由線程池自己處理,而非默認線程處理。
節點監聽:
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.framework.recipes.cache.NodeCache;
import org.apache.curator.framework.recipes.cache.NodeCacheListener;
import org.apache.curator.retry.ExponentialBackoffRetry;
import org.apache.zookeeper.CreateMode;
public class NodeCache_Sample {
public static void main(String[] args) throws Exception {
String path = "/zk-book/nodecache";
CuratorFramework client = CuratorFrameworkFactory.builder()
.connectString("127.0.0.1:2181")
.sessionTimeoutMs(5000)
.retryPolicy(new ExponentialBackoffRetry(1000, 3))
.namespace("base")
.build();
client.start();
//新建節點
client.create().creatingParentsIfNeeded().withMode(CreateMode.EPHEMERAL).forPath(path, "i am nodecache".getBytes());
//監聽
final NodeCache cache = new NodeCache(client, path, false);
cache.start(true);
cache.getListenable().addListener(new NodeCacheListener() {
public void nodeChanged() throws Exception {
System.out.println("Node data update, new data: " + new String(cache.getCurrentData().getData()));
}
});
//更新節點
client.setData().forPath(path, "u".getBytes());
Thread.sleep(1000);
}
}
運行結果:
Node data update, new data: u
當節點數據變更后收到了通知。NodeCache不僅可以監聽數據節點的內容變更,也能監聽指定節點是否存在。
子節點監聽:
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.framework.recipes.cache.PathChildrenCache;
import org.apache.curator.framework.recipes.cache.PathChildrenCache.StartMode;
import org.apache.curator.framework.recipes.cache.PathChildrenCacheEvent;
import org.apache.curator.framework.recipes.cache.PathChildrenCacheListener;
import org.apache.curator.retry.ExponentialBackoffRetry;
import org.apache.zookeeper.CreateMode;
public class PathChildrenCache_Sample {
public static void main(String[] args) throws Exception {
String path = "/zk-book/nodecache";
CuratorFramework client = CuratorFrameworkFactory.builder()
.connectString("127.0.0.1:2181")
.sessionTimeoutMs(5000)
.retryPolicy(new ExponentialBackoffRetry(1000, 3))
.namespace("base")
.build();
client.start();
PathChildrenCache cache = new PathChildrenCache(client, path, true);
cache.start(StartMode.POST_INITIALIZED_EVENT);
cache.getListenable().addListener(new PathChildrenCacheListener() {
public void childEvent(CuratorFramework client, PathChildrenCacheEvent event) throws Exception {
switch (event.getType()) {
case CHILD_ADDED:
System.out.println("CHILD_ADDED," + event.getData().getPath());
break;
case CHILD_UPDATED:
System.out.println("CHILD_UPDATED," + event.getData().getPath());
break;
case CHILD_REMOVED:
System.out.println("CHILD_REMOVED," + event.getData().getPath());
break;
default:
break;
}
}
});
client.create().withMode(CreateMode.PERSISTENT).forPath(path);
client.create().withMode(CreateMode.PERSISTENT).forPath(path + "/c1");
client.delete().forPath(path + "/c1");
Thread.sleep(1000);
}
}
運行結果:
CHILD_ADDED,/zk-book/c1
CHILD_REMOVED,/zk-book/c1
監聽節點的子節點,包括新增、數據變化、刪除三類事件。
Master選舉:
借助Zookeeper,開發者可以很方便地實現Master選舉功能,其大體思路如下:選擇一個根節點,如/master_select,多臺機器同時向該節點創建一個子節點/master_select/lock,利用Zookeeper特性,最終只有一臺機器能夠成功創建,成功的那臺機器就是Master。
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.framework.recipes.leader.LeaderSelector;
import org.apache.curator.framework.recipes.leader.LeaderSelectorListenerAdapter;
import org.apache.curator.retry.ExponentialBackoffRetry;
public class Recipes_MasterSelect {
public static void main(String[] args) throws Exception {
String path = "/zk-book/nodecache";
CuratorFramework client = CuratorFrameworkFactory.builder()
.connectString("127.0.0.1:2181")
.sessionTimeoutMs(5000)
.retryPolicy(new ExponentialBackoffRetry(1000, 3))
.namespace("base")
.build();
client.start();
LeaderSelector selector = new LeaderSelector(client, master_path, new LeaderSelectorListenerAdapter() {
public void takeLeadership(CuratorFramework client) throws Exception {
System.out.println("成為Master角色");
Thread.sleep(3000);
System.out.println("完成Master操作,釋放Master權利");
}
});
selector.autoRequeue();
selector.start();
Thread.sleep(1000);
}
}
運行結果:
成為Master角色
完成Master操作,釋放Master權利
成為Master角色
以上結果會反復循環,并且當一個應用程序完成Master邏輯后,另外一個應用程序的相應方法才會被調用,即當一個應用實例成為Master后,其他應用實例會進入等待,直到當前Master掛了或者推出后才會開始選舉Master。