依赖配置中心实现注有@ConfigurationProperties的bean相关属性刷新

配置中心是什么

  配置中心,通过key=value的形式存储环境变量。配置中心的属性做了修改,项目中可以通过配置中心的依赖(sdk)立即感知到。需要做的就是如何在属性发生变化时,改变带有@ConfigurationProperties的bean的相关属性。

配置中心原理

  在读配置中心源码的时候发现,里面维护了一个Environment,以及ZookeeperPropertySource。当配置中心属性发生变化的时候,清空ZookeeperPropertySource,并放入最新的属性值。

  

public class ZookeeperPropertySource extends EnumerablePropertySource<Properties>

  

  ZookeeperPropertySource重写了equals和hahscode方法,根据这两个方法可以判定配置中心是否修改了属性。

   

动态刷新bean属性原理

实现原理图

  

动态刷新bean父类

public abstract class BaseConfigCenterBean implements InitializingBean {

    private static Logger LOGGER = LoggerFactory.getLogger(BaseConfigCenterBean.class);

    //配置中心是否生效
    protected boolean cfgCenterEffect = false;

    public boolean isCfgCenterEffect() {
        this.checkCfgCenterEffect();
        return cfgCenterEffect;
    }

    private void checkCfgCenterEffect() {
        boolean tmpCfgCenterEffect = !Objects.isNull(ConfigHelper.getEnvironment());
        if (tmpCfgCenterEffect) {// NOSONAR
            String value = (String) ConfigHelper.getZookeeperPropertySource().getProperty("cfg.center.effect");
            if (StringUtils.isBlank(value)) {
                tmpCfgCenterEffect = false;
            } else {
                tmpCfgCenterEffect = Boolean.valueOf(value);
            }
        }

        cfgCenterEffect = tmpCfgCenterEffect;

        if (cfgCenterEffect) {
            String prefix = this.getConfigPrefix();
            cfgCenterEffect = Arrays.stream(ConfigHelper.getZookeeperPropertySource().getPropertyNames())
                    .filter(keyName -> keyName.indexOf(prefix) == 0)
                    .count() > 0;
            if (!cfgCenterEffect) {
                LOGGER.info(String.format("配置中心没有发现模块=%s, prefix=%s的配置,将使用本地配置...", this.getModuleName(), prefix));
            }
        }
    }

    /**
     * 绑定自身目标
     **/
    protected void doBind() {
        Class<? extends BaseConfigCenterBean> clazz = this.getClass();
        if (AopUtils.isCglibProxy(this)) {
            clazz = (Class<? extends BaseConfigCenterBean>) AopUtils.getTargetClass(this);
        }
        BaseConfigCenterBean target = binding(isCfgCenterEffect(), clazz, this.getDefaultResourcePath());
        this.copyProperties(target);
    }

    private void copyProperties(BaseConfigCenterBean target) {
        ReflectionUtils.doWithFields(this.getClass(), field -> {
            field.setAccessible(true);
            field.set(this, field.get(target));
        }, field -> AnnotatedElementUtils.isAnnotated(field, ConfigField.class));
    }

    /**
     * 绑定其他目标
     *
     * @param clazz 目标类
     **/
    protected <T> T doBind(Class<T> clazz) {
        T target = binding(isCfgCenterEffect(), clazz, this.getDefaultResourcePath());
        if (target instanceof InitializingBean) {
            try {
                ((InitializingBean) target).afterPropertiesSet();
            } catch (Exception e) {
                LOGGER.error(String.format("属性初始化失败[afterPropertiesSet], class=%s", ClassUtils.getSimpleName(clazz), e));
            }
        }
        return target;
    }

    private <T> T binding(boolean cfgCenterEffect, Class<T> clazz, String defaultResourcePath) {
        Optional<PropertySource> propertySource = Optional.empty();

        if (cfgCenterEffect) {
            propertySource = Optional.ofNullable(ConfigHelper.getZookeeperPropertySource());
        } else {
            Optional<ResourcePropertySource> resourcePropertySource = ResourceUtils.getResourcePropertySource(defaultResourcePath);
            if (resourcePropertySource.isPresent()) {
                propertySource = Optional.ofNullable(resourcePropertySource.get());
            }
        }

        if (propertySource.isPresent()) {
            T target;
            try {
                target = RelaxedConfigurationBinder
                        .with(clazz)
                        .setPropertySources(propertySource.get())
                        .doBind();
            } catch (GeneralException e) {
                LOGGER.error(String.format("属性绑定失败, class=%s", ClassUtils.getSimpleName(clazz)), e);
                return null;
            }
            return target;
        }
        return null;
    }

