基於zookeeper實現配置中心功能的簡單工程

上一篇文章說到了一些zookeeper的應用場景,本篇將是對這些場景進行代碼模擬實戰的開篇文章。之前已經在本地搭建起了zk集羣,並進行了一些api實踐,因此本章主要講述如何使用zk客戶端模擬一個配置中心,推送變更的場景。

初始化配置bean

package com.coderman.zookeeper.clusterdemo.configdemo;

import java.io.Serializable;

/**
 * @description:
 * @author: Fanchunshuai
 * @time: 2020/2/13 15:04
 * zookeeper目錄規則:/group/ip/appName
 * 這裏的對象需要實現序列化
 */
public class ServerConfigBean implements Serializable {
    /**
     * 集羣節點目錄一級分組
     */
    private String group;
    /**
     * 集羣節點數據內容
     */
    private String nodeData;
    /**
     * 集羣ip
     */
    private String ip;
    /**
     * 集羣中的應用名稱
     */
    private String appName;


    public String getGroup() {
        return group;
    }

    public void setGroup(String group) {
        this.group = group;
    }

    public String getNodeData() {
        return nodeData;
    }

    public void setNodeData(String nodeData) {
        this.nodeData = nodeData;
    }

    public String getIp() {
        return ip;
    }

    public void setIp(String ip) {
        this.ip = ip;
    }

    public String getAppName() {
        return appName;
    }

    public void setAppName(String appName) {
        this.appName = appName;
    }

    @Override
    public String toString() {
        return "ServerConfigBean{" +
                "group='" + group + '\'' +
                ", nodeData='" + nodeData + '\'' +
                ", ip='" + ip + '\'' +
                ", appName='" + appName + '\'' +
                '}';
    }
}

構建配置管理器

package com.coderman.zookeeper.clusterdemo.configdemo;

import org.I0Itec.zkclient.IZkDataListener;
import org.I0Itec.zkclient.ZkClient;

/**
 * @description:
 * @author: Fanchunshuai
 * @time: 2020/2/14 15:45
 * 配置管理器
 */
public class ConfigManager {
    private static StringBuffer buffer = new StringBuffer();
    private static final int CONNECT_TIMEOUT = 5000;

    static {
        buffer.append("192.168.1.8:2184,");
        buffer.append("192.168.1.8:2181,");
        buffer.append("192.168.1.8:2182,");
        buffer.append("192.168.1.8:2183,");
        buffer.append("192.168.1.8:2185");
    }

    /**
     * 初始化數據
     * 一般需要將配置文件從配置管理平臺導入到zookeeper集羣,或者從別的文件等來源將配置數據持久化到zk
     *
     * @return
     */
    public ServerConfigBean getServerConfigBeanFromDB(){
        ServerConfigBean serverConfigBean = new ServerConfigBean();
        serverConfigBean.setAppName("app1");
        serverConfigBean.setIp("10.0.0.1");
        serverConfigBean.setNodeData("a,b,b,c,d");
        serverConfigBean.setGroup("b2bgroup");
        return serverConfigBean;
    }

    /**
     * 第一次初始化數據節點,並將配置文件數據同步到zookeeper中
     * 這裏相當於服務端的服務接口
     */
    public void syncConfigToZookeeper(ServerConfigBean serverConfigBean){
        ZkClient zkClient = new ZkClient(buffer.toString(),CONNECT_TIMEOUT);
        String path = "/"+serverConfigBean.getGroup()+"/"+serverConfigBean.getIp()+"/"+serverConfigBean.getAppName();
        if(!zkClient.exists(path)){
            //遞歸創建持久節點
            zkClient.createPersistent(path,true);
            zkClient.writeData(path,serverConfigBean);
        }
        zkClient.close();
    }

    /**
     * 更新配置到zookeeper集羣
     * 這裏相當於服務端的服務接口
     * @param serverConfigBean
     */
    public void updateConfigToZookeeper(ServerConfigBean serverConfigBean){
        ZkClient zkClient = new ZkClient(buffer.toString(),CONNECT_TIMEOUT);
        String path = "/"+serverConfigBean.getGroup()+"/"+serverConfigBean.getIp()+"/"+serverConfigBean.getAppName();
        zkClient.writeData(path,serverConfigBean);
        zkClient.close();
    }


