基於Zookeeper的路由和負載均衡實現思想

一)服務註冊中心思想
圖1
如上圖所示,Zookeeper上的服務配置中心分成三層結構,最上面一層爲根節點,用來聚集服務節點,通過他可以查詢到所有的服務,而服務名稱下掛載的是服務提供者的服務器地址。根節點和服務名稱採用的是Zookeeper的持久節點(persistent),服務提供者的地址節點,採用的是非持久節點(ephemeral)。

服務提供者在啓動時,將其提供的服務名稱、服務器地址,以節點(Znode)的形式註冊到服務配置中心。服務消費者通過服務配置中心來獲得需要調用的服務名稱節點下的機器列表節點。通過負載均衡算法,選取其中一臺服務器調用。當服務器宕機或者下線時,由於Znode非持久節點(EPHEMERAL)的特性,相應的機器可以動態地從服務配置中心裏移除,並觸發服務消費者的watcher。在這個過程中,服務消費者只需要第一次調用服務時查詢服務配置中心,然後將查詢的服務信息緩存到本地,當服務地址列表變更時,變更行爲會觸發服務消費者註冊的watcher,在watcher的回調事件中進行服務地址的修改。這種無中心化的結構,使得服務消費者在服務信息沒有變更時,幾乎不依賴配置中心,大大降低了服務配置中心的壓力。
圖2
當服務規模變大,服務之間的依賴變得越來越複雜時,服務之間的調用關係已非人力所能理清,很多時候我們不僅需要了解有哪些服務提供方,還需要知道有哪些服務消費者,以瞭解服務的調用情況。對比圖1,圖2增加了一層用來表示節點類型,每個服務包含兩種節點類型,即consumer和provider。當服務消費者啓動時,即在服務配置中心裏,在其調用的所有服務的consumer節點下增加自己的機器地址,如圖2所示。這樣,只需要後臺監控程序解析出對應服務的consumer節點的子節點,便能清楚地知道某一服務被哪些機器消費了。

二、實現代碼
基於Zookeeper所實現的服務消費者獲取服務提供者地址列表的部分代碼如下

        String zkServerList="127.0.0.1:2181";
        String serviceName="serviceB";
        String servicePath="/configcenter/"+serviceName;//服務節點路徑
        ZkClient zkClient=new ZkClient(zkServerList);
        List<String> serviceList=null;
        boolean serviceExists=zkClient.exists(servicePath);
        if(serviceExists){//服務存在,取服務地址
            serviceList=zkClient.getChildren(servicePath);
        }else{
            throw new RuntimeException("service not exist!");
        }
        //註冊監聽事件,服務列表節點變更時,重新獲取
       zkClient.subscribeChildChanges(servicePath, new IZkChildListener() {
            public void handleChildChange(String parentPaths, List<String> currentChilds) throws Exception {
                serviceList=currentChilds;
            }
        });

服務提供者向Zookeeper集羣註冊服務的部分代碼如下

       String zkServerList="127.0.0.1:2181";
        String rootPath="/configcenter/";//根節點路徑
        String serviceName="serviceB";//服務名稱

        ZkClient zkClient=new ZkClient(zkServerList);

        boolean rootExists=zkClient.exists(rootPath);
        if(!rootExists){
            zkClient.createPersistent(rootPath);
        }

        boolean serviceExists=zkClient.exists(rootPath+"/"+serviceName);
        if(!serviceExists){
            //創建服務節點
            zkClient.createPersistent(rootPath+"/"+serviceName);
        }
        //註冊當前服務器,可以在節點的數據裏面存放節點的權重
        InetAddress addr= InetAddress.getLocalHost();
        String ip=addr.getHostAddress().toString();//獲得本機IP

        //創建當前服務器節點
        zkClient.createEphemeral(rootPath+"/"+serviceName+"/"+ip);
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章