Server
-
如何加載
- 核心接口是EnvironmentRepository,提供配置的讀取
public interface EnvironmentRepository { Environment findOne(String application, String profile, String label); }
- EnvironmentRepository有多種實(shí)現(xiàn),基于JDBC、SVN、GIT等等
- 默認(rèn)情況下,使用的是 MultipleJGitEnvironmentRepository(可以配置多個(gè)地址的GIT數(shù)據(jù)源)
- ConfigServerAutoConfiguration -> EnvironmentRepositoryConfiguration -> DefaultRepositoryConfiguration
@Configuration @ConditionalOnMissingBean(value = EnvironmentRepository.class) class DefaultRepositoryConfiguration { ... @Bean public MultipleJGitEnvironmentRepository defaultEnvironmentRepository(...) throws Exception { return gitEnvironmentRepositoryFactory.build(environmentProperties); } }
- MultipleJGitEnvironmentRepository 代理遍歷每個(gè) 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()); }
其中g(shù)etLocations這部會(huì)從GIT遠(yuǎn)程倉庫同步到本地
JGitEnvironmentRepository#getLocations -> JGitEnvironmentRepository.refresh
public String refresh(String label) {
Git git = createGitClient();
...
checkout(git, label);
...
merge(git, label);
...
resetHard(...)
...
}
- 配置文件加載優(yōu)先級順序(上面的覆蓋下面的)
模式 | 應(yīng)用 |
---|---|
{spring.application.name}-{profile}.properties/yml | 應(yīng)用-環(huán)境-配置 |
{spring.application.name}.properties/yml | 應(yīng)用-全局-配置 |
application-{profile}.properties/yml | 公眾-環(huán)境-配置 |
application.properties/yml | 公眾-全局-配置 |
具體的邏輯參考NativeEnvironmentRepository和ConfigFileApplicationListener
-
如何注入
- ConfigServer本身也可以注入自己的讀取的配置,使得其他服務(wù)可以和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會(huì)調(diào)用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負(fù)責(zé)所有BootStrap配置的加載,所有實(shí)現(xiàn)PropertySourceLocator接口的服務(wù)都會(huì)被調(diào)用
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地址,然后獲得其他配置
參考國內(nèi)比較成熟的分布式集中配置,比如百度Disconf,攜程Apollo等基本都是第1種策略,好處是唯一需要本地寫死的是配置中心的參數(shù).
但是Spring Cloud 1.X 并不支持ConfigServer集群的高可用配置。需要單獨(dú)將spring-cloud-starter-config升級到2.x(可以和其他1.x組件混用)
升級后就可以配多個(gè)地址了,如下:
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
- 但是網(wǎng)上更多介紹的是第二種方案,這里也貼出來。配置項(xiàng)更多了,需要同時(shí)指定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
- 啟動(dòng)過程和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);
}
...
}
該服務(wù)實(shí)現(xiàn)了PropertySourceLocator接口,在PropertySourceBootstrapConfiguration#init中會(huì)被注入到bootStrap的context中
方案2比方案1多一個(gè)環(huán)節(jié),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);
}
}