    @Override
    public void afterPropertiesSet() {
        Class<?> target = this.getClass();
        if (AopUtils.isAopProxy(this)) {
            target = AopUtils.getTargetClass(this);
        }
        LOGGER.info(String.format("%s->%s模块引入配置中心%s...", this.getModuleName(), ClassUtils.getSimpleName(target), (isCfgCenterEffect() ? "生效" : "无效")));
    }

    public String getModuleName() {
        return StringUtils.EMPTY;
    }

    @Subscribe
    public void listenRefreshEvent(ConfigCenterUtils.ConfigRefreshEvent refreshEvent) {
        if (!refreshEvent.getModuleName().equals(this.getModuleName())) {
            this.refreshForEvent();
        }
    }

    //通过事件进行刷新
    public abstract void refreshForEvent();

    //获取本地配置默认路径
    public abstract String getDefaultResourcePath();

    //获取配置属性的公共前缀
    public abstract String getConfigPrefix();
}

  1、isCfgCenterEffect方法主要判断项目是否接入了配置中心并且配置中心配有bean中相关的属性。

  2、binding方法主要根据isCfgCenterEffect方法的返回值去加载配置中心的properties还是本地的properties。

  3、getDefaultResourcePath是主要是获取本地资源的默认路径(在没有接入配置中心的情况下)。

  4、getConfigPrefix方法返回bean中配置属性的公共前缀(等同于@ConfigurationProperties中的prefix属性)。

  5、refreshForEvent方法主要是在某个bean感知到配置中心更新属性时异步通知其他bean进行属性的更新。

bean属性绑定工具类

  动态将propertysource绑定到带有@ConfigurationProperties注解的bean中。

  参考 org.springframework.boot.context.properties.ConfigurationPropertiesBindingPostProcessor

public class RelaxedConfigurationBinder<T> {
    private final PropertiesConfigurationFactory<T> factory;

    public RelaxedConfigurationBinder(T object) {
        this(new PropertiesConfigurationFactory<>(object));
    }

    public RelaxedConfigurationBinder(Class<?> type) {
        this(new PropertiesConfigurationFactory<>(type));
    }

    public static <T> RelaxedConfigurationBinder<T> with(T object) {
        return new RelaxedConfigurationBinder<>(object);
    }

    public static <T> RelaxedConfigurationBinder<T> with(Class<T> type) {
        return new RelaxedConfigurationBinder<>(type);
    }

    public RelaxedConfigurationBinder(PropertiesConfigurationFactory<T> factory) {
        this.factory = factory;
        ConfigurationProperties properties = getMergedAnnotation(factory.getObjectType(), ConfigurationProperties.class);
        javax.validation.Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
        factory.setValidator(new SpringValidatorAdapter(validator));
        factory.setConversionService(new DefaultConversionService());
        if (!Objects.isNull(properties)) {//NOSONAR
            factory.setIgnoreNestedProperties(properties.ignoreNestedProperties());
            factory.setIgnoreInvalidFields(properties.ignoreInvalidFields());
            factory.setIgnoreUnknownFields(properties.ignoreUnknownFields());
            factory.setTargetName(properties.prefix());
            factory.setExceptionIfInvalid(properties.exceptionIfInvalid());
        }
    }

    public RelaxedConfigurationBinder<T> setTargetName(String targetName) {
        factory.setTargetName(targetName);
        return this;
    }

    public RelaxedConfigurationBinder<T> setPropertySources(PropertySource<?>... propertySources) {
        MutablePropertySources sources = new MutablePropertySources();
        for (PropertySource<?> propertySource : propertySources) {
            sources.addLast(propertySource);
        }
        factory.setPropertySources(sources);
        return this;
    }

