这是我参与更文挑战的第 14 天,活动详情查看: 更文挑战
《配置中心 Spring Cloud Config 详解》系列文章更新,一起在技术的路上精进!本系列文章将会介绍Spring Cloud 中提供了分布式配置中心Spring Cloud Config。应用服务中除了实现系统功能的代码,还需要连接资源和其它应用,经常有很多需要在外部配置的数据去调整应用的行为,如切换不同的数据库,设置功能开关等。随着微服务的不断增加,需要系统具备可伸缩和可扩展性,除此之外就是管理相当多的服务实例的配置数据。在应用的开发阶段由各个服务自治,但是到了生产环境之后会给运维带来很大的麻烦,特别是微服务的规模比较大,配置的更新更为麻烦。为此,系统需要建立一个统一的配置管理中心。
在前面的文章,我们进一步介绍了客户端实现的一些细节。本文将会重点介绍如何通过 HTTP URI 和服务发下的方式来指定配置服务器。
通过 HTTP URI 指定 Config Server
重试注解指定了拦截器的配置configServerRetryInterceptor
,这对象的初始化,在前面已经讲过。ConfigServicePropertySourceLocator
实质是一个属性资源定位器,其主要方法是#locate(Environment environment)
。首先用当前运行应用的环境的application、profile和label替换configClientProperties中的占位符并初始化RestTemplate,然后遍历labels数组直到获取到有效的配置信息,最后还会根据是否快速失败进行重试。主要流程图如下所示:
#locate(Environment environment)
调用#getRemoteEnvironment
方法通过http的方式获取远程服务器上的配置数据。实现过程为,首先替换请求路径path中占位符,然后进行头部headers组装,组装好了就可以发送请求,最后返回结果。
在上面的实现中,我们看到获取到的配置信息存放在CompositePropertySource
,那是如何使用它的呢?这边补充另一个重要的类是PropertySourceBootstrapConfiguration,它实现了ApplicationContextInitializer接口,该接口会在应用上下文刷新之前refresh()
被回调,从而执行初始化操作,应用启动后的调用栈如下:
SpringApplicationBuilder.run() -> SpringApplication.run() -> SpringApplication.createAndRefreshContext() -> SpringApplication.applyInitializers() -> PropertySourceBootstrapConfiguration.initialize()
复制代码
而上述ConfigServicePropertySourceLocator
的locate方法会在PropertySourceBootstrapConfiguration.#initialize
中被调用,从而保证上下文在刷新之前能够拿到必要的配置信息。具体看一下initialize方法:
public class PropertySourceBootstrapConfiguration implements
ApplicationContextInitializer<ConfigurableApplicationContext>, Ordered {
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
CompositePropertySource composite = new CompositePropertySource(
BOOTSTRAP_PROPERTY_SOURCE_NAME);
AnnotationAwareOrderComparator.sort(this.propertySourceLocators);
boolean empty = true;
ConfigurableEnvironment environment = applicationContext.getEnvironment();
for (PropertySourceLocator locator : this.propertySourceLocators) {
PropertySource<?> source = null;
source = locator.locate(environment);
if (source == null) {
continue;
}
composite.addPropertySource(source);
empty = false;
}
if (!empty) {
MutablePropertySources propertySources = environment.getPropertySources();
String logConfig = environment.resolvePlaceholders("${logging.config:}");
LogFile logFile = LogFile.get(environment);
if (propertySources.contains(BOOTSTRAP_PROPERTY_SOURCE_NAME)) {
propertySources.remove(BOOTSTRAP_PROPERTY_SOURCE_NAME);
}
insertPropertySources(propertySources, composite);
reinitializeLoggingSystem(environment, logConfig, logFile);
setLogLevels(environment);
handleIncludedProfiles(environment);
}
}
...
}
复制代码
下面我们看一下,在initialize
方法中进行了哪些操作。
- 根据默认的 AnnotationAwareOrderComparator 排序规则对propertySourceLocators数组进行排序
- 获取运行的环境上下文ConfigurableEnvironment
- 遍历propertySourceLocators时
- 调用 locate 方法,传入获取的上下文environment
- 将source添加到PropertySource的链表中
- 设置source是否为空的标识标量empty
- source不为空的情况,才会设置到environment中
- 返回Environment的可变形式,可进行的操作如addFirst、addLast
- 移除propertySources中的bootstrapProperties
- 根据config server覆写的规则,设置propertySources
- 处理多个active profiles的配置信息
如上过程,即可将从指定的Config Server 拉取的配置信息应用到我们的客户端服务中。
通过服务发现指定Config Server
通过服务发现指定Config Server,Config Client在启动时,首先会通过服务发现找到Config Server,然后从Config Server拉取其相应的配置信息,并用这些远端的属性资源初始化好Spring的环境。
如果你使用了服务发现组件,如Eureka 、Spring Cloud Consul,需要设置 spring.cloud.config.discovery.enabled=true
,因为默认的是HTTP URI的方式,导致客户端应用不能利用服务注册。
所有的客户端应用需要配置正确的服务发现信息。比如使用Spring Cloud Netflix,你需要指定Eureka服务器的地址eureka.client.serviceUrl.defaultZone
。在启动时定位服务注册,这样做的开销是需要额外的网络请求。优点是Config Server实现高可用,避免单点故障。
配置的Config Server,默认serviceId是”configserver”,我们可以通过在客户端中的spring.cloud.config.discovery.serviceId
属性来更改(通常在Config Server中通过spring.application.name配置一个服务)。服务发现的客户端实现支持多种类型的metadata map,比如Eureka的 eureka.instance.metadataMap
。Config Server的一些额外属性,需要配置在服务注册的元数据中,这样客户端才能正确连接。如果Config Server使用了基本的HTTP安全,我们可以配置证书的用户名和密码;或者是Config Server有一个上下文路径,我们可以设置 “configPath”。客户端关于config信息可以配置如下:
eureka:
instance:
...
metadataMap:
user: osufhalskjrtl
password: lviuhlszvaorhvlo5847
configPath: /config
复制代码
下面我们看一下DiscoveryClientConfigServiceBootstrapConfiguration
中配置了哪些启动前的上下文。
@ConditionalOnProperty(value = "spring.cloud.config.discovery.enabled", matchIfMissing = false)
@Configuration
@Import({ UtilAutoConfiguration.class })
@EnableDiscoveryClient
public class DiscoveryClientConfigServiceBootstrapConfiguration {
@Bean
public ConfigServerInstanceProvider configServerInstanceProvider(
DiscoveryClient discoveryClient) {
return new ConfigServerInstanceProvider(discoveryClient);
}
@EventListener(ContextRefreshedEvent.class)
public void startup(ContextRefreshedEvent event) {
refresh();
}
@EventListener(HeartbeatEvent.class)
public void heartbeat(HeartbeatEvent event) {
if (monitor.update(event.getValue())) {
refresh();
}
}
}
复制代码
主要配置了 config client 通过服务发现组件寻找 config server 服务,还配置了两种事件的监听器:上下文刷新事件和心跳事件。在获取到 Config Server 中的配置信息之后,剩余的过程与指定 HTTP URI 方式获取 Config Server 是一样的,在上一篇文章中已经讲解。
小结
本文主要介绍了 Spring Cloud Config 客户端如何通过 HTTP URI 和服务发现的方式来指定配置服务器,以及相关的实现细节。下面的文章将会介绍客户端实现中对于服务端配置信息变更监听的相关实现。