spring cloud config server源码学习(一)

ops/2024/10/20 3:57:20/

文章目录

  • 1. 注解EnableConfigServer
  • 2. ConfigServerAutoConfiguration
    • 2.1 @ConditionalOnBean和@ConditionalOnProperty
    • 2.2 @Import注解
      • 2.2.1. EnvironmentRepositoryConfiguration.class
      • 2.2.2. CompositeConfiguration.class
      • 2.2.3. ResourceRepositoryConfiguration.class
      • 2.2.4. ConfigServerEncryptionConfiguration.class
      • 2.2.5. ConfigServerMvcConfiguration.class
      • 2.2.6. ResourceEncryptorConfiguration.class
  • 3. EnvironmentRepository
  • 4. EnvironmentRepositoryConfiguration.class
  • 5. rest接口

spring cloud config server 作为一个spring boot工程,到底是如何运行起来的?似乎如上一篇文章中那样,引入了starter,启动了注解,配置了git的信息,就可以获取到数据了。那具体的原理是什么呢?

1. 注解EnableConfigServer

@EnableConfigServer
@SpringBootApplication
@EnableDiscoveryClient
public class ConfigServer {public static void main(String[] args) {SpringApplication.run(ConfigServer.class, args);}
}

可以看到启动类上加入注解@EnableConfigServer。我们查看该注解的源码:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(ConfigServerConfiguration.class)
public @interface EnableConfigServer {}

这个注解的定义当中,通过Import注解加载Bean ConfigServerConfiguration.class

@Configuration(proxyBeanMethods = false)
public class ConfigServerConfiguration {@Beanpublic Marker enableConfigServerMarker() {return new Marker();}class Marker {}}

这个Configuration类只是加载了一个 Bean Marker。
这是spring加载Bean的一种常用方式。

2. ConfigServerAutoConfiguration

spring cloud config Server的jar中,org.springframework.boot.autoconfigure.AutoConfiguration.imports文件内包含了多个自动配置的类,其中就包括ConfigServerAutoConfiguration类。

org.springframework.cloud.config.server.bootstrap.ConfigServerBootstrapOverridesAutoConfiguration
org.springframework.cloud.config.server.config.ConfigServerAutoConfiguration
org.springframework.cloud.config.server.config.RsaEncryptionAutoConfiguration
org.springframework.cloud.config.server.config.DefaultTextEncryptionAutoConfiguration
org.springframework.cloud.config.server.config.EncryptionAutoConfiguration
org.springframework.cloud.config.server.config.VaultEncryptionAutoConfiguration

根据命名,可以看到各个自动配置类的功能,是启用bootstrap配置还是启用RSA加密等等。这里暂时先关注
ConfigServerAutoConfiguration类:

@Configuration(proxyBeanMethods = false)
@ConditionalOnBean(ConfigServerConfiguration.Marker.class)
@ConditionalOnProperty(name = ConfigServerProperties.PREFIX + ".enabled", matchIfMissing = true)
@EnableConfigurationProperties(ConfigServerProperties.class)
@Import({ EnvironmentRepositoryConfiguration.class, CompositeConfiguration.class, ResourceRepositoryConfiguration.class,ConfigServerEncryptionConfiguration.class, ConfigServerMvcConfiguration.class,ResourceEncryptorConfiguration.class })
public class ConfigServerAutoConfiguration {}

这个类的注解包含了三个重要的注解@ConditionalOnBean、@ConditionalOnProperty和@Import。

2.1 @ConditionalOnBean和@ConditionalOnProperty

当这两个注解出现在同一个类上的时候,两个Conditional条件必须同时满足,即ConfigServerConfiguration.Marker.class这个Bean存在的同时,还要满足Spring环境属性中存在 ConfigServerProperties.PREFIX + “.enabled” 属性且其值为 true,或者该属性缺失(由于 matchIfMissing = true)。这个PREFIX是spring.cloud.config.server。
这里刚好好上面通过@EnableConfigServer加载的Bean Marker.class呼应上。

2.2 @Import注解

Import注解引入了六个类要加载到spring context中。

2.2.1. EnvironmentRepositoryConfiguration.class

EnvironmentRepositoryConfiguration 是Spring Cloud Config Server中配置存储库的核心配置类。它负责创建和配置EnvironmentRepository实例,EnvironmentRepository用于从各种后端(如Git、SVN、本地文件系统等)获取配置属性。