    public RelaxedConfigurationBinder<T> setPropertySources(Environment environment) {
        factory.setPropertySources(((ConfigurableEnvironment) environment).getPropertySources());
        return this;
    }

    public RelaxedConfigurationBinder<T> setPropertySources(PropertySources propertySources) {
        factory.setPropertySources(propertySources);
        return this;
    }

    public RelaxedConfigurationBinder<T> setConversionService(ConversionService conversionService) {
        factory.setConversionService(conversionService);
        return this;
    }

    public RelaxedConfigurationBinder<T> setValidator(Validator validator) {
        factory.setValidator(validator);
        return this;
    }

    public RelaxedConfigurationBinder<T> setResolvePlaceholders(boolean resolvePlaceholders) {
        factory.setResolvePlaceholders(resolvePlaceholders);
        return this;
    }

    public T doBind() throws GeneralException {
        try {
            return factory.getObject();
        } catch (Exception ex) {
            throw new GeneralException("配置绑定失败!", ex);
        }
    }
}

配置中心工具类

public class ConfigCenterUtils {
    private static Logger LOGGER = LoggerFactory.getLogger(ConfigCenterUtils.class);

    private static AsyncEventBus eventBus = new AsyncEventBus(Executors.newFixedThreadPool(8));//NOSONAR

    private static Properties cfgProperties;

    private static Environment environment;

    static {
        cfgProperties = new Properties();
        cfgProperties.putAll(ConfigHelper.getZookeeperPropertySource().getProperties());
    }

    public static void setEnvironment(Environment environment) {
        ConfigCenterUtils.environment = environment;
    }

    public static String getValue(String name) {
        try {
            return PropertiesUtil.getValue(name);
        } catch (Exception e) {
            LOGGER.info("配置中心无效, property name=" + name, e);
        }
        if (Objects.isNull(environment)) {
            LOGGER.info("environment无效,property name=" + name);
            return StringUtils.EMPTY;
        }
        if (!environment.containsProperty(name)) {
            LOGGER.info("environment无配置 property name=" + name);
            return StringUtils.EMPTY;
        }
        return environment.getProperty(name);
    }

    public synchronized static boolean propertySourceShouldRefresh(String moduleName, ZookeeperPropertySource newPropertySource) {
        if (!cfgProperties.equals(newPropertySource.getProperties())) {
            cfgProperties.clear();
            cfgProperties.putAll(newPropertySource.getProperties());
            eventBus.post(new ConfigRefreshEvent(moduleName));
            return true;
        }
        return false;
    }

    public static <T> T createToRefreshPropertiesBean(Class<T> clazz) {
        Enhancer enhancer = new Enhancer();
        // 设置代理对象父类
        enhancer.setSuperclass(clazz);
        // 标识Spring-generated proxies
        enhancer.setInterfaces(new Class[]{SpringProxy.class});
        // 设置增强
        enhancer.setCallback((MethodInterceptor) (target, method, args, methodProxy) -> {
            ToRefresh toRefresh = AnnotationUtils.findAnnotation(method, ToRefresh.class);
            if (Objects.isNull(toRefresh) || StringUtils.isBlank(toRefresh.method())) {
                return methodProxy.invokeSuper(target, args);
            }
            Method refreshMethod = ReflectionUtils.findMethod(target.getClass(), toRefresh.method());
            if (Objects.isNull(refreshMethod)) {
                return methodProxy.invokeSuper(target, args);
            }
            refreshMethod = BridgeMethodResolver.findBridgedMethod(refreshMethod);
            refreshMethod.setAccessible(true);
            refreshMethod.invoke(target, null);
            return methodProxy.invokeSuper(target, args);
        });

        T target = (T) enhancer.create();// 创建代理对象

        MethodIntrospector.selectMethods(clazz, (ReflectionUtils.MethodFilter) method -> AnnotatedElementUtils.isAnnotated(method, ToInitial.class))
                .stream().findFirst().ifPresent(method -> {
            method.setAccessible(true);
            try {
                method.invoke(target, null);
            } catch (Exception e) {
                LOGGER.error(String.format("初始化异常,class=%s ...", ClassUtils.getSimpleName(clazz)), e);
            }
        });

        return target;
    }

