經過前面四章的講解,我們已經對zookeeper建立起初步的概念,這篇文章就來做一個小小的實踐,用zookeeper實作一個簡單版的服務注冊與發現中心。
zookeeper的一個常見功能就是作為服務注冊與發現中心。
我們先建立一個節點/services。
Stat stat = zkClient.exists("/services",false);
if (stat == null ){
zkClient.create("/services","".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
}
每當有一個服務上線時,我們就向我們的服務注冊與發現中心zookeeper注冊我們的應用。
比如,我們注冊一個user服務,服務位址是localhost:8080,那麼我們就在/services下面建立一個user子節點,子節點資料為user服務的真實url位址,比如localhost:8080,子節點類型為臨時節點。
public void registerService()throws Exception{
zkClient.create("/services/user","localhost:8080".getBytes(),
ZooDefs.Ids.OPEN_ACL_UNSAFE,CreateMode.EPHEMERAL);
}
當我們向user請求服務時,首先通過/services節點擷取user服務,判斷user服務是否存在。進而擷取它的位址,發起真正的請求。同時,我們注冊一個監聽事件,監聽節點的狀态變化。當user服務出現故障或其他因素而下線時,/services/user節點會被删除,zookeeper server會通知到監聽這個節點的用戶端,進而使用戶端做出自己的響應,同樣的,當user服務上線或位址修改,用戶端也能收到通知。
public void invokeUserService()throws Exception{
Stat stat = zkClient.exists("/services/user",false);
if (stat == null){
System.out.println("未能找到user服務,服務未注冊或已下線");
}
byte[] url = zkClient.getData("/services/user", new Watcher() {
@Override
public void process(WatchedEvent watchedEvent) {
if (watchedEvent.getType() == Event.EventType.NodeDeleted){
System.out.println("服務下線");
// 處理業務邏輯
}
if (watchedEvent.getType() == Event.EventType.NodeCreated){
System.out.println("服務上線");
// 處理業務邏輯
}
if (watchedEvent.getType() == Event.EventType.NodeDataChanged){
System.out.println("服務位址修改了");
}
}
}, null);
// 處理業務邏輯
System.out.println("向"+new String(url)+"發起請求");
}

如果對前面有印象的話,應該記得zookeeper的watcher隻觸發一次,當節點狀态改變一次之後,節點狀态的第二次改變就不能監聽到了。為了能夠持續監聽,我們需要修改一下我們的代碼。
我們把判斷服務上線的代碼挪到上面來,并且在下面的監聽事件裡回調invokeUserService方法,實作持續監聽的功能。
為了簡單易懂,這裡代碼寫得并不夠好,如果是實際項目,需要再做點拆分與封裝。
public void invokeUserService()throws Exception{
Stat stat = zkClient.exists("/services/user",false);
if (stat == null){
System.out.println("未能找到user服務,服務未注冊或已下線");
zkClient.exists("/services/user", new Watcher() {
@Override
public void process(WatchedEvent watchedEvent) {
if (watchedEvent.getType() == Event.EventType.NodeCreated){
System.out.println("服務上線");
// 處理業務邏輯
}
}
});
}else{
byte[] url = zkClient.getData("/services/user", new Watcher() {
@Override
public void process(WatchedEvent watchedEvent) {
if (watchedEvent.getType() == Event.EventType.NodeDeleted){
System.out.println("服務下線");
// 處理業務邏輯
}
if (watchedEvent.getType() == Event.EventType.NodeDataChanged){
System.out.println("服務位址修改了");
}
try {
invokeUserService();
}catch (Exception e){
}
}
}, null);
// 處理業務邏輯
System.out.println("向"+new String(url)+"發起請求");
}
}
zookeeper作為一個分布式協調架構,它的建立就是為了友善或者簡化分布式應用的開發。除了服務注冊與發現之外,它還能夠提供更多的功能,但是對于入門來說,簡單的了解到這裡就已經足夠了。下面會講zookeeper的架構設計與原理,比如zookeeper的原子協定,leader選舉算法等。