Nacos config
Nacos 提供用于存储配置和其他元数据的 key/value 存储,为分布式系统中的外部化配置提供服务器端和客户端支持。使用 Spring Cloud Alibaba Nacos Config,可以在 Nacos Server 集中管理你 Spring Cloud 应用的外部属性配置。
Spring Cloud Alibaba Nacos Config 是 Config Server 和 Client 的替代方案,客户端和服务器上的概念与 Spring Environment 和 PropertySource 有着一致的抽象,在特殊的 bootstrap 阶段,配置被加载到 Spring 环境中。当应用程序通过部署管道从开发到测试再到生产时,可以管理这些环境之间的配置,并确保应用程序具有迁移时需要运行的所有内容。
Config相关配置
Nacos 数据模型 Key 由三元组唯一确定, Namespace默认是空串,公共命名空间(public),分组默认是DEFAULT_GROUP
@SpringBootApplication
public class NacosConfigApplication {
public static void main(String[] args) throws InterruptedException {
ConfigurableApplicationContext applicationContext = SpringApplication.run(NacosConfigApplication.class, args);
while(true) {
//当动态配置刷新时,会更新到 Enviroment中,因此这里每隔一秒中从Enviroment中获取配置
String userName = applicationContext.getEnvironment().getProperty("common.name");
String userAge = applicationContext.getEnvironment().getProperty("common.age");
System.err.println("common name :" + userName + "; age: " + userAge);
TimeUnit.SECONDS.sleep(1);
}
}
}
支持profile粒度的配置
spring.cloud.starter.alibaba.nacos.config 在加载配置的时候,不仅仅加载了以 dataid 为
${spring.application.name}.${file-extension:properties} 为前缀的基础配置,还加载了dataid为
${spring.application.name}-${profile}.${file-extension:properties} 的基础配置。在日常开发中如果遇到多套环境下的不同配置,可以通过Spring 提供的 ${spring.profiles.active} 这个配置项来配置。
spring.profiles.active=dev
支持自定义 namespace 的配置
用于进行租户粒度的配置隔离。不同的命名空间下,可以存在相同的 Group 或 Data ID 的配置。Namespace 的常用场景之一是不同环境的配置的区分隔离,例如开发测试环境和生产环境的资源(如配置、服务)隔离等。 在没有明确指定${spring.cloud.nacos.config.namespace} 配置的情况下, 默认使用的是 Nacos 上 Public 这个namespace。如果需要使用自定义的命名空间,可以通过以下配置来实现:
spring.cloud.nacos.config.namespace=71bb9785‐231f‐4eca‐b4dc‐6be446e12ff8
支持自定义 Group 的配置
Group是组织配置的维度之一。通过一个有意义的字符串(如 Buy 或 Trade )对配置集进行分组,从而区分 Data ID 相同的配置集。当您在 Nacos 上创建一个配置时,如果未填写配置分组的名称,则配置分组的名称默认采用DEFAULT_GROUP 。配置分组的常见场景:不同的应用或组件使用了相同的配置类型,如 database_url 配置和MQ_topic 配置。
在没有明确指定 ${spring.cloud.nacos.config.group} 配置的情况下,默认是DEFAULT_GROUP 。如果需要自定义自己的Group,可以通过以下配置来实现:
spring.cloud.nacos.config.group=DEVELOP_GROUP
支持自定义扩展的 Data Id 配置
Data ID 是组织划分配置的维度之一。Data ID 通常用于组织划分系统的配置集。一个系统或者应用可以包含多个配置集,每个配置集都可以被一个有意义的名称标识。Data ID 通常采用类 Java 包(如 com.taobao.tc.refund.log.level)的命名规则保证全局唯一性。此命名规则非强制。
# 自定义 Data Id 的配置
#不同工程的通用配置 支持共享的 DataId
spring.cloud.nacos.config.sharedConfigs[0].data‐id= common.yaml
spring.cloud.nacos.config.sharedConfigs[0].group=REFRESH_GROUP
spring.cloud.nacos.config.sharedConfigs[0].refresh=true
# config external configuration
# 支持一个应用多个 DataId 的配置
spring.cloud.nacos.config.extensionConfigs[0].data‐id=ext‐config‐common01.properties
spring.cloud.nacos.config.extensionConfigs[0].group=REFRESH_GROUP
spring.cloud.nacos.config.extensionConfigs[0].refresh=true
spring.cloud.nacos.config.extensionConfigs[1].data‐id=ext‐config‐common02.properties
spring.cloud.nacos.config.extensionConfigs[1].group=REFRESH_GROUP
spring.cloud.nacos.config.extensionConfigs[1].refresh=true
可以看到:
- 通过
spring.cloud.nacos.config.extension-configs[n].data-id的配置方式来支持多个Data Id的配置。 - 通过
spring.cloud.nacos.config.extension-configs[n].group的配置方式自定义Data Id所在的组,不明确配置的话,默认是 DEFAULT_GROUP。 - 通过
spring.cloud.nacos.config.extension-configs[n].refresh的配置方式来控制该Data Id在配置变更时,是否支持应用中可动态刷新, 感知到最新的配置值。默认是不支持的。
Note
- 多个 Data Id 同时配置时,他的优先级关系是
spring.cloud.nacos.config.extension-configs[n].data-id其中 n 的值越大,优先级越高。 spring.cloud.nacos.config.extension-configs[n].data-id的值必须带文件扩展名,文件扩展名既可支持 properties,又可以支持 yaml/yml。 此时spring.cloud.nacos.config.file-extension的配置对自定义扩展配置的 Data Id 文件扩展名没有影响。
所以,通过自定义扩展的 Data Id 配置,既可以解决多个应用间配置共享的问题,又可以支持一个应用有多个配置文件。
为了更加清晰的在多个应用间配置共享的 Data Id ,可以通过以下的方式来配置:
# 配置支持共享的 Data Id
spring.cloud.nacos.config.shared-configs[0].data-id=common.yaml
# 配置 Data Id 所在分组,缺省默认 DEFAULT_GROUP
spring.cloud.nacos.config.shared-configs[0].group=GROUP_APP1
# 配置Data Id 在配置变更时,是否动态刷新,缺省默认 false
spring.cloud.nacos.config.shared-configs[0].refresh=true
可以看到:
- 通过
spring.cloud.nacos.config.shared-configs[n].data-id来支持多个共享 Data Id 的配置。 - 通过
spring.cloud.nacos.config.shared-configs[n].group来配置自定义 Data Id 所在的组,不明确配置的话,默认是DEFAULT_GROUP。 - 通过
spring.cloud.nacos.config.shared-configs[n].refresh来控制该Data Id在配置变更时,是否支持应用中动态刷新,默认false。
配置的优先级
Spring Cloud Alibaba Nacos Config 目前提供了三种配置能力从 Nacos 拉取相关的配置。
- A: 通过
spring.cloud.nacos.config.shared-configs支持多个共享 Data Id 的配置 - B: 通过
spring.cloud.nacos.config.ext-config[n].data-id的方式支持多个扩展 Data Id 的配置 - C: 通过内部相关规则(应用名、应用名+ Profile )自动生成相关的 Data Id 配置
当三种方式共同使用时,他们的一个优先级关系是: A < B < C
优先级从高到低:
nacos.config.product.yaml精准配置nacos.config.yaml同工程不同环境的通用配置ext.config不同工程 扩展配置shared.dataids不同工程通用配置:common2.yml > common1.yml
@RefreshScope
@Value注解可以获取到配置中心的值,但是无法动态感知修改后的值,需要利用@RefreshScope注解
@RestController
@RefreshScope
public class TestController {
@Value("${common.age}")
private String age;
@GetMapping("/common")
public String hello() {
return age;
}
}
完全关闭配置
通过设置 spring.cloud.nacos.config.enabled = false 来完全关闭 Spring Cloud Nacos Config。
配置中心的架构
nacos config client源码分析
获取配置
获取配置的主要方法是 NacosConfigService 类的 getConfig 方法,通常情况下该方法直接从本地文件中取得配置的值,如果本地文件不存在或者内容为空,则再通过 HTTP GET 方法从远端拉取配置,并保存到本地快照中。当通过 HTTP 获取远端配置时,Nacos 提供了两种熔断策略,一是超时时间,二是最大重试次数,默认重试三次。
注册监听器
配置中心客户端会通过对配置项注册监听器达到在配置项变更的时候执行回调的功能。
NacosConfigService#getConfigAndSignListener
ConfigService#addListener
Nacos 可以通过以上方式注册监听器,它们内部的实现均是调用 ClientWorker 类的 addCacheDataIfAbsent。其中 CacheData 是一个维护配置项和其下注册的所有监听器的实例,所有的 CacheData 都保存在 ClientWorker 类中的原子 cacheMap 中,其内部的核心成员有
配置长轮询
ClientWorker 通过其下的两个线程池完成配置长轮询的工作,一个是单线程的 executor,每隔 10ms 按照每 3000 个配置项为一批次捞取待轮询的 cacheData 实例,将其包装成为一个 LongPollingTask 提交进入第二个线程池 executorService 处理。
nacos config server源码分析
配置dump
服务端启动时就会依赖 DumpService 的 init 方法,从数据库中 load 配置存储在本地磁盘上,并将一些重要的元信息例如 MD5 值缓存在内存中。服务端会根据心跳文件中保存的最后一次心跳时间,来判断到底是从数据库 dump 全量配置数据还是部分增量配置数据(如果机器上次心跳间隔是 6h 以内的话)。
全量 dump 当然先清空磁盘缓存,然后根据主键 ID 每次捞取一千条配置刷进磁盘和内存。增量 dump 就是捞取最近六小时的新增配置(包括更新的和删除的),先按照这批数据刷新一遍内存和文件,再根据内存里所有的数据全量去比对一遍数据库,如果有改变的再同步一次,相比于全量 dump 的话会减少一定的数据库 IO 和磁盘 IO 次数。
配置发布
发布配置的代码位于 ConfigController#publishConfig中。集群部署,请求一开始也只会打到一台机器,这台机器将配置插入Mysql中进行持久化。服务端并不是针对每次配置查询都去访问 MySQL ,而是会依赖 dump 功能在本地文件中将配置缓存起来。因此当单台机器保存完毕配置之后,需要通知其他机器刷新内存和本地磁盘中的文件内容,因此它会发布一个名为 ConfigDataChangeEvent 的事件,这个事件会通过 HTTP 调用通知所有集群节点(包括自身),触发本地文件和内存的刷新。
处理长轮询
客户端会有一个长轮询任务,拉取服务端的配置变更,服务端处理逻辑在LongPollingService类中,其中有一个 Runnable 任务名为ClientLongPolling,服务端会将受到的轮询请求包装成一个 ClientLongPolling 任务,该任务持有一个 AsyncContext 响应对象,通过定时线程池延后 29.5s 执行。比客户端 30s 的超时时间提前 500ms 返回是为了最大程度上保证客户端不会因为网络延时造成超时。
总结
Nacos Client
- 客户端启动会读取bootstrap.properties
- 然后依次读取加载到的配置,shared优先级最低,文件扩展名优先级最高(加载顺序:
文件名-profile.文件扩展名 > 文件名.文件扩展名 > 文件名(微服务名称)),注意:优先级高的配置会覆盖优先级低的配置。 - 客户端读取配置,会先读本地文件,如果有直接返回。没有则拉取远程的配置,注意不是读取的mysql数据库的配置,而是读取远程本地磁盘文件。
- 拉取到配置后,完成初始化
- 容器启动后,调用事件监听器,为每一个dataId绑定注册一个监听器,监听
ApplicationReadyEvent事件,动态感知服务端配置的变更。
Nacos Server
- nacos服务启动,DumpService会全量Dump配置信息(将数据库中所有ConfigInfo查询出来并写入本地磁盘文件中)。
- 通过管理台点击发布一个配置,首先配置会持久化到mysql,同时也会写入本地磁盘文件(全量更新或增量更新),
- 无论全量dump或增量dump都会更新MD5,缓存配置信息的MD5到内存中,更新之后发布配置变更事件(触发
ConfigDataChangeEvent事件)