    public static void registerListener(BaseConfigCenterBean refreshableBean) {
        eventBus.register(refreshableBean);
    }

    public static class ConfigRefreshEvent {
        private String moduleName;

        public ConfigRefreshEvent(String moduleName) {
            this.moduleName = moduleName;
        }

        public String getModuleName() {
            return moduleName;
        }

        public void setModuleName(String moduleName) {
            this.moduleName = moduleName;
        }
    }
}

  这个工具主要作用:

  1、判断配置中心的属性是否发生了变化

  2、为BaseConfigCenterBean子类创建代理类,使属性在getter方法时检测属性是否应该刷新。

  3、提供将BaseConfigCenterBean类型的对象的注册为guava eventbus的监听对象,使之具有根据刷新事件自动刷新自身属性。

bean后置处理器

public class ConfigCenterBeanPostProcessor implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        if (AnnotatedElementUtils.isAnnotated(bean.getClass(), ConfigCenterBean.class)) {
            BaseConfigCenterBean refreshableBean = (BaseConfigCenterBean) ConfigCenterUtils.createToRefreshPropertiesBean(bean.getClass());
            ConfigCenterUtils.registerListener(refreshableBean);
            return refreshableBean;
        }
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }
}

  该后置处理器的作用是对所有BaseConfigCenterBean类型的bean进行处理,生成代理bean,并注册为guava eventbus相应的listener。

pojo属性绑定配置中心优雅方案1

@ConfigCenterBean
@ConfigurationProperties(prefix = "wx.temporary.qrcode")
@Component
public class QrcodeConstants extends BaseConfigCenterBean {

    private static Logger LOGGER = LoggerFactory.getLogger(QrcodeConstants.class);

    //渠道
    @ConfigField //标识该属性来自配置中心
    private List<Scene> channels;

    //业务
    @ConfigField
    private List<Scene> bizs;

    //业务和渠道映射关系
    @ConfigField
    private Map<String, String> biz2Channel;

    private Map<String, Scene> channelMap;

    private Map<String, Scene> bizMap;

    public List<Scene> getChannels() {
        return channels;
    }

    public void setChannels(List<Scene> channels) {
        this.channels = channels;
    }

    public List<Scene> getBizs() {
        return bizs;
    }

    public void setBizs(List<Scene> bizs) {
        this.bizs = bizs;
    }

    @ToRefresh(method = "toRefresh")
    public Map<String, Scene> getChannelMap() {
        return channelMap;
    }

    @ToRefresh(method = "toRefresh")
    public Map<String, Scene> getBizMap() {
        return bizMap;
    }

    @ToRefresh(method = "toRefresh")
    public Map<String, String> getBiz2Channel() {
        return biz2Channel;
    }

    public void setBiz2Channel(Map<String, String> biz2Channel) {
        this.biz2Channel = biz2Channel;
    }

    @ToInitial
    private void refreshQrcodeProperties() {
        try {
            super.doBind();

            //属性处理
            if (CollectionUtils.isEmpty(channels)) {
                this.channelMap = Maps.newHashMap();
            } else {
                this.channelMap = channels.stream()
                        .collect(Collectors.toMap(channel -> channel.getType(), Function.identity()));
            }

            if (CollectionUtils.isEmpty(bizs)) {
                this.bizMap = Maps.newHashMap();
            } else {
                this.bizMap = bizs.stream()
                        .collect(Collectors.toMap(biz -> biz.getType(), Function.identity()));
            }

            LOGGER.info(String.format("%s 刷新成功..., 当前配置=%s...", this.getModuleName(), this));
        } catch (Exception e) {
            LOGGER.error("QrcodeConstants 对象属性绑定失败...", e);
        }
    }

    private void toRefresh() {
        try {
            if (isCfgCenterEffect()) {
                ZookeeperPropertySource propertySource = ConfigHelper.getZookeeperPropertySource();
                if (ConfigCenterUtils.propertySourceShouldRefresh(this.getModuleName(), propertySource)) {
                    this.refreshQrcodeProperties();
                }
            }
        } catch (Exception e) {
            LOGGER.error("QrcodeConstants 对象属性刷新失败", e);
        }
    }

