0%

springCloudConfig获取远程配置

Spring Cloud Config 远程配置获取流程:客户端与服务端交互详解

Spring Cloud Config 的核心能力是实现配置的远程管理与分发,其底层通过客户端主动拉取服务端按需提供的方式完成配置交互。本文从客户端和服务端两方面,结合源码解析远程配置的获取全流程。

客户端(Config Client)获取远程配置的流程

客户端获取远程配置的核心是 PropertySourceLocator 接口,其实现类 ConfigServicePropertySourceLocator 负责与服务端通信并拉取配置。

核心接口:PropertySourceLocator

PropertySourceLocator 是 Spring Cloud 定义的配置源定位接口,用于从远程(如 Config Server)或本地加载配置,核心方法为 locate

1
2
3
4
public interface PropertySourceLocator {
// 定位并返回配置源(PropertySource)
PropertySource<?> locate(Environment environment);
}

Spring Cloud Config 客户端通过 ConfigServicePropertySourceLocator 实现该接口,完成远程配置拉取。

客户端拉取配置的触发时机

客户端配置拉取发生在 Spring Boot 启动的早期阶段( Bootstrap 阶段),由以下组件协同触发:

(1)Bootstrap 上下文初始化

Spring Cloud 会创建一个独立的 Bootstrap Context( Bootstrap 上下文),作为应用主上下文的父上下文,专门用于加载远程配置。其初始化由 BootstrapApplicationListener 监听器触发:

1
2
3
4
// SpringApplication 构造器中注册监听器
public SpringApplication(...) {
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
}

BootstrapApplicationListener 是关键监听器,会在应用启动时触发 Bootstrap 上下文的初始化。

(2)PropertySourceBootstrapConfiguration 执行配置加载

在 Bootstrap 上下文初始化过程中,PropertySourceBootstrapConfiguration(ApplicationContextInitializer 的实现类)会被执行。其 initialize 方法遍历所有 PropertySourceLocator 实例,调用 locate 方法拉取配置:

1
2
3
4
5
6
7
8
9
10
11
@Override
public void initialize(ConfigurableApplicationContext context) {
// 遍历所有 PropertySourceLocator(包括 ConfigServicePropertySourceLocator)
for (PropertySourceLocator locator : this.propertySourceLocators) {
PropertySource<?> source = locator.locate(context.getEnvironment());
if (source != null) {
// 将拉取的配置添加到环境中
context.getEnvironment().getPropertySources().addFirst(source);
}
}
}

ConfigServicePropertySourceLocator 拉取配置的细节

ConfigServicePropertySourceLocatorlocate 方法是客户端拉取配置的核心实现,步骤如下:

(1)构建请求参数

从客户端环境中提取配置参数(应用名、环境、分支等):

1
2
3
ConfigClientProperties properties = this.defaultProperties.override(environment);
// 分支参数(支持多个分支,按顺序尝试)
String[] labels = StringUtils.commaDelimitedListToStringArray(properties.getLabel());
(2)通过 RestTemplate 调用 Config Server

客户端通过 RestTemplate 向 Config Server 发送 HTTP 请求,路径格式为 /{name}/{profile}/{label}

1
2
// 调用远程配置中心
Environment result = getRemoteEnvironment(restTemplate, properties, label.trim(), state);