    /**
     * 從zookeeper集羣中獲取數據配置
     * 這裏相當於客戶端的服務接口
     * @return
     */
    public ServerConfigBean getConfigFromZookeeper(ServerConfigBean serverConfigBean){
        ZkClient zkClient = new ZkClient(buffer.toString(),CONNECT_TIMEOUT);
        final ServerConfigBean[] serverConfigBeanx = {serverConfigBean};
        String path = getPath(serverConfigBean);
        //先讀取數據
        serverConfigBeanx[0] = zkClient.readData(path);
        zkClient.subscribeDataChanges(path,new IZkDataListener (){
            @Override
            public void handleDataChange(String s, Object o) throws Exception {
                System.out.println("config data changed = "+s);
                //如果數據有變化則更新
                serverConfigBeanx[0] = (ServerConfigBean)o;
                System.out.println( serverConfigBeanx[0].toString());
            }

            @Override
            public void handleDataDeleted(String s) throws Exception {
                System.out.println("config data deleted = "+s);
            }
        });
        return serverConfigBeanx[0];
    }

    private String getPath (ServerConfigBean serverConfigBean){
        return "/"+serverConfigBean.getGroup()+"/"+serverConfigBean.getIp()+"/"+serverConfigBean.getAppName();
    }

}

演示主代碼

package com.coderman.zookeeper.clusterdemo.configdemo;

import com.alibaba.fastjson.JSON;

/**
 * @description:
 * @author: Fanchunshuai
 * @time: 2020/2/14 16:16
 * 配置中心變更演示案例
 */
public class ConfigChangeDemo {
    public static void main(String[] args) {
        ConfigManager configManager = new ConfigManager();
        ServerConfigBean serverConfigBean = configManager.getServerConfigBeanFromDB();
        configManager.syncConfigToZookeeper(serverConfigBean);

        try {
            Thread.sleep(10000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        ServerConfigBean serverConfigBean1 = configManager.getConfigFromZookeeper(serverConfigBean);
        System.out.println(JSON.toJSONString(serverConfigBean1));

        try {
            Thread.sleep(10000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //更新數據
        serverConfigBean1.setNodeData("sdfasdfasdfasdf");
        //重新同步
        configManager.updateConfigToZookeeper(serverConfigBean1);
        //重新獲取數據
        ServerConfigBean serverConfigBean2 = configManager.getConfigFromZookeeper(serverConfigBean);
        System.out.println(JSON.toJSONString(serverConfigBean2));

        try {
            Thread.sleep(10000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //多線程更新,查看數據監聽變化
        //這裏的線程其實就是數據操作變更的客戶端

        new Thread(()->{
            //更新數據
            serverConfigBean1.setNodeData("sdfasdfasddddfasdf");
            //重新同步
            configManager.updateConfigToZookeeper(serverConfigBean1);
            ServerConfigBean serverConfigBean3 = configManager.getConfigFromZookeeper(serverConfigBean);
            System.out.println("serverConfigBean3 = "+JSON.toJSONString(serverConfigBean3));

        },"thread1").start();
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(()->{
            //更新數據
            serverConfigBean1.setNodeData("sddfasdf");
            //重新同步
            configManager.updateConfigToZookeeper(serverConfigBean1);
            ServerConfigBean serverConfigBean4 = configManager.getConfigFromZookeeper(serverConfigBean);
            System.out.println("serverConfigBean4 = "+JSON.toJSONString(serverConfigBean4));

        },"thread2").start();
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(()->{
            //更新數據
            serverConfigBean1.setNodeData("sdfasdfasddddddddfasdf");
            //重新同步
            configManager.syncConfigToZookeeper(serverConfigBean1);
            ServerConfigBean serverConfigBean5 = configManager.getConfigFromZookeeper(serverConfigBean);
            System.out.println("serverConfigBean5 = "+JSON.toJSONString(serverConfigBean5));

        },"thread3").start();

        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

總結:
zookeeper實現分佈式配置服務推送的主要角色有
1.zookeeper集羣,相當於存儲配置數據的容器
2.zookeeper管理平臺,相當於管理平臺提供對數據增,刪,改的服務接口或者可視化界面,數據源可以是手動輸入的也可以是從其他系統或文件中導入獲取的。
3.zookeeper的應用客戶端,比如我有一個app服務器,服務器端也需要連接zookeeper集羣,監聽節點變化,由於配置可能隨時更新,因此應用客戶端捕獲到內容變化之後需要更新配置。
如果想要做的更豐富的話,就需要有下面的輔助設施
1.監控:監控zk心跳,zk集羣等數據容量,數據延遲等
2.審批:節點審批,數據變更審批
相關鏈接:
https://www.cnblogs.com/hujunzheng/p/10932153.html

這裏僅僅進行了簡單的模擬知道怎麼回事,需要怎麼弄,如果想要做到可以生產環境使用還需要做更多內容。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章