    //刷新事件调用
    @Override
    public void refreshForEvent() {
        this.refreshQrcodeProperties();
    }

  //本地资源文件
    @Override
    public String getDefaultResourcePath() {
        return "config/qrcode.properties";
    }

  //属性配置 公共前缀(和@ConfigurationProperties prefix 属性一致)
    @Override
    public String getConfigPrefix() {
        return "wx.temporary.qrcode";
    }

  //模块名称
    @Override
    public String getModuleName() {
        return "微信临时二维码配置";
    }

    @Override
    public String toString() {
        return ReflectionToStringBuilder.toString(this
                , ToStringStyle.JSON_STYLE
                , false
                , false
                , QrcodeConstants.class);
    }

    public static class Scene {
        private String type;
        private String desc;

        public String getType() {
            return type;
        }

        public void setType(String type) {
            this.type = type;
        }

        public String getDesc() {
            return desc;
        }

        public void setDesc(String desc) {
            this.desc = desc;
        }

        @Override
        public String toString() {
            return ReflectionToStringBuilder.toString(this
                    , ToStringStyle.JSON_STYLE
                    , false
                    , false
                    , Scene.class);
        }
    }
}

pojo属性绑定配置中心优雅方案2

@ConfigCenterBean
@Component
public class QrcodeConstants extends BaseConfigCenterBean {

    private static Logger LOGGER = LoggerFactory.getLogger(QrcodeConstants.class);

    //业务和渠道映射关系
    private Map<String, String> biz2Channel;

    //渠道
    private Map<String, Scene> channelMap;

    //业务
    private Map<String, Scene> bizMap;

    private QrcodeProperties qrcodeProperties;

    @ToRefresh(method = "toRefresh")
    public Map<String, Scene> getChannelMap() {
        return channelMap;
    }

    @ToRefresh(method = "toRefresh")
    public Map<String, Scene> getBizMap() {
        return bizMap;
    }

    @ToRefresh(method = "toRefresh")
    public Map<String, String> getBiz2Channel() {
        return biz2Channel;
    }

    public void setBiz2Channel(Map<String, String> biz2Channel) {
        this.biz2Channel = biz2Channel;
    }

    public QrcodeProperties getRawQrcodeProperties() {
        return qrcodeProperties;
    }

    @ToInitial
    private void refreshQrcodeProperties() {
        try {
            QrcodeProperties qrcodeProperties = super.doBind(QrcodeProperties.class);
            if (Objects.isNull(qrcodeProperties)) {
                LOGGER.error(String.format("没有加载到%s配置,请检查配置...", this.getModuleName()));
                return;
            }

            this.qrcodeProperties = qrcodeProperties;

            //属性处理
            if (CollectionUtils.isEmpty(qrcodeProperties.channels)) {
                this.channelMap = Maps.newHashMap();
            } else {
                this.channelMap = qrcodeProperties.channels.stream()
                        .collect(Collectors.toMap(channel -> channel.getType(), Function.identity()));
            }

            if (CollectionUtils.isEmpty(qrcodeProperties.bizs)) {
                this.bizMap = Maps.newHashMap();
            } else {
                this.bizMap = qrcodeProperties.bizs.stream()
                        .collect(Collectors.toMap(biz -> biz.getType(), Function.identity()));
            }

            if (CollectionUtils.isEmpty(qrcodeProperties.getBiz2Channel())) {
                this.biz2Channel = Maps.newHashMap();
            } else {
                this.biz2Channel = qrcodeProperties.getBiz2Channel();
            }

            LOGGER.info(String.format("%s 刷新成功..., 当前配置=%s...", this.getModuleName(), this));
        } catch (Exception e) {
            LOGGER.error("QrcodeConstants 对象属性绑定失败...", e);
        }
    }

    private void toRefresh() {
        try {
            if (isCfgCenterEffect()) {
                ZookeeperPropertySource propertySource = ConfigHelper.getZookeeperPropertySource();
                if (ConfigCenterUtils.propertySourceShouldRefresh(this.getModuleName(), propertySource)) {
                    this.refreshQrcodeProperties();
                }
            }
        } catch (Exception e) {
            LOGGER.error("QrcodeConstants 对象属性刷新失败", e);
        }
    }

