最近频繁的系统上线,每次打包都要把配置文件替换为正式环境的配置文件,虽然说就是复制粘贴的事,架不住文件杂乱,而且多。

  期初的想法是有没有办法将配置文件与系统隔离开来,这样在更新时候,就只需要更新代码部分,配置文件不用去管了。查了不少资料,没找到什么好的实现方式,别人比较好的发布框架都是自己写的,也用了,干脆自己研究研究能不能使用zk做一个配置文件的管理中心,顺带实现动态加载配置文件而不需要重启系统(正式环境这种情况使用的应该很少,不过本地倒是用起来顺手很多)。

  

  1,在虚拟机上安装zk(需先安装jdk):下载tar文件,解压,修改 cp zoo_sample.cfg zoo.cfg,

  • tickTime:这个时间是作为 Zookeeper 服务器之间或客户端与服务器之间维持心跳的时间间隔,也就是每个 tickTime 时间就会发送一个心跳。
  • dataDir:顾名思义就是 Zookeeper 保存数据的目录,默认情况下,Zookeeper 将写数据的日志文件也保存在这个目录里。
  • clientPort:这个端口就是客户端连接 Zookeeper 服务器的端口,Zookeeper 会监听这个端口,接受客户端的访问请求。

  进入到bin目录下,sh zkServer.sh start

  进入zk客户端   sh zkCli.sh -server localhost:2181

  创建文件  create /fileName value

 

  2.java端引入zk客户端

  1).maven

   <!-- ZooKeeper -->
        <dependency>
            <groupId>org.apache.zookeeper</groupId>
            <artifactId>zookeeper</artifactId>
            <version>3.4.6</version>
        </dependency>
        <dependency>
            <groupId>org.apache.curator</groupId>
            <artifactId>curator-recipes</artifactId>
            <version>2.9.0</version>
        </dependency>
        <dependency>
            <groupId>org.apache.curator</groupId>
            <artifactId>curator-framework</artifactId>
            <version>2.9.0</version>
        </dependency>
    <!-- Zookeeper -->

  2).zookeeper.properties  

    zk.servers=192.168.3.252:2181
    zk.config.root.path=/conf

  3.我们在使用配置文件的时候,都是使用spring来托管,在项目启动的时候就加载到项目中去,zk有三种监听方式,为能够同时监听主目录及子目录的实时变化,选用TreeCacheListener。zk能够直接从服务端获取对应目录下的数据,并能够实时监听对应节点的变化以及变化之后的值,这里首先在项目启动加载配置文件的时候,用服务端的数据去覆盖项目中的数据,这样保证服务端有配置时以服务端为准,没有时则以项目中配置运行,然后实时监听对应节点,只要有服务端配置被修改,则覆盖项目中的原有配置,并刷新容器,使用最新的配置。

  核心代码:继承spring PropertyPlaceholderConfigurer 重写processProperties方法加载配置文件

  

@Override
    protected void processProperties(ConfigurableListableBeanFactory beanFactory, Properties props) throws BeansException {
   
        InputStream in = ZooKeeperService.class.getClassLoader().getResourceAsStream("zookeeper.properties");
        Properties pro = new Properties();
        try{
            pro.load(in);
        }catch(Exception e){
            logger.debug("zookeeper.properties读取错误!"+e.getMessage());
        }
        
        this.zookeeperServers = pro.getProperty("zk.servers");
        PATH = pro.getProperty("zk.config.root.path");
        
        RetryPolicy retryPolicy = new ExponentialBackoffRetry(BASE_SLEEP_TIMEMS, MAX_RETRIES);
        this.client = CuratorFrameworkFactory.builder().connectString(this.zookeeperServers).retryPolicy(retryPolicy).build();
        client.start();
        
        @SuppressWarnings("resource")
        TreeCache cache = new TreeCache(this.client, PATH);
        try {
            cache.start();
            
            TreeCacheListener listener = new TreeCacheListener() {
                
                @Override
                public void childEvent(CuratorFramework client, TreeCacheEvent event)
                        throws Exception {
                    if(null != event && null != event.getData()){
                        logger.debug(event.getData().getPath().toString()+":"
                                +new String(event.getData().getData()));
                        
                        String[] arr = event.getData().getPath().toString().split("/");
                        String key = arr[arr.length-1];
                        String value = new String(event.getData().getData());
                        proMap.putAll(updateConf(key,value));
                        
                    }
                }
            };
            
            cache.getListenable().addListener(listener);
            
        } catch (Exception e) {
            logger.debug(e.getMessage());
        }
        
        try {
        //加载顺序问题,因为是一个监听的过程,所以没办法顺序执行,测试时这里直接使用了延时来保证先获取到服务端的数据然后在加载参数,没有想到其他好的方法解决,在尝试使用
CountDownLatch来解决,后续修改
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        props.putAll(proMap);
        super.processProperties(beanFactory, props);
    }

 

  基本实现如此。待完善。

 

 

maven 插件方式拆分开发,测试,正式环境配置文件

  resources 文件夹下 创建 dev(开发) qa(测试) pre(预生产) prod(生产) 几个文件夹 并且把对应的配置丢到(公用的不用丢)对应的文件夹

修改pom.xml
在dependencies 下面增加
<!--定义不同的环境变量-->
  <profiles>
    <profile>
      <!--开发-->
      <id>dev</id>
      <properties>
        <env>dev</env>
      </properties>
    </profile>
    <profile>
      <!--测试-->
      <id>qa</id>
      <properties>
        <env>qa</env>
      </properties>
  <!--默认是测试,额外加参数可修改此项,例如: mvn clean package -Pdev 表示打包时使用开发环境-->
      <activation>
        <activeByDefault>true</activeByDefault>
      </activation>
    </profile>
    <profile>
  <!--预生产-->
      <id>pre</id>
      <properties>
        <env>pre</env>
      </properties>
    </profile>
    <profile>
      <!--生产-->
      <id>prod</id>
      <properties>
        <env>prod</env>
      </properties>
    </profile>
  </profiles>

build 节点下增加
  <!-- 控制 复制到target\classes文件夹的资源 的情况 -->
  <resources>
  <!-- 资源根目录排除各环境的配置,使用单独的资源目录来指定,这一步复制公共的配置 -->
    <resource>
      <directory>src/main/resources</directory>
      <filtering>true</filtering>
      <excludes>
        <exclude>dev/*</exclude>
        <exclude>qa/*</exclude>
        <exclude>pre/*</exclude>
        <exclude>prod/*</exclude>
      </excludes>
    </resource>
  <!-- 这一步复制指定环境的配置 -->
    <resource>
    <directory>src/main/resources/${env}</directory>
    </resource>
  </resources>

 

搞定

maven插件会自动将对应的配置文件build进去,避免每次修改配置文件