定义和配置不同类型的EnvironmentRepository(如GitEnvironmentRepository、NativeEnvironmentRepository)。
通过注入不同的配置属性,来灵活配置不同类型的存储库。

2.2.2. CompositeConfiguration.class

CompositeConfiguration 负责配置和管理CompositeEnvironmentRepository。CompositeEnvironmentRepository允许将多个EnvironmentRepository组合在一起,以便从多个源获取配置。

配置CompositeEnvironmentRepository,将多个EnvironmentRepository实例组合成一个逻辑上的存储库。
提供从多个配置源合并配置属性的功能,以实现更复杂的配置管理场景。

2.2.3. ResourceRepositoryConfiguration.class

ResourceRepositoryConfiguration 负责配置与管理资源存储库(ResourceRepository)。ResourceRepository用于访问和管理配置服务器上的静态资源,如配置文件、密钥等。

配置不同类型的ResourceRepository,如文件系统资源存储库或Git资源存储库。
通过REST接口提供资源访问和管理功能。

2.2.4. ConfigServerEncryptionConfiguration.class

ConfigServerEncryptionConfiguration 负责配置加密和解密功能。它提供加密和解密配置属性的能力,确保敏感数据在传输和存储时得到保护。

配置加密器(TextEncryptor),用于加密和解密敏感配置信息。
提供加密和解密端点,允许客户端通过API进行加密和解密操作。

2.2.5. ConfigServerMvcConfiguration.class

ConfigServerMvcConfiguration 配置Spring MVC相关的组件,为Spring Cloud Config Server提供RESTful API。它定义了Config Server的主要控制器和路由。

定义Config Server的REST API端点,处理配置属性的请求。
配置HTTP请求处理、路由和控制器。

2.2.6. ResourceEncryptorConfiguration.class

ResourceEncryptorConfiguration 负责配置与资源加密相关的功能。它确保资源存储库中的敏感数据在存储和访问时得到加密保护。

配置用于资源加密和解密的组件。
提供加密资源的支持,确保静态资源的安全性。

3. EnvironmentRepository

对于 Spring Cloud Config 而言,它把所有的配置信息抽象为一种 Environment(环境),而存储这些配置信息的地方就称为 EnvironmentRepository。

public interface EnvironmentRepository {Environment findOne(String application, String profile, String label);default Environment findOne(String application, String profile, String label, boolean includeOrigin) {return findOne(application, profile, label);}}

spring cloud中把配置信息抽象为application,profile和label三个维度来管理,即哪一个应用application在什么样的环境profile下,使用哪一个label的配置数据。

4. EnvironmentRepositoryConfiguration.class

这个类里加载很多的内容,包括svn、git、jdbc等等的RepositoryConfiguration。其中有一个Configuration类是DefaultRepositoryConfiguration.class。这个是放在最后一个加载的配置,即默认的配置:

@Configuration(proxyBeanMethods = false)
@ConditionalOnMissingBean(value = EnvironmentRepository.class, search = SearchStrategy.CURRENT)
class DefaultRepositoryConfiguration {@Beanpublic MultipleJGitEnvironmentRepository defaultEnvironmentRepository(MultipleJGitEnvironmentRepositoryFactory gitEnvironmentRepositoryFactory,MultipleJGitEnvironmentProperties environmentProperties) throws Exception {return gitEnvironmentRepositoryFactory.build(environmentProperties);}}

这里是加载MultipleJGitEnvironmentRepository的Bean,由gitEnvironmentRepositoryFactory的build方法来构建:

public MultipleJGitEnvironmentRepository build(MultipleJGitEnvironmentProperties environmentProperties)throws Exception {if (this.connectionFactory.isPresent()) {HttpTransport.setConnectionFactory(this.connectionFactory.get());this.connectionFactory.get().addConfiguration(environmentProperties);}MultipleJGitEnvironmentRepository repository = new MultipleJGitEnvironmentRepository(this.environment,environmentProperties, ObservationRegistry.NOOP);repository.setTransportConfigCallback(transportConfigCallbackFactory.build(environmentProperties));if (this.server.getDefaultLabel() != null) {repository.setDefaultLabel(this.server.getDefaultLabel());}repository.setGitCredentialsProviderFactory(gitCredentialsProviderFactory);repository.getRepos().forEach((name, repo) -> repo.setGitCredentialsProviderFactory(gitCredentialsProviderFactory));return repository;}

