Spring集成Disconf 实现配置动态获取 (配置类方式实现)

一. 背景

最近在重构自己负责产品的脚手架,本文介绍了我在Spring中集成Disconf客户端并实现配置动态获取的做法。

二. 配置

2.1 maven

<dependency>
      <groupId>com.baidu.disconf</groupId>
      <artifactId>disconf-client</artifactId>
      <version>2.6.36</version>
 </dependency>

2.2 Disconf 配置类

Disconf配置类需要提供最核心的几个类分别是:

1. DisconfMgrBean

    第一次扫描。下载远端disconf中的配置文件、扫描本地指定路径的静态配置类以及配置文件信息,最终将所有的数据整合并存入本地仓库(其实就是一个Map)中。

2. DisconfMgrBeanSecond

   第二次扫描。扫描配置项或配置文件的回调函数,获取并处理指定的配置文件实例,接着处理配置文件中的所有文件项。

   比如现在有一个专门存储配置的实例:

   1. 配置项在仓库中存在,则将此实例的配置文件项的属性值设置成仓库里的值。

   2. 配置项在仓库中不存在,则将此实例的配置文件项的属性值设置成默认值。(默认值可能来源于静态文件、也可能直接来源于数据类型(由内存擦除后获得)) 。

   也就是说,第二次扫描后,配置信息才会真正的填充到实例中。

3. ReloadablePropertiesFactoryBean

    1. 定义哪些Bean是需要被托管的配置类,它会经历配置成仓库容器模块->扫描模块->下载模块->watch模块

    2. 记录哪些配置与哪些Bean的属性绑定。

import com.baidu.disconf.client.DisconfMgrBean;
import com.baidu.disconf.client.DisconfMgrBeanSecond;
import com.baidu.disconf.client.addons.properties.ReloadablePropertiesFactoryBean;
import com.google.common.collect.ImmutableList;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer;
import org.springframework.boot.autoconfigure.AutoConfigureOrder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.io.IOException;
import java.util.List;
import java.util.Objects;
import java.util.Properties;

@Configuration(value = "disConfConfiguration")
public class DisConfConfiguration {
    /**
     * DisconfMgrBean 下载远端disconf中的配置文件、扫描本地静态配置类以及配置文件信息,最终将所有的数据整合并入库
     *                所谓的入库就是将配置信息存储到一个Map集合中
     *                DisconfMgrBean着重用处理文件
     * @return DisconfMgrBean
     */
    @Bean
    public DisconfMgrBean getDisconfMgrBean(){
        DisconfMgrBean bean = new DisconfMgrBean();
        // 配置类所在的包路径
        bean.setScanPackage("uyun.unipass.config.disconf");
        return bean;
    }

    /**
     * DisconfMgrBean2 扫描配置项或配置文件的回调函数,获取并处理指定的配置文件实例,接着处理配置文件中的所有文件项
     *                 比如:
     *                 1. 配置项在仓库中存在,则将此实例的配置文件项的域值设置成仓库里的值。
     *                 2. 配置项在仓库中不存在,则将此实例的配置文件项的域值设置成默认值。 (默认值可能来源于静态文件、也可能直接来源于数据类型(由内存擦除后获得))
     *                 DisconfMgrBean2着重用处理文件中的每一条文件项。
     *                 显然,DisconfMgrBeanSecond一定要在DisconfMgrBean之后执行/生成
     */
    @Bean(destroyMethod = "destroy", initMethod = "init")
    public DisconfMgrBeanSecond getDisconfMgrBean2(){
        return new DisconfMgrBeanSecond();
    }

    @Bean(name = "reloadablePropertiesFactoryBean")
    @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
    public ReloadablePropertiesFactoryBean reloadablePropertiesFactoryBean() {
        ReloadablePropertiesFactoryBean propertiesFactoryBean = new ReloadablePropertiesFactoryBean();
        List<String> remoteFileNames = ImmutableList.of("classpath*:common.properties", "classpath*:platform-unipass.properties");
        propertiesFactoryBean.setSingleton(true);
        propertiesFactoryBean.setLocations(remoteFileNames);
        return propertiesFactoryBean;
    }
}

 2.3 项目配置类

以上只是让Disconf托管了配置文件,程序本身并不会自动的重新重载(reload)值到指定的属性上,若想实现此功能,则需要使用以下注解。注意,如果使用了Spring的注解,如@Component,则一定要将项目配置类申明为单例@Scope(singleton),这是因为@Component注解没有使用cglib动态代理,每一次提供的对象都是一个全新的对象,这样一来disconf就不知道往哪个对象中注入属性值了。使用时请务必通过getXXX()方法来获取配置值,这样做才能达到动态获取值得效果。

1. @DisconFile(filename = "xxx.properties")

    当前项目配置类对应哪一份配置文件

2. @DisconfFileItem

   项目配置类的属性(域)对应配置文件中的哪一条配置信息的key。    

import com.baidu.disconf.client.common.annotations.DisconfFile;
import com.baidu.disconf.client.common.annotations.DisconfFileItem;
import com.baidu.disconf.client.common.annotations.DisconfUpdateService;
import lombok.Setter;
import lombok.ToString;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.context.annotation.Scope;
import uyun.unipass.config.ConfigCentral;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**
 * disconfig common 配置文件
 * @author mamr
 */

@Slf4j
@ToString
@Setter
@DisconfFile(filename = "common.properties")
@SuppressWarnings({"unused"})
public class CommonConfig {
    /**
     * zookeeper服务地址
     */
    private String zkUrl;

    @DisconfFileItem(name = "zk.url")
    public String getZkUrl() {
        return zkUrl;
    }
}

  2.4 disconf.properties

# 是否使用远程配置文件
# true:会从远端获取配置文件 false: 直接使用本地配置文件 默认值:true
enable.remote.conf=true

# Disconf服务器的地址,若有多个,则用英文逗号分隔
conf_server_host=192.168.0.10/disconf

# 版本, 请采用 X_X_X_X 格式
version=1_0_0

# APP 请采用 产品线_服务名 格式
app=prometheus_demo

# 环境
env=local

# debug
debug=false

# 忽略哪些分布式的配置
ignore=

# 获取远程配置 重试次数,默认是3次
conf_server_url_retry_times=10

#获取远程配置 重试时休眠的时间间隔,默认是5秒
conf_server_url_retry_sleep_seconds=3

三. Disconf中关键类的作用

3.1 ReloadConfigurationMonitor

   TimeTask定时任务类。每隔一段时间,从远端拉取最新的配置文件,使用File lastModified()获取文件最后一次修改时间,并与本地配置文件作对比,若有差异,则说明远端配置文件被修改,重新将新值填充(刷新)到项目配置类中。

3.2 IDisconfUpdate

   当远端(disconf服务器上)的配置更新时,我们可以通过实现这个接口,定义自己的回调函数(实现reload方法)。

   注意:

   1. 当配置更新后,这个接口会被调用两次!

   2. 实现IDisconfUpdate的类一定要受到Spring的管理。

四. 写在最后

本文只是简单的实现了Spring集成Disconf 实现配置动态获取的功能,如果要在其它配置类中读取disconf的动态配置属性,尤其是在项目启动时,另一个Configuration内声明的@Bean对象中需要用到Disconf动态配置值时,则还需要做一些额外的工作。具体请参考我的另一篇博客: 传送门

发布了45 篇原创文章 · 获赞 13 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/miaomiao19971215/article/details/103795793