Spring Cloud全解析:配置中心之springCloudConfig获取远程配置

springCloudConfig获取远程配置

client端获取配置

springCloudConfig核心其实在于实现了一个PropertySourceLocator接口来进行获取远程配置的

@Order(0)
public class ConfigServicePropertySourceLocator implements PropertySourceLocator {

   @Override
   @Retryable(interceptor = "configServerRetryInterceptor")
   public org.springframework.core.env.PropertySource<?> locate(
         org.springframework.core.env.Environment environment) {
      ConfigClientProperties properties = this.defaultProperties.override(environment);
      CompositePropertySource composite = new OriginTrackedCompositePropertySource(
            "configService");
      RestTemplate restTemplate = this.restTemplate == null
            ? getSecureRestTemplate(properties) : this.restTemplate;
      Exception error = null;
      String errorBody = null;
      try {
         String[] labels = new String[] { "" };
         if (StringUtils.hasText(properties.getLabel())) {
            labels = StringUtils
                  .commaDelimitedListToStringArray(properties.getLabel());
         }
         String state = ConfigClientStateHolder.getState();
         // Try all the labels until one works
         for (String label : labels) {
           // 获取远程配置,这里就是使用restTemplate访问spring.cloud.config.uri对应的配置中心server地址/{name}/{profile}/{label}
            Environment result = getRemoteEnvironment(restTemplate, properties,
                  label.trim(), state);
            if (result != null) {
               log(result);

               // result.getPropertySources() can be null if using xml
               if (result.getPropertySources() != null) {
                  for (PropertySource source : result.getPropertySources()) {
                     @SuppressWarnings("unchecked")
                     Map<String, Object> map = translateOrigins(source.getName(),
                           (Map<String, Object>) source.getSource());
                     composite.addPropertySource(
                           new OriginTrackedMapPropertySource(source.getName(),
                                 map));
                  }
               }

               if (StringUtils.hasText(result.getState())
                     || StringUtils.hasText(result.getVersion())) {
                  HashMap<String, Object> map = new HashMap<>();
                  putValue(map, "config.client.state", result.getState());
                  putValue(map, "config.client.version", result.getVersion());
                  composite.addFirstPropertySource(
                        new MapPropertySource("configClient", map));
               }
               return composite;
            }
         }
         errorBody = String.format("None of labels %s found", Arrays.toString(labels));
      }
      catch (HttpServerErrorException e) {
         error = e;
         if (MediaType.APPLICATION_JSON
               .includes(e.getResponseHeaders().getContentType())) {
            errorBody = e.getResponseBodyAsString();
         }
      }
      catch (Exception e) {
         error = e;
      }
      if (properties.isFailFast()) {
         throw new IllegalStateException(
               "Could not locate PropertySource and the fail fast property is set, failing"
                     + (errorBody == null ? "" : ": " + errorBody),
               error);
      }
      
      return null;

   }

}

那么PropertySourceLocator接口是何时被调用的呢?

在springBoot启动的时候执行SpringApplication构造器的时候设置了ApplicationListener监听器

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
   this.resourceLoader = resourceLoader;
   Assert.notNull(primarySources, "PrimarySources must not be null");
   this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
   this.webApplicationType = WebApplicationType.deduceFromClasspath();
   setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
  // 设置ApplicationListener监听器
   setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
   this.mainApplicationClass = deduceMainApplicationClass();
}

其中监听器中有BootstrapApplicationListener,在该监听器中会加入一个ApplicationContextInitializer初始化器的实现类PropertySourceBootstrapConfiguration

Set target = new LinkedHashSet<>(application.getInitializers());
target.addAll(
      getOrderedBeansOfType(context, ApplicationContextInitializer.class));

而在applyInitializers(context)中会遍历initializers初始化器集合,调用initialize方法

for (ApplicationContextInitializer initializer : getInitializers()) {
   Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(initializer.getClass(),
         ApplicationContextInitializer.class);
   Assert.isInstanceOf(requiredType, context, "Unable to call initializer.");
   initializer.initialize(context);
}

在PropertySourceBootstrapConfiguration的initialize方法中会遍历propertySourceLocators集合,来执行PropertySourceLocator的locateCollection方法,这样就和springCloudConfig获取配置接上了,使用ConfigServicePropertySourceLocator类

server端获取配置

根据上边的代码可以看到已经请求到server端了,那么server端如何拿到配置呢?先找一下访问地址对应的controller,果然在EnvironmentController中找到了对应的地址

@RequestMapping(path = "/{name}/{profiles}/{label:.*}",
      produces = MediaType.APPLICATION_JSON_VALUE)
public Environment labelled(@PathVariable String name, @PathVariable String profiles,
      @PathVariable String label) {
   return getEnvironment(name, profiles, label, false);
}

这里会调用

Environment environment = this.repository.findOne(name, profiles, label,
      includeOrigin);

我们以git为例,也就是使用MultipleJGitEnvironmentRepository来进行获取

public Environment findOne(String application, String profile, String label,
      boolean includeOrigin) {
   for (PatternMatchingJGitEnvironmentRepository repository : this.repos.values()) {
      if (repository.matches(application, profile, label)) {
         for (JGitEnvironmentRepository candidate : getRepositories(repository,
               application, profile, label)) {
            try {
               if (label == null) {
                  label = candidate.getDefaultLabel();
               }
               Environment source = candidate.findOne(application, profile,
                     label, includeOrigin);
               if (source != null) {
                  return source;
               }
            }
            catch (Exception e) {
               if (this.logger.isDebugEnabled()) {
                  this.logger.debug(
                        "Cannot load configuration from " + candidate.getUri()
                              + ", cause: (" + e.getClass().getSimpleName()
                              + ") " + e.getMessage(),
                        e);
               }
               continue;
            }
         }
      }
   }
   JGitEnvironmentRepository candidate = getRepository(this, application, profile,
         label);
   if (label == null) {
      label = candidate.getDefaultLabel();
   }
   if (candidate == this) {
      return super.findOne(application, profile, label, includeOrigin);
   }
   return candidate.findOne(application, profile, label, includeOrigin);
}

就简单点,假如是第一次进来,就不用看上边的遍历了,直接去获取

super.findOne(application, profile, label, includeOrigin);

这里调用的是AbstractScmEnvironmentRepository的方法,

public synchronized Environment findOne(String application, String profile,
      String label, boolean includeOrigin) {
   NativeEnvironmentRepository delegate = new NativeEnvironmentRepository(
         getEnvironment(), new NativeEnvironmentProperties());
  // 创建本地git仓库,从对应git上进行clone,pull等操作
   Locations locations = getLocations(application, profile, label);
   delegate.setSearchLocations(locations.getLocations());
  // 之后就可以从本地git仓库进行读取了
   Environment result = delegate.findOne(application, profile, "", includeOrigin);
   result.setVersion(locations.getVersion());
   result.setLabel(label);
   return this.cleaner.clean(result, getWorkingDirectory().toURI().toString(),
         getUri());
}

https://zhhll.icu/2021/框架/微服务/springcloud/配置中心/springCloudConfig/源码分析/1.springCloudConfig获取远程配置/

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

拾光师

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值