而DefaultRepositoryConfiguration这个类,被GitRepositoryConfiguration继承了。

@Configuration(proxyBeanMethods = false)
@Profile("git")
class GitRepositoryConfiguration extends DefaultRepositoryConfiguration {}

也就是说 Spring Cloud Config 中默认使用 Git 作为配置仓库来完成配置信息的存储和管理,提供的 EnvironmentRepository 就是 MultipleJGitEnvironmentRepository,而 MultipleJGitEnvironmentRepository 则继承了抽象类 JGitEnvironmentRepository。

当服务器启动时,在 JGitEnvironmentRepository 中会决定是否调用 initClonedRepository() 方法来完成从远程 Git 仓库 Clone 代码。如果执行了这一操作,相当于会将配置文件从 Git 上 clone 到本地,然后再进行其他的操作。在 JGitEnvironmentRepository 抽象类中,提供了大量针对第三方 Git 仓库的操作代码,无论采用诸如 Git、SVN 等具体某一种配置仓库的实现方式,最终我们处理的对象都是位于本地文件系统中的配置文件。

请添加图片描述
在AbstractScmEnvironmentRepository类中

@Overridepublic synchronized Environment findOne(String application, String profile, String label) {return findOne(application, profile, label, false);}@Overridepublic synchronized Environment findOne(String application, String profile, String label, boolean includeOrigin) {NativeEnvironmentRepository delegate = new NativeEnvironmentRepository(getEnvironment(),new NativeEnvironmentProperties(), this.observationRegistry);Locations locations = getLocations(application, profile, label);delegate.setSearchLocations(locations.getLocations());Environment result = delegate.findOne(application, profile, "", includeOrigin);result.setVersion(locations.getVersion());result.setLabel(label);return this.cleaner.clean(result, getWorkingDirectory().toURI().toString(), getUri());}

在上面的findOne方法中,调用了NativeEnvironmentRepository类的findOne方法:

@Overridepublic Environment findOne(String config, String profile, String label, boolean includeOrigin) {try {ConfigurableEnvironment environment = getEnvironment(config, profile, label);DefaultResourceLoader resourceLoader = new DefaultResourceLoader();Map<org.springframework.core.env.PropertySource<?>, PropertySourceConfigData> propertySourceToConfigData = new HashMap<>();ConfigDataEnvironmentPostProcessor.applyTo(environment, resourceLoader, null,StringUtils.commaDelimitedListToSet(profile), new ConfigDataEnvironmentUpdateListener() {@Overridepublic void onPropertySourceAdded(org.springframework.core.env.PropertySource<?> propertySource,ConfigDataLocation location, ConfigDataResource resource) {propertySourceToConfigData.put(propertySource,new PropertySourceConfigData(location, resource));}});environment.getPropertySources().remove("config-data-setup");return clean(ObservationEnvironmentRepositoryWrapper.wrap(this.observationRegistry, new PassthruEnvironmentRepository(environment)).findOne(config, profile, label, includeOrigin), propertySourceToConfigData);}catch (Exception e) {String msg = String.format("Could not construct context for config=%s profile=%s label=%s includeOrigin=%b",config, profile, label, includeOrigin);String completeMessage = NestedExceptionUtils.buildMessage(msg,NestedExceptionUtils.getMostSpecificCause(e));throw new FailedToConstructEnvironmentException(completeMessage, e);}}

我们看到最终委托 PassthruEnvironmentRepository 完成配置文件的读取,然后通过 clean 方法完成本地文件地址与远程仓库之间地址的转换。ConfigDataEnvironmentUpdateListener用于监听Environment的更新。

5. rest接口

Server端获取到了数据,是通过rest接口来提供给client的。这里EnvironmentController提供了rest接口:

