ZooKeeper zkClient的使用


ZkClient解決了watcher的一次性注冊問題,將znode的事件重新定義為子節點的變化、數據的變化、連接狀態的變化三類,有ZkClient統一將watcher的WatchedEvent轉換到以上三種情況中去處理,watcher執行后重新讀取數據的同時,在注冊新的相同的watcher。

1.簡單的使用ZkClient

    public static void main( String[] args )
    {
        //連接Zookeeper
        ZkClient zkClient = new ZkClient("127.0.0.1:2181");
        //創建節點
        zkClient.create("/root", "server data", CreateMode.PERSISTENT);
        //創建子節點
        zkClient.create("/root/server", "server data", CreateMode.EPHEMERAL);
        //獲取子節點
        List<String> children = zkClient.getChildren("/root");
        //編輯子節點
        for (String item : children) {
            System.out.println(item);
        }
        //獲取子節點的數量
        Integer number = zkClient.countChildren("/root");
        //判斷節點是否存在
        if(zkClient.exists("/root")){
            System.out.println("存在");
        };
        //寫入數據
        zkClient.writeData("/root/child", "哈哈哈");
        //讀取節點數據
        Object object = zkClient.readData("/root/child");
        //刪除節點
        zkClient.delete("/root/child");
    }
    

ZkClient將Zookeeper的watcher機制轉化為一種更加容易理解的訂閱形式,並且這種關系是可以保持的,而非一次性的。也就是說子節點的變化、數據的變化、狀態的變化是可以訂閱的。當watcher使用完后,zkClient會自動增加一個相同的watcher。

        zkClient.subscribeChildChanges("/root", new IZkChildListener() {
            
            @Override
            public void handleChildChange(String arg0, List<String> arg1) throws Exception {
                // TODO Auto-generated method stub
                System.out.println("子節點的變化");
            }
        });
        zkClient.subscribeDataChanges("/root", new IZkDataListener() {
            
            @Override
            public void handleDataDeleted(String arg0) throws Exception {
                // TODO Auto-generated method stub
                System.out.println("數據被刪除");
            }
            
            @Override
            public void handleDataChange(String arg0, Object arg1) throws Exception {
                // TODO Auto-generated method stub
                System.out.println("數據被更改");
            }
        });
        zkClient.subscribeStateChanges(new IZkStateListener() {
            
            @Override
            public void handleStateChanged(KeeperState arg0) throws Exception {
                // TODO Auto-generated method stub
                System.out.println("數據狀態改變");
            }
            
            @Override
            public void handleNewSession() throws Exception {
                // TODO Auto-generated method stub
                System.out.println("當session expire異常重新連接時,由於原來的所有的watcher和EPHEMERAL節點都已失效,可以在這里進行相應的容錯處理");
            }
        });

2.路由和負載均衡的實現

當服務越來越多,規模越來越大,機器的數量也會越來越大,如果純靠人工來管理和維護服務及地址的信息,已經越來越困難了。並且,依賴單一的硬件負載均衡設備或者使用LVS和Ngnix等軟件方案來解決進行路由和負載均衡調度,一旦服務路由或者服務器宕機,所依賴的所有服務都將失效。如果采用雙機高可用的部署方案,使用一台服務器“stand by”,能部分解決問題,但是鑒於負載均衡設備的昂貴成本,也難以全面推廣。

一旦服務器與Zookeeper集群斷開連接,節點就不存在了,通過注冊相應的watcher,服務消費者能夠第一時間獲知提供者機器信息的變更。利用其znode的特點和watcher記住,將作為動態注冊和獲取服務信息的配置中心,統一管理服務名稱和其對應的服務列表消息。我們近乎實時的感知到后端服務器的狀態(上線、下線、宕機)。Zookeeper集群之間通過zab協議,服務配置信息能夠保持一致,而Zookeeper本身的容錯特性和leader選舉機制,能保障我們方便地進行擴容。

        //基於Zookeeper所實現的服務消費者獲取服務提供者地址列表的關鍵代碼 消費者
        ZkClient zkClient = new ZkClient("127.0.0.1:2181");
        String serviceName = "service-B";
        String zkServiceList = "127.0.0.1:2181";
        
        String SERVICE_PATH = "/configcenter/"+serviceName;
        //判斷是否存在
        boolean serviceExists = zkClient.exists(SERVICE_PATH);
        //如果存在,獲取地址列表
        if(serviceExists){
            serviceList = zkClient.getChildren(SERVICE_PATH);
        }else{
            throw new RuntimeException("節點不存在");
        }
        //注冊事件監聽
        zkClient.subscribeChildChanges(SERVICE_PATH, new IZkChildListener() {
            
            @Override
            public void handleChildChange(String arg0, List<String> currentChilds) throws Exception {
                // TODO Auto-generated method stub
                //返回新的列表
                serviceList = currentChilds;
            }
        });

這段代碼實例化了一個zkClient對象,判斷服務名稱是否已經注冊,如果還沒有注冊該節點,則表明沒有該服務,繼而拋出異常。如果存在則獲取其所有子節點獲取地址后就可以通過負載均衡算法,選出一台服務器,發起遠程調用。服務器列表緩存在本地只有當地址列表發生變化時,才需要更新該列表,以降低網絡開銷。這就是注冊一個 subscribeChildChanges 的作用。

3.服務提供者想Zookeeper集群注冊的部分關鍵代碼:

        //服務提供者想Zookeeper集群注冊服務的關鍵代碼  服務者
        ZkClient zkClient = new ZkClient("127.0.0.1:2181");
        String PATH = "/configcenter";//根節點路徑
        //判斷是否存在
        boolean rootExists = zkClient.exists(PATH);
        //如果存在,獲取地址列表
        if(!rootExists){
            zkClient.createPersistent(PATH);
        }
        String serviceName = "service-c";
        boolean serviceExists = zkClient.exists(PATH+"/"+serviceName);
        if(!serviceExists){
            zkClient.createPersistent((PATH+"/"+serviceName));
        }
        InetAddress address  = InetAddress.getLocalHost();
        //創建當前服務器節點
        String ip = address.getHostAddress().toString();
        //創建當前服務器節點
        zkClient.createEphemeral(PATH+"/"+serviceName+"/"+ip);
        
        for (String item : zkClient.getChildren("/configcenter")) {
            System.out.println(item);
        }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM