之前这篇文章介绍了Nacos Config Client的实现,今天继续聊下Nacos Config Client与Spring Cloud的结合。

官方示例

引入依赖

dependencyManagement中添加如下配置。

1
2
3
4
5
6
7
8
9
10
11
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>0.2.1.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>

引入 Nacos Config Starter。

1
2
3
4
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>

配置Nacos Config元数据

bootstrap.properties配置文件中配置Nacos Config元数据。

1
2
spring.application.name=nacos-config-example
spring.cloud.nacos.config.server-addr=127.0.0.1:8848

使用配置

应用会从Nacos Config中获取dataId为nacos-config-example.properties,group为DEFAULT_GROUP的配置,并添加在 Spring Environment 的 PropertySources 中。可以使用 @Value 注解来将对应的配置注入到相应字段,并添加 @RefreshScope 打开动态刷新功能。

1
2
3
4
5
6
7
8
9
@RefreshScope
class SampleController {

@Value("${user.name}")
String userName;

@Value("${user.age}")
int age;
}

可选配置

现把目前支持的配置项总结如下,配置前缀为spring.cloud.nacos.config.

key 默认值 说明
server-addr 服务端地址
prefix {spring.application.name} DataId前缀
group DEFAULT_GROUP Group
file-extension properties dataID后缀及内容文件格式
encode UTF-8 配置的编码
timeout 3000 获取配置的超时时间,单位为 ms
namespace 配置的命名空间
access-key 阿里云AccessKey
secret-key 阿里云SecretKey
context-path 服务端 API 的相对路径
endpoint 服务端接入点
cluster-name 集群名
name 如果未配置prefix,会把该值当prefix
sharedDataids 共享配置的dataId,逗号分隔
refreshableDataids 共享配置中允许自动刷新的dataId
extConfig 额外配置项
refresh.enabled true 是否开启监听和自动刷新

spring.factories

在聊Nacos Naming与Spring Cloud的结合时,曾简单介绍了自动装配的背景知识。Nacos Config的spring.factories配置如下。

1
2
3
4
5
6
7
org.springframework.cloud.bootstrap.BootstrapConfiguration=\
org.springframework.cloud.alibaba.nacos.NacosConfigBootstrapConfiguration
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.cloud.alibaba.nacos.NacosConfigAutoConfiguration,\
org.springframework.cloud.alibaba.nacos.endpoint.NacosConfigEndpointAutoConfiguration
org.springframework.boot.diagnostics.FailureAnalyzer=\
org.springframework.cloud.alibaba.nacos.diagnostics.analyzer.NacosConnectionFailureAnalyzer

其中有一些之前没有提过的类型,在此简单介绍一下,想要详细了解可以参考我给出的链接。

FailureAnalyzer

FailureAnalyzer是用于启动失败时,向控制台输出一些有用的信息以帮助排查问题的。简单来看下Nacos的实现。

1
2
3
4
5
6
7
8
9
10
public class NacosConnectionFailureAnalyzer
extends AbstractFailureAnalyzer<NacosConnectionFailureException> {

@Override
protected FailureAnalysis analyze(Throwable rootFailure,
NacosConnectionFailureException cause) {
return new FailureAnalysis("Application failed to connect to Nacos server",
"check your nacos server config", cause);
}
}

当连接Nacos服务端失败抛出NacosConnectionFailureException时,会向控制台输出如代码中的信息。但是,目前版本中并没有任何地方会抛出NacosConnectionFailureException,因此目前FailureAnalysis实际是无效的,可能会在后续版本中增加吧。

可参考:
FailureAnalysis Example
Spring Boot Document

BootstrapConfiguration

Spring Cloud引入了Bootstrap Application ContextBootstrap Application Context作为应用的Main Application Context的父Context,优先于Main Application Context初始化。不同于Main Application Context加载application.properties(或application.yml),Bootstrap Application Context会使用bootstrap.properties(或bootstrap.yml),所以在上面的示例中可以看到Nacos Config的相关配置放在了bootstrap.properties中。

spring.factoriesBootstrapConfiguration类似于EnableAutoConfiguration等配置,但BootstrapConfiguration配置的类注册的Bean会在Main Application Context初始化之前创建。

可参考:
Bootstrap Application Context
Customizing the Bootstrap Configuration

Beans

接下来我们看一下Nacos Config注册了哪些Bean。如spring.factories所示,Nacos Config有NacosConfigBootstrapConfigurationNacosConfigAutoConfigurationNacosConfigEndpointAutoConfigurationNacosConnectionFailureAnalyzer四个配置类,最后一个NacosConnectionFailureAnalyzer上面已经简单分析过并且目前实质也没有用到,这里就不再重复。重点分析其他三个配置类。

NacosConfigBootstrapConfiguration

NacosConfigBootstrapConfiguration注册了2个Bean,NacosConfigPropertiesNacosPropertySourceLocator

1
2
3
4
5
6
7
8
9
10
11
@Bean
@ConditionalOnMissingBean
public NacosConfigProperties nacosConfigProperties() {
return new NacosConfigProperties();
}

@Bean
public NacosPropertySourceLocator nacosPropertySourceLocator(
NacosConfigProperties nacosConfigProperties) {
return new NacosPropertySourceLocator(nacosConfigProperties);
}

NacosConfigProperties即对应上文提到的那些可选配置。

NacosPropertySourceLocator实现了PropertySourceLocator接口,PropertySourceLocator接口只有一个locate(Environment environment)方法,返回值为PropertySource,Spring会把返回的PropertySource添加到Spring的Environment中,所有我们才可以在程序中通过@Value使用这些配置。可参考:Customizing the Bootstrap Property Sources

Nacos Config的实现如下。

1
2
3
4
5
6
7
8
9
10
11
12
@Override
public PropertySource<?> locate(Environment env) {
// 省略...
CompositePropertySource composite = new CompositePropertySource(
NACOS_PROPERTY_SOURCE_NAME);

loadSharedConfiguration(composite);
loadExtConfiguration(composite);
loadApplicationConfiguration(composite, dataIdPrefix, nacosConfigProperties, env);

return composite;
}

Nacos Config返回的是一个CompositePropertySourceCompositePropertySource支持添加多个PropertySource,获取配置时会依照添加的顺序依次获取。
Nacos Config的配置可以分为3种:SharedConfigurationExtConfigurationApplicationConfiguration
代码的实现虽然是按照SharedConfiguration -> ExtConfiguration -> ApplicationConfiguration的顺序依次加载的,但向CompositePropertySource添加时调用的是addFirstPropertySource()方法,会把配置添加到首位。
因此,配置使用的实际顺序是ApplicationConfiguration -> ExtConfiguration -> SharedConfiguration

而这几种配置的获取实际就是利用了Nacos Config Client提供的API,调用String getConfig(String dataId, String group, long timeoutMs)方法从Nacos服务端获取,

关于这几个不同配置类型的使用大家还可以参考didi大佬的教程——Spring Cloud Alibaba基础教程:Nacos配置的多文件加载与共享配置

NacosConfigAutoConfiguration

NacosConfigAutoConfiguration中注册的几个Bean主要是用于动态刷新配置。关键的Bean是NacosContextRefresher。之前介绍Nacos Config Client时介绍过支持设置Listener来监听配置的变化,这里的动态刷新也是利用这个来实现的。

注册监听

NacosContextRefresher会监听ApplicationReadyEvent事件,注册Nacos各个配置项的监听是在接收到ApplicationReadyEvent事件时进行的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@Override
public void onApplicationEvent(ApplicationReadyEvent event) {
// many Spring context
if (this.ready.compareAndSet(false, true)) {
this.registerNacosListenersForApplications();
}
}

private void registerNacosListenersForApplications() {
if (refreshProperties.isEnabled()) {
for (NacosPropertySource nacosPropertySource : NacosPropertySourceRepository
.getAll()) {

if (!nacosPropertySource.isRefreshable()) {
continue;
}

String dataId = nacosPropertySource.getDataId();
registerNacosListener(nacosPropertySource.getGroup(), dataId);
}
}
}

从代码可以看到只会对开启了动态刷新的dataId注册监听。对于不同的配置类型策略不同。

  • ApplicationConfiguration:默认开启刷新。
  • ExtConfiguration:默认关闭刷新,需在配置项中主动开启,例如:spring.cloud.nacos.config.ext-config[0].refresh=true
  • SharedConfiguration:需使用spring.cloud.nacos.config.refreshableDataids配置项明确指定开启刷新的dataId。

如果需要关闭所有的动态刷新,可以将spring.cloud.nacos.config.refresh.enabled设置为false。

Listener

注册的Listener代码如下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@Override
public void receiveConfigInfo(String configInfo) {
refreshCountIncrement();
String md5 = "";
if (!StringUtils.isEmpty(configInfo)) {
try {
MessageDigest md = MessageDigest.getInstance("MD5");
md5 = new BigInteger(1, md.digest(configInfo.getBytes("UTF-8")))
.toString(16);
}
catch (NoSuchAlgorithmException | UnsupportedEncodingException e) {
log.warn("[Nacos] unable to get md5 for dataId: " + dataId, e);
}
}
refreshHistory.add(dataId, md5);
applicationContext.publishEvent(
new RefreshEvent(this, null, "Refresh Nacos config"));
if (log.isDebugEnabled()) {
log.debug("Refresh Nacos config group " + group + ",dataId" + dataId);
}
}

刷新配置的关键代码是applicationContext.publishEvent(new RefreshEvent(this, null, "Refresh Nacos config"));,分发了一个RefreshEvent事件。
之后就交由Spring Cloud处理了,RefreshEvent事件与调用/refresh的Endpoint是一样的,会重建Environment,从而会再次调用PropertySourceLocator接口的locate方法,Nacos就会把更新后的PropertySource返回。其他信息可以参考Refresh Scope相关文档或其他文章。

NacosConfigEndpointAutoConfiguration

NacosConfigEndpointAutoConfiguration用于暴露Endpoint,path为/nacos_config【Spring Boot 1.x】或/actuator/nacos-config【Spring Boot 2.x】返回的信息包括3个部分:

  • NacosConfigProperties:Nacos Config Starter 本身的配置。
  • Sources:表示此客户端从哪些 Nacos Config 配置项中获取了信息。
  • RefreshHistory:表示动态刷新的历史记录,最多保存20条,

见官网文档的示例:

actuator
actuator

总结

Nacos Config借助Spring提供的特性实现了配置中心所需的两大功能:

  • 获取配置:借助PropertySourceLocator将配置添加到Spring Environment中。
  • 动态刷新:借助RefreshEvent刷新Spring Environment。