@GetMapping(path = "/{name}/{profiles:(?!.*\\b\\.(?:ya?ml|properties|json)\\b).*}",produces = MediaType.APPLICATION_JSON_VALUE)public Environment defaultLabel(@PathVariable String name, @PathVariable String profiles) {return getEnvironment(name, profiles, null, false);}@GetMapping(path = "/{name}/{profiles:(?!.*\\b\\.(?:ya?ml|properties|json)\\b).*}",produces = EnvironmentMediaType.V2_JSON)public Environment defaultLabelIncludeOrigin(@PathVariable String name, @PathVariable String profiles) {return getEnvironment(name, profiles, null, true);}@GetMapping(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);}@GetMapping(path = "/{name}/{profiles}/{label:.*}", produces = EnvironmentMediaType.V2_JSON)public Environment labelledIncludeOrigin(@PathVariable String name, @PathVariable String profiles,@PathVariable String label) {return getEnvironment(name, profiles, label, true);}

这里要注意path路径的配置,在类注解上有@RequestMapping(method = RequestMethod.GET, path = “${spring.cloud.config.server.prefix:}”)可以配置前缀,不配置的话就是默认值“”。
在方法上的路径,是/name/profile/label。这个正是配置文件中配置的内容。


http://www.ppmy.cn/ops/45332.html

相关文章

SpringBoot3笔记(一)SpringBoot3-核心特性

快速学习 SpringBoot 看官方文档&#xff1a; Spring Boot Reference Documentation 计划三天学完 笔记&#xff1a;https://www.yuque.com/leifengyang/springboot3 代码&#xff1a;https://gitee.com/leifengyang/spring-boot-3 一、SpringBoot3 - 快速入门 1.1 简介 …

解决文件传输难题:如何绕过Gitee的100MB上传限制

引言 在版本控制和代码托管领域&#xff0c;Gitee作为一个流行的平台&#xff0c;为用户提供了便捷的服务。然而&#xff0c;其对单个文件大小设定的100MB限制有时会造成一些不便。 使用云存储服务 推荐理由&#xff1a; 便捷性&#xff1a;多数云存储服务如&#xff1a; Dro…

力扣2028. 找出缺失的观测数据

题目&#xff1a; 现有一份 n m 次投掷单个 六面 骰子的观测数据&#xff0c;骰子的每个面从 1 到 6 编号。观测数据中缺失了 n 份&#xff0c;你手上只拿到剩余 m 次投掷的数据。幸好你有之前计算过的这 n m 次投掷数据的 平均值 。 给你一个长度为 m 的整数数组 rolls &a…

windows系统电脑外插键盘驱动出现感叹号或者显示未知设备,键盘无法输入的解决办法

笔记本外插的键盘不能用&#xff0c;鼠标可以使用。 查找故障&#xff0c;结果打开设备管理器看到键盘那项里是一个的黄色惊叹号显示未知设备&#xff01;[图片]如下图所示 其实解决办法很简单&#xff0c;不要相信网上的一些博主说删除什么注册表&#xff0c;我开始跟着他们操…

【busybox记录】【shell指令】mkdir

目录 内容来源&#xff1a; 【GUN】【mkdir】指令介绍 【busybox】【mkdir】指令介绍 【linux】【mkdir】指令介绍 使用示例&#xff1a; 创建文件夹 - 默认 创建文件夹 - 创建的同时指定文件权限 创建文件夹 - 指定多级文件路径&#xff0c;如果路径不存在&#xff0c…

【LeetCode面试经典150题】226. 翻转二叉树

一、题目 226. 翻转二叉树 - 力扣&#xff08;LeetCode&#xff09;给你一棵二叉树的根节点 root &#xff0c;翻转这棵二叉树&#xff0c;并返回其根节点。 二、思路 其实就是个二叉树遍历问题&#xff0c;只不过不是单纯的遍历&#xff0c;而是在遍历的同时进行元素的交换&a…

那智不二越机器人维修案例分享

那智不二越工业机器人在工业范围内广泛应用于各种生产领域。其示教器作为人机交互的重要设备&#xff0c;常常需要定期维护和Nachi不二越机械手示教盒修理。 【Nachi不二越机器人示教器维修步骤】 1. 关闭电源 在进行任何那智不二越机器人维修操作之前&#xff0c;务必确保机器…

C语言从头学13——流程控制(二)

接着学习流程控制&#xff0c;并继续上一篇的编号。 4、while 语句 while语句用于循环结构&#xff0c;满足条件时&#xff0c;不断执行循环体&#xff0c;直到条件不满足时退出。如果 一直满足循环条件无法结束循环&#xff0c;会陷入死循环&#xff0c;这是应当避免的…