作者:Grey
原文地址:ZooKeeper學習筆記三:使用ZooKeeper實現一個簡單的配置中心
前置知識
完成ZooKeeper叢集搭建以及熟悉ZooKeeperAPI基本使用
需求
很多程式往往是通過心跳檢測來判斷配置的變更,通過zk的回撥機制,我們可以實現比心跳更為快速的配置檢測機制,包括配置的新增,修改和刪除
流程也比較簡單:
環境準備
一個zk集權,ip和埠分別為:
- 192.168.205.145:2181
- 192.168.205.146:2181
- 192.168.205.147:2181
- 192.168.205.148:2181
定義主方法
App.java
public class App {
public static void main(String[] args) {
// 需要監控的路徑是/AppConf
String path = "/AppConf";
while (true) {
ConfigCenter configCenter = new ConfigCenter(path);
String conf = configCenter.getConf();
// 配置不為空則拿到最新的配置
if (null != conf && !conf.trim().isEmpty()) {
System.out.println(conf);
}
// 睡眠一段時間
pending(1000);
}
}
}
zk初始化工具類
參考ZooKeeperAPI基本使用中的ZookeeperConfig類,主要的方法為:
public class ZookeeperConfig {
private static final String ADDRESS = "192.168.205.145:2181,192.168.205.146:2181,192.168.205.147:2181,192.168.205.148:2181";
private static ZooKeeper zk;
static CountDownLatch latch;
// 獲取一個zk客戶端
public static ZooKeeper create() {
latch = new CountDownLatch(1);
try {
zk = new ZooKeeper(ADDRESS, 3000, new DefaultWatch());
latch.await();
} catch (IOException | InterruptedException e) {
e.printStackTrace();
}
return zk;
}
...
}
實現配置中心邏輯
配置中心的入口方法為:getConf()
public String getConf() {
aWait();
return value;
}
其中aWait()方法用於監聽配置資訊的變動(比如修改,刪除,增加),並且通過CountDownLatch阻塞執行,zk一旦監聽到配置資訊的變動,即會觸發回撥,並執行countDown(),這樣前面的CountDownLatch即可往下執行:
public void aWait() {
zk.exists(conf, this, this, "dasdfa");
try {
latch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
ConfigCenter類實現了Watcher, AsyncCallback.StatCallback, AsyncCallback.DataCallback三個介面,所以監聽方法和回撥方法都在ConfigCenter中實現,其中,監聽方法:
@Override
public void process(WatchedEvent event) {
Event.EventType type = event.getType();
switch (type) {
case None:
break;
case NodeCreated:
System.out.println("node created");
zk.getData(conf, this, this, "node created");
latch.countDown();
break;
case NodeDeleted:
try {
System.out.println("config deleted");
this.value = "";
latch = new CountDownLatch(1);
} catch (Exception e) {
e.printStackTrace();
}
break;
case NodeDataChanged:
System.out.println("node changed");
zk.getData(conf, this, this, "node changed");
latch.countDown();
break;
case NodeChildrenChanged:
break;
case DataWatchRemoved:
break;
case ChildWatchRemoved:
break;
case PersistentWatchRemoved:
break;
}
}
如上,當監聽到節點建立和修改的時候,觸發getData,當監聽到節點刪除的時候,重新初始化CountDownLatch,讓執行緒阻塞。
回撥方法如下:
@Override
public void processResult(int rc, String path, Object ctx, Stat stat) {
if (stat != null) {
zk.getData(conf, this, this, "getData");
}
}
@Override
public void processResult(int rc, String path, Object ctx, byte[] data, Stat stat) {
if (data != null) {
this.value = new String(data);
latch.countDown();
}
}
方法1表示,當節點存在(stat!=null)的時候,獲取一次資料(重新註冊監聽)。
方法2表示,當節點資料存在的時候,將節點資料取出,並且countDown(),解除阻塞。
執行效果
先把zk中的/AppConf節點刪除,執行App.java,程式阻塞
通過zkCli.sh連線任意zk服務端,建立一個/AppConf節點,並賦值
[zk: localhost:2181(CONNECTED) 58] create /AppConf "hello"
Created /AppConf
控制檯同時列印出:
node created
hello
hello
hello
hello
hello
hello
hello
hello
繼續通過zkCli重新設定/AppConf節點的值
[zk: localhost:2181(CONNECTED) 59] set /AppConf "world"
[zk: localhost:2181(CONNECTED) 60]
配置資訊立馬生效,控制檯列印
...
hello
hello
node changed
world
world
world
...
通過zkCli.sh刪除/AppConf節點,控制檯阻塞執行,並列印
config deleted
通過zkCli.sh重新建立/AppConf節點,
[zk: localhost:2181(CONNECTED) 61] create /AppConf "hello2"
Created /AppConf
控制檯立即解除阻塞,並列印
config deleted
node created
hello2
hello2
..