getRemoteEnvironment 方法内部构造请求 URL(如 http://config-server:7010/user-service/dev/master),并发送 GET 请求。

(3)处理服务端响应

服务端返回 Environment 对象(包含配置键值对),客户端将其转换为 PropertySource 并添加到本地环境中:

1
2
3
4
for (PropertySource source : result.getPropertySources()) {
Map<String, Object> map = (Map<String, Object>) source.getSource();
composite.addPropertySource(new OriginTrackedMapPropertySource(source.getName(), map));
}

至此,客户端成功获取远程配置,后续应用启动时可直接读取这些配置。

服务端(Config Server)提供远程配置的流程

服务端接收客户端的配置请求后,从 Git 等存储源加载配置并返回,核心由 EnvironmentControllerEnvironmentRepository 协同完成。

接收客户端请求:EnvironmentController

Config Server 通过 EnvironmentController 暴露 REST 接口,接收客户端的配置请求:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@RestController
public class EnvironmentController {
// 处理 /{name}/{profiles}/{label} 格式的请求
@RequestMapping(path = "/{name}/{profiles}/{label:.*}", produces = MediaType.APPLICATION_JSON_VALUE)
public Environment labelled(
@PathVariable String name, // 应用名
@PathVariable String profiles, // 环境(如 dev、prod)
@PathVariable String label // 分支(如 master)
) {
return getEnvironment(name, profiles, label, false);
}

private Environment getEnvironment(String name, String profiles, String label, boolean includeOrigin) {
// 调用 EnvironmentRepository 获取配置
return this.repository.findOne(name, profiles, label, includeOrigin);
}
}

加载配置:EnvironmentRepository

EnvironmentRepository 是服务端加载配置的核心接口,Git 存储对应实现类 JGitEnvironmentRepository(或 MultipleJGitEnvironmentRepository 用于多仓库场景)。

(1)多仓库场景:MultipleJGitEnvironmentRepository

若配置了多个 Git 仓库,MultipleJGitEnvironmentRepository 会先匹配当前请求对应的仓库:

1
2
3
4
5
6
7
8
9
10
11
public Environment findOne(String application, String profile, String label, boolean includeOrigin) {
for (PatternMatchingJGitEnvironmentRepository repo : this.repos.values()) {
if (repo.matches(application, profile, label)) { // 匹配仓库规则
// 从匹配的仓库中加载配置
Environment source = candidate.findOne(application, profile, label, includeOrigin);
if (source != null) return source;
}
}
// 未匹配到则使用默认仓库
return super.findOne(application, profile, label, includeOrigin);
}
(2)单仓库场景:JGitEnvironmentRepository

对于匹配的仓库,JGitEnvironmentRepository 完成配置加载,步骤如下:

① 克隆 / 拉取 Git 仓库到本地

服务端首次启动(或配置 clone-on-start: true)时,将远程 Git 仓库克隆到本地缓存目录;后续请求会先拉取最新代码(force-pull: true 配置强制拉取):

1
2
3
4
5
6
7
// 克隆远程仓库到本地
private Git cloneToBasedir() throws GitAPIException {
CloneCommand clone = gitFactory.getCloneCommand()
.setURI(getUri()) // 远程仓库地址
.setDirectory(getBasedir()); // 本地缓存目录
return clone.call();
}
② 从本地仓库加载配置文件

仓库准备完成后,服务端通过 NativeEnvironmentRepository 从本地缓存目录加载配置文件(如 user-service-dev.yml):

1
2
3
4
5
6
7
8
9
10
11
12
public Environment findOne(String application, String profile, String label, boolean includeOrigin) {
// 1. 准备本地仓库(克隆/拉取)
Locations locations = getLocations(application, profile, label);
// 2. 从本地目录加载配置
NativeEnvironmentRepository delegate = new NativeEnvironmentRepository(...);
delegate.setSearchLocations(locations.getLocations()); // 本地配置文件路径
Environment result = delegate.findOne(application, profile, "", includeOrigin);
// 3. 补充版本和分支信息
result.setVersion(locations.getVersion()); // Git 提交哈希
result.setLabel(label);
return result;
}
③ 转换为 Environment 对象

本地配置文件被加载后,转换为 Environment 对象(包含配置源、版本、分支等信息),返回给客户端:

1
2
3
4
5
6
7
8
// Environment 包含多个 PropertySource(对应多个配置文件)
public class Environment {
private String name; // 应用名
private String[] profiles; // 环境
private String label; // 分支
private String version; // Git 提交版本
private List<PropertySource> propertySources; // 配置键值对
}

客户端与服务端交互的完整流程总结

  1. 客户端启动:触发 Bootstrap 上下文初始化,PropertySourceBootstrapConfiguration 调用 ConfigServicePropertySourceLocatorlocate 方法。
  2. 客户端请求:通过 RestTemplate 向 Config Server 发送请求(/{name}/{profiles}/{label})。
  3. 服务端接收请求EnvironmentController 接收请求,调用 EnvironmentRepository 加载配置。
  4. 服务端加载配置JGitEnvironmentRepository 从 Git 仓库(本地缓存)加载配置文件,转换为 Environment 对象。
  5. 服务端响应:将 Environment 对象返回给客户端。
  6. 客户端处理响应:将 Environment 转换为 PropertySource,添加到本地环境中,供应用启动使用

欢迎关注我的其它发布渠道