本文詳細了解下java操作Zookeeper的方法。
一.CURD操作
1.創建zk實例
private static String zkip = "192.168.1.207:2181";
private static ZooKeeper zk;
static{
try {
zk = new ZooKeeper(zkip, 6000, new Watcher() {
public void process(WatchedEvent watchedEvent) {
System.out.println("Receive event "+watchedEvent);
if(Event.KeeperState.SyncConnected == watchedEvent.getState())
System.out.println("connection is ok");
}
});
} catch (IOException e) {
e.printStackTrace();
}
}
new一個ZooKeeper需要三個參數:
第一個是ZooKeeper 服務器的ip和端口,
第二個是超時時間,
第三個是連接監聽器。
2.增刪改查操作
public void create() throws Exception{
zk.create("/zx", "aaa".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL);
String value = zk.getData("/zx", false, null).toString();
System.out.println(value);
}
public static void update() throws Exception{
zk.create("/zx1", "aaa".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
String value = new String(zk.getData("/zx1", false, null).toString());
System.out.println(value);
zk.setData("/zx1", "bbb".getBytes(),-1);
value = new String(zk.getData("/zx1", false, null).toString());
System.out.println(value);
}
public static void delete() throws Exception{
zk.create("/zx2", "aaa".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL);
String value = new String(zk.getData("/zx2", false, null));
System.out.println(value);
zk.delete("/zx2",0);
value = new String(zk.getData("/zx2", false, null).toString());
System.out.println(value);
}
注意:
1.create方法需要一個CreateMode,這是一個枚舉,定義了創建的節點是什么類型:持久(persistent)節點,順序(sequential)節點和臨時(ephemeral)節點。
2.update方法使用zk.setData進行更新時,需要傳入一個version,這個版本號需要與當前的版本號一直才能修改成功,如果需要忽略,傳入-1即可。
3.delete刪除時,同樣需要版本號。
二.Watcher使用
在zookeeper中,watcher機制是當服務端的節點信息發生了變化時通知所有監聽了該節點的客戶端。其允許客戶端向服務端注冊一個watcher監聽,當服務端的一些指定事件觸發了這個watcher,就會向指定的客戶端發送一個事件通知。
下面通過例子來看一下
public static void watch() throws Exception{
zk.create("/zx3", "aaa".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
zk.exists("/zx3", new Watcher() {
public void process(WatchedEvent watchedEvent) {
System.out.println(111);
System.out.println(watchedEvent.getPath());
System.out.println(watchedEvent.getState());
System.out.println(watchedEvent.getType());
}
});
zk.delete("/zx3", -1);
System.out.println(222);
}
輸出結果
Receive event WatchedEvent state:SyncConnected type:None path:null
connection is ok
111
/zx3
222
SyncConnected
NodeDeleted
watcher特性:
執行順序:注意客戶端watch回調的過程是一個異步觸發的過程,可以看到輸出111和222的位置,在執行了最后一行刪除代碼后才觸發的Watcher事件,此時主線程和watcher線程是并行的。
一次性:watcher監控是一次性的,一旦觸發了watch事件,該watcher也就失效了,所以每次必須重新設置監控。
輕量:WatchedEvent是ZK進行watch通知的最小單元,整個數據結構包含:事件狀態、事件類型、節點路徑。注意ZK只是通知client節點的數據發生了變化,而不會直接提供具體的數據內容。
三.AsyncCallback異步回調
AsyncCallback是在以異步方式使用 ZooKeeper API,例如:getData同步調用的版本是:byte[] getData(String path, boolean watch,Stat stat)
,異步調用的版本是:void getData(String path,Watcher watcher,AsyncCallback.DataCallback cb,Object ctx)
,可以看到,前者是直接返回獲取的結果,后者是通過AsyncCallback回調來拿到結果的。
public static void AsyncCallback() throws Exception{
zk.create("/zx4", "aaa".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL);
zk.getData("/zx4", false, new AsyncCallback.DataCallback() {
public void processResult(int rc, String path, Object ctx, byte[] data, Stat stat) {
System.out.println(2);
}
}, null);
}
輸出結果:
Receive event WatchedEvent state:SyncConnected type:None path:null
connection is ok
此時并沒有輸出zk.getData的結果值,因為程序先發出getData請求,然后運行結束,當Zookeeper服務器處理完請求后通過異步回調來返給客戶端結果。
我們在程序中增加一個睡眠時間來等待結果返回。
public static void AsyncCallback() throws Exception{
zk.create("/zx4", "aaa".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL);
zk.getData("/zx4", false, new AsyncCallback.DataCallback() {
public void processResult(int rc, String path, Object ctx, byte[] data, Stat stat) {
System.out.println(2);
System.out.println(path);
System.out.println(new String(data));
}
}, null);
Thread.sleep(100000);
}
輸出結果
Receive event WatchedEvent state:SyncConnected type:None path:null
connection is ok
2
/zx4
aaa
這時結果返回了。
四.Watcher 和 AsyncCallback 的區別
zooKeeper.getData(root, new Watcher() {
public void process(WatchedEvent event) {
}
}, new AsyncCallback.DataCallback() {
public void processResult(int rc, String path, Object ctx, byte[] data, Stat stat) {
}
}, null);
可以看到,getData方法可以同時設置兩個回調:Watcher 和 AsyncCallback,同樣是回調,它們的區別是什么呢?
Watcher:Watcher是用于監聽節點,session 狀態的,比如getData對數據節點a設置了watcher,那么當a的數據內容發生改變時,客戶端會收到NodeDataChanged通知,然后進行watcher的回調。
AsyncCallback:AsyncCallback是在以異步方式使用 ZooKeeper API ,用于處理返回結果的。例如:getData:void getData(String path,Watcher watcher,AsyncCallback.DataCallback cb,Object ctx),通過AsyncCallback回調來處理返回結果。
下面我們用一幅圖來說明:
Zookeeper 的exists,getData,getChildren方法都有異步的版本,它們與同步方法的區別僅僅在于是否等待響應,底層發送都是通過sendThread異步發送的。