Server
-
如何加载
- 核心接口是EnvironmentRepository,提供配置的读取
public interface EnvironmentRepository { Environment findOne(String application, String profile, String label); }
- EnvironmentRepository有多种实现,基于JDBC、SVN、GIT等等
- 默认情况下,使用的是 MultipleJGitEnvironmentRepository(可以配置多个地址的GIT数据源)
- ConfigServerAutoConfiguration -> EnvironmentRepositoryConfiguration -> DefaultRepositoryConfiguration
@Configuration @ConditionalOnMissingBean(value = EnvironmentRepository.class) class DefaultRepositoryConfiguration { ... @Bean public MultipleJGitEnvironmentRepository defaultEnvironmentRepository(...) throws Exception { return gitEnvironmentRepositoryFactory.build(environmentProperties); } }
- MultipleJGitEnvironmentRepository 代理遍历每个 JGitEnvironmentRepository, JGitEnvironmentRepository 下使用 NativeEnvironmentRepository 代理读取本地文件。
- AbstractScmEnvironmentRepository#findOne
public synchronized Environment findOne(String application, String profile, String label) { NativeEnvironmentRepository delegate = new NativeEnvironmentRepository(getEnvironment(), new NativeEnvironmentProperties()); Locations locations = getLocations(application, profile, label); delegate.setSearchLocations(locations.getLocations()); Environment result = delegate.findOne(application, profile, ""); ... getUri()); }
其中getLocations这部会从GIT远程仓库同步到本地
JGitEnvironmentRepository#getLocations -> JGitEnvironmentRepository.refresh
public String refresh(String label) {
Git git = createGitClient();
...
checkout(git, label);
...
merge(git, label);
...
resetHard(...)
...
}
- 配置文件加载优先级顺序(上面的覆盖下面的)
模式 | 应用 |
---|---|
{spring.application.name}-{profile}.properties/yml | 应用-环境-配置 |
{spring.application.name}.properties/yml | 应用-全局-配置 |
application-{profile}.properties/yml | 公众-环境-配置 |
application.properties/yml | 公众-全局-配置 |
具体的逻辑参考NativeEnvironmentRepository和ConfigFileApplicationListener
-
如何注入
- ConfigServer本身也可以注入自己的读取的配置,使得其他服务可以和ConfigServer配置在一起,比如Eureka
- Config的自身注入在BootStrap阶段
- ConfigServerBootstrapConfiguration#LocalPropertySourceLocatorConfiguration
@Configuration @ConditionalOnProperty("spring.cloud.config.server.bootstrap") public class ConfigServerBootstrapConfiguration { @Bean public EnvironmentRepositoryPropertySourceLocator environmentRepositoryPropertySourceLocator() { return new EnvironmentRepositoryPropertySourceLocator(this.repository, this.client.getName(), this.client.getProfile(), getDefaultLabel()); } }
- EnvironmentRepositoryPropertySourceLocator会调用EnvironmentRepository获取配置
public class EnvironmentRepositoryPropertySourceLocator implements PropertySourceLocator{ @Override public PropertySource<?> locate(Environment environment) { CompositePropertySource composite = new CompositePropertySource("configService"); for (PropertySource source : environmentRepository.findOne(name, profiles, label) .getPropertySources()) { composite.addPropertySource(...); } return composite; } }
- PropertySourceBootstrapConfiguration#init负责所有BootStrap配置的加载,所有实现PropertySourceLocator接口的服务都会被调用
CompositePropertySource composite = new CompositePropertySource( BOOTSTRAP_PROPERTY_SOURCE_NAME); for (PropertySourceLocator locator : this.propertySourceLocators) { PropertySource<?> source = locator.locate(environment); composite.addPropertySource(source); }
Client
-
2种高可用方式, Spring-Cloud-Config-Client配置存在两种策略
- 通过ConfigServer获取注册中心地址和其他配置
- 通过注册中心获取ConfigServer地址,然后获得其他配置
参考国内比较成熟的分布式集中配置,比如百度Disconf,携程Apollo等基本都是第1种策略,好处是唯一需要本地写死的是配置中心的参数.
但是Spring Cloud 1.X 并不支持ConfigServer集群的高可用配置。需要单独将spring-cloud-starter-config升级到2.x(可以和其他1.x组件混用)
升级后就可以配多个地址了,如下:
spring:
application:
name: user
cloud:
config:
fail-fast: true
profile: dev
label: master
uri: http://localhost:8761/config, http://localhost:8762/config, http://localhost:8763/config
- 但是网上更多介绍的是第二种方案,这里也贴出来。配置项更多了,需要同时指定configserver和eureka
spring:
application:
name: user
cloud:
config:
fail-fast: true
profile: dev
label: master
discovery:
enabled: true
serviceId: dashboard
eureka:
client:
register-with-eureka: true
fetch-registry: true
serviceUrl:
defaultZone: http://localhost:8761/eureka/,http://localhost2:8762/eureka,http://localhost3:8763/eureka
- 启动过程和ConfigServer注入过程类似
- ConfigServiceBootstrapConfiguration -> ConfigServicePropertySourceLocator -> locate
public PropertySource<?> locate(Environment environment) {
CompositePropertySource composite = new CompositePropertySource("configService");
...
RestTemplate restTemplate = getSecureRestTemplate(properties);
...
Environment result = getRemoteEnvironment(restTemplate...);
...
for (PropertySource source : result.getPropertySources()) {
...
composite.addPropertySource(source);
}
...
}
该服务实现了PropertySourceLocator接口,在PropertySourceBootstrapConfiguration#init中会被注入到bootStrap的context中
方案2比方案1多一个环节,configServer 的 uri 是由注册中心获取的
DiscoveryClientConfigServiceBootstrapConfiguration#refresh
@ConditionalOnProperty(value = "spring.cloud.config.discovery.enabled", matchIfMissing = false)
@Configuration
@EnableDiscoveryClient
public class DiscoveryClientConfigServiceBootstrapConfiguration {
void refresh() {
String serviceId = this.config.getDiscovery().getServiceId();
List<ServiceInstance> serviceInstances = this.instanceProvider
.getConfigServerInstances(serviceId);
for (int i = 0; i < serviceInstances.size(); i++) {
ServiceInstance server = serviceInstances.get(i);
String url = getHomePage(server);
listOfUrls.add(url);
}
...
configClientProperties.setUri(listOfUrls);
}
}