    @Override
    public void refreshForEvent() {
        this.refreshQrcodeProperties();
    }

    @Override
    public String getDefaultResourcePath() {
        return "config/qrcode.properties";
    }

    @Override
    public String getConfigPrefix() {
        return "wx.temporary.qrcode";
    }

    @Override
    public String getModuleName() {
        return "微信临时二维码配置";
    }

    @Override
    public String toString() {
        return new ToStringBuilder(this)
                .append("biz2Channel", biz2Channel)
                .append("channelMap", channelMap)
                .append("bizMap", bizMap)
                .toString();
    }

    @ConfigurationProperties(prefix = "wx.temporary.qrcode")
    public static class QrcodeProperties {
        //渠道
        private List<Scene> channels;

        //业务
        private List<Scene> bizs;

        //业务和渠道映射关系
        private Map<String, String> biz2Channel;

        public List<Scene> getChannels() {
            return channels;
        }

        public void setChannels(List<Scene> channels) {
            this.channels = channels;
        }

        public List<Scene> getBizs() {
            return bizs;
        }

        public void setBizs(List<Scene> bizs) {
            this.bizs = bizs;
        }

        public Map<String, String> getBiz2Channel() {
            return biz2Channel;
        }

        public void setBiz2Channel(Map<String, String> biz2Channel) {
            this.biz2Channel = biz2Channel;
        }
    }

    public static class Scene {
        private String type;
        private String desc;

        public String getType() {
            return type;
        }

        public void setType(String type) {
            this.type = type;
        }

        public String getDesc() {
            return desc;
        }

        public void setDesc(String desc) {
            this.desc = desc;
        }

        @Override
        public String toString() {
            return ReflectionToStringBuilder.toString(this
                    , ToStringStyle.JSON_STYLE
                    , false
                    , false
                    , Scene.class);
        }
    }
}

  方案1和方案2略有不同,针对一些属性,我们需要做一些逻辑处理。方案1中将源属性和逻辑之后的属性都放在了同一类中,方案二则是将源属性单独放到一个静态类中,最终处理过后的属性放在了目标类中。另外二者的doBind方法也是有区别的,仔细看一下BaseConfigCenterBean这个类就可以了。

     就先分享这么多了,更多分享请关注我们的技术公众吧!!!

  参考文章:算法和技术SHARING

原文地址:https://www.cnblogs.com/hujunzheng/p/9697282.html

时间: 2024-09-30 23:22:29

依赖配置中心实现注有@ConfigurationProperties的bean相关属性刷新的相关文章

SpringCloud学习系列之五-----配置中心(Config)和消息总线(Bus)完美使用版

前言 在上篇中介绍了SpringCloud Config的使用,本篇则介绍基于SpringCloud(基于SpringBoot2.x,.SpringCloud Finchley版)中的分布式配置中心(SpringCloud Config)的配置刷新和消息总线(RabbitMQ和Kafka)使用教程. SpringCloud Config Refresh 在上一篇中我们介绍了springcloud配置中心的本地使用和Git使用的用法,但是当重新修改配置文件提交后,客户端获取的仍然是修改前的信息,需

Spring Cloud第十篇 | 分布式配置中心Config

? 本文是Spring Cloud专栏的第十篇文章,了解前九篇文章内容有助于更好的理解本文: Spring Cloud第一篇 | Spring Cloud前言及其常用组件介绍概览 Spring Cloud第二篇 | 使用并认识Eureka注册中心 Spring Cloud第三篇 | 搭建高可用Eureka注册中心 Spring Cloud第四篇 | 客户端负载均衡Ribbon Spring Cloud第五篇 | 服务熔断Hystrix Spring Cloud第六篇 | Hystrix仪表盘监控

Spring Cloud构建微服务架构分布式配置中心

