继续浏览精彩内容
慕课网APP
程序员的梦工厂
打开
继续
感谢您的支持,我会继续努力的
赞赏金额会直接到老师账户
将二维码发送给自己后长按识别
微信支付
支付宝支付

Spring Cloud Config 客户端的高可用实现

慕虎7371278
关注TA
已关注
手记 1125
粉丝 201
获赞 871

Spring Cloud Config 用于构建分布式系统的配置中心,本文关注客户端的配置高可用,避免对spring cloud config server的强依赖!

Spring cloud config

在使用spring cloud 构建分布式系统的过程中,为了完成多个服务的配置统一管理,使用了spring cloud config作为配置中心,管理所有微服务的系统配置。

在分布式系统中,配置中心是一个独立的服务部件,作用是专门为其他服务提供系统启动所需的配置信息,保证系统正确启动。

使用中带来一个问题,即配置中心的高可用。

配置中心的高可用问题

配置中心(spring cloud config server)本身可以通过部署多个节点,并且通过服务注册中心(Eureka) 向其他服务系统提供服务。但是这种高可用在我看来本身并不可靠。

  • 配置中心不是业务系统,不会有其他业务系统那么高的高可用实施优先级,节点数量,主机性能,稳定性都会有所差距。

  • 配置中心可能又会依赖其他系统,如git,降低了高可用性,git一旦停止服务,则配置中心直接挂掉。

  • 后果严重 ,配置中心一旦全部失效,会导致所有服务都无法正常启动。

如何缓解

本文从客户端的角度出发,不是增加配置中心的高可用性,而是降低客户端对配置中心的依赖程度,从而提高整个分布式架构的健壮性。仅是缓解,不敢妄谈解决!
Spring cloud config 如何使用可参考:Spring Cloud构建微服务架构(四)分布式配置中心

简单的服务结构如下:

webp

spring cloud config server & client


服务A启动的主要步骤:

  1. 服务A判断Spring-cloud-starter-config是否在classpath

  2. 根据spring.cloud.config的配置(bootstrap.yml)请求配置中心,获取配置

  3. 配置中心根据application.name,profile和label访问git获取对应目录下的相应配置文件,并返回给服务A

  4. 服务A将加载获取到配置,并根据是否allowOverride来决定是否覆盖本地配置

  5. 继续启动...

本文关注的就是第4步中容易出现的问题,当配置中心/Git无法响应请求时,服务A能做什么。

更具体点

服务A在启动过程中调用了Spring cloud的启动环境配置类PropertySourceBootstrapConfiguration,该类实现了ApplicationContextInitializer接口,查看源码

// method initializeCompositePropertySource composite = new CompositePropertySource("bootstrapProperties");for (PropertySourceLocator locator : this.propertySourceLocators) {
    PropertySource<?> source = null;
    source = locator.locate(environment);    if (source == null) {        continue;
    }
    logger.info("Located property source: " + source);
    composite.addPropertySource(source);
}
insertPropertySources(propertySources, composite);

其中的PropertySourceLocator就是spring cloud config
提供的获取访问配置中心获取配置的方法。
可以看到,获取的结果被放入了composite中,并最终和本地的其他配置项合并。

insertPropertySources(propertySources, composite);

怎么做

理清了这个启动顺序,那么我们要做的是在这一步骤之后,判断服务A是否从配置中心获取到了配置数据。

  1. 如果获得了配置数据,备份配置数据。

  2. 如果没有获得,从备份配置数据中获取替代配置中心的配置数据完成启动。
    代码很简单,如下:

public class CloudConfigSupportConfiguration implements
        ApplicationContextInitializer<ConfigurableApplicationContext>, Ordered {    private int order = Ordered.HIGHEST_PRECEDENCE + 11;@Override
    public void initialize(ConfigurableApplicationContext applicationContext) {        if (!isHasCloudConfigLocator(this.propertySourceLocators)) {
            logger.info("未启用Config Server管理配置");            return;
        }
        logger.info("检查Config Service配置资源");

        ConfigurableEnvironment environment = applicationContext.getEnvironment();

        MutablePropertySources propertySources = environment.getPropertySources();
        logger.info("加载PropertySources源:" + propertySources.size() + "个");

        CloudConfigSupportProperties configSupportProperties = new CloudConfigSupportProperties();        new RelaxedDataBinder(configSupportProperties, CloudConfigSupportProperties.CONFIG_PREFIX)
                .bind(new PropertySourcesPropertyValues(propertySources));        if (!configSupportProperties.isEnable()) {
            logger.warn("未启用配置备份功能,可使用{}.enable打开", CloudConfigSupportProperties.CONFIG_PREFIX);            return;
        }        if (isCloudConfigLoaded(propertySources)) {
            PropertySource cloudConfigSource = getLoadedCloudPropertySource(propertySources);
            logger.info("成功获取ConfigService配置资源");            //备份
            Map<String, Object> backupPropertyMap = makeBackupPropertyMap(cloudConfigSource);
            doBackup(backupPropertyMap, configSupportProperties.getFile());

        } else {
            logger.error("获取ConfigService配置资源失败");

            Properties backupProperty = loadBackupProperty(configSupportProperties.getFile());            if (backupProperty != null) {
                HashMap backupSourceMap = new HashMap<>(backupProperty);

                PropertySource backupSource = new MapPropertySource("backupSource", backupSourceMap);
                propertySources.addFirst(backupSource);
                logger.warn("使用备份的配置启动:{}", configSupportProperties.getFile());
            }
        }
    }

}

要注意的是,启动顺序的设置

private int order = Ordered.HIGHEST_PRECEDENCE + 11;

这是因为spring cloud使用的PropertySourceBootstrapConfiguration启动顺序为

private int order = Ordered.HIGHEST_PRECEDENCE + 10;

完成coding后,为了在spring boot(服务A)启动时能够及时调用,需要加入/resources/META-INF/spring.factories的配置

# Bootstrap componentsorg.springframework.cloud.bootstrap.BootstrapConfiguration=\
df.open.support.configuration.CloudConfigSupportConfiguration\

Ok,启动服务A就会发现,自定义的CloudConfigSupportConfiguration已经生效,并且就在PropertySourceBootstrapConfiguration的后面执行!

至此,客户端的配置高可用就初步完成了,当然对于备份方式,以及服务刷新时的再次备份还需要进一步的工作去实现。



作者:化简为繁
链接:https://www.jianshu.com/p/8ac7ce6fe7e5


打开App,阅读手记
0人推荐
发表评论
随时随地看视频慕课网APP