Spring Cloud Config是Spring Cloud团队创建的一个全新项目,用来为分布式系统中的基础设施和微服务应用提供集中化的外部配置支持,它分为服务端与客户端两个部分.其中服务端也称为分布式配置中心,它是一个独立的微服务应用,用来连接配置仓库并为客户端提供获取配置信息.加密/解密信息等访问接口:而客户端则是微服务架构中的各个微服务应用或基础设施,它们通过指定的配置中心来管理应用资源与业务相关的配置内容,并在启动的时候从配置中心获取和加载配置信息.Spring Cloud Conf

构建微服务架构Spring Cloud:分布式配置中心

Spring Cloud Config是Spring Cloud团队创建的一个全新项目,用来为分布式系统中的基础设施和微服务应用提供集中化的外部配置支持,它分为服务端与客户端两个部分.其中服务端也称为分布式配置中心,它是一个独立的微服务应用,用来连接配置仓库并为客户端提供获取配置信息.加密/解密信息等访问接口:而客户端则是微服务架构中的各个微服务应用或基础设施,它们通过指定的配置中心来管理应用资源与业务相关的配置内容,并在启动的时候从配置中心获取和加载配置信息.Spring Cloud Conf

Spring Cloud(Dalston.SR5)--Config 集群配置中心

Spring Cloud Config 是一个全新的项目,用来为分布式系统中的基础设施和微服务应用提供集中化的外部配置支持,他分为服务端和客户端两个部分.服务端也称为分布式配置中心,是一个独立的微服务应用,用来连接配置仓库并为客户端提供获取配置信息.加密.解密信息等访问接口:而客户端则是为微服务架构中的各个微服务应用,通过指定的配置中心来管理应用资源与业务相关的配置内容,并在启动的时候从配置中心获取和加载配置信息.服务端与客户端的结构图如下: ? ? ? ? Spring Cloud 程序在进行

spring cloud 入门系列七:基于Git存储的分布式配置中心--Spring Cloud Config

我们前面接触到的spring cloud组件都是基于Netflix的组件进行实现的,这次我们来看下spring cloud 团队自己创建的一个全新项目:Spring Cloud Config.它用来为分布式系统中的基础设施和微服务提供集中化的外部配置支持,分为服务端和客户端两个部分. 其中服务端也称为分布式配置中心,他是独立的微服务应用,用来连接配置仓库并为客户端提供获取接口(这些接口返回配置信息.加密.解密信息等): 客户端是微服务架构中的各个微服务应用或基础设施,它们通过制定的配置中心来管理

第八章 分布式配置中心:Spring Cloud Config

Spring Cloud Config 是 Spring Cloud 团队创建的一个全新项目,用来为分布式系统中的基础设施和微服务应用提供集中化的外部配置支持, 它分为服务端与客户端两个部分. 其中服务端也称为分布式配置中心, 它是一个独立的微服务应用, 用来连接配置仓库并为客户端提供获取配置信息. 加密/解密信息等访问接口:而客户端则是微服务架构中的各个微服务应用或基础设施, 它们通过指定的配置中心来管理应用资源与业务相关的配置内容,并在启动的时候从配置中心获取和加载配置信息. Spring

springcloud配置中心

SpringCloud Config简介 Spring Cloud Config 是 Spring Cloud 团队创建的一个全新项目,用来为分布式系统中的基础设施和微服务应用提供集中化的外部配置支持,它分为服务端与客户端两个部分.其中服务端也称为分布式配置中心,它是一个独立的微服务应用,用来连接配置仓库并为客户端提供获取配置信息.加密 / 解密信息等访问接口:而客户端则是微服务架构中的各个微服务应用或基础设施,它们通过指定的配置中心来管理应用资源与业务相关的配置内容,并在启动的时候从配置中心获

SpringCloud服务配置中心

SpringCloud Config简介 Spring Cloud Config 是 Spring Cloud 团队创建的一个全新项目,用来为分布式系统中的基础设施和微服务应用提供集中化的外部配置支持,它分为服务端与客户端两个部分.其中服务端也称为分布式配置中心,它是一个独立的微服务应用,用来连接配置仓库并为客户端提供获取配置信息.加密 / 解密信息等访问接口:而客户端则是微服务架构中的各个微服务应用或基础设施,它们通过指定的配置中心来管理应用资源与业务相关的配置内容,并在启动的时候从配置中心获