源码解析-Nacos配置中心
一、Nacos配置中心实战
1.1 快速开始
Group : DEFAULT_GROUP
配置格式: Properties
配置内容: user.name=shiwn
user.age=100
注意:Data Id是以 properties (默认的文件扩展名方式)为扩展名,名称可自由定义(如:redis-config.properties,mybaties.properties)
1.2 搭建nacos-config服务
通过 Nacos Server 和 spring-cloud-starter-alibaba-nacos-config 实现配置的动态变更
<!--nacos配置中心--> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId> </dependency>
spring.application.name=nacos-config # 配置中心地址 spring.cloud.nacos.config.server-addr=127.0.0.1:8848
3) 启动服务,测试微服务是否使用配置中心的配置
@SpringBootApplication public class ServiceNacosConfigApplication { public static void main(String[] args) { ConfigurableApplicationContext applicationContext = SpringApplication.run(ServiceNacosConfigApplication.class, args); // 当动态配置刷新时,会更新到 Enviroment中,因此这里每隔3秒中从Enviroment中获取配置 while (true) { String userName = applicationContext.getEnvironment().getProperty("common.name"); String userAge = applicationContext.getEnvironment().getProperty("common.age"); System.err.println("common name :" + userName + "; age: " + userAge); try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); } } } }
启动项目,可以看到如下输出结果:读取了 nacos server 中的配置
1.3 Config相关配置
支持配置的动态更新
代码参考1.2-3
Note:你可以通过配置 spring.cloud.nacos.config.refresh.enabled=false 来关闭动态刷新支持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=develop
Note:${spring.profiles.active} 当通过配置文件来指定时必须放在 bootstrap.properties 文件中。
支持自定义 namespace 的配置
用于进行租户粒度的配置隔离。不同的命名空间下,可以存在相同的 Group 或 Data ID 的配置。Namespace 的常用场景之一是不同环境的配置的区分隔离,例如开发测试环境和生产环境的资源(如配置、服务)隔离等。
在没有明确指定 ${spring.cloud.nacos.config.namespace} 配置的情况下, 默认使用的是 Nacos 上 Public 这个namespae。如果需要使用自定义的命名空间,可以通过以下配置来实现:
spring.cloud.nacos.config.namespace=b3404bc0-d7dc-4855-b519-570ed34b62d7
Note:该配置必须放在 bootstrap.properties 文件中。此外 spring.cloud.nacos.config.namespace
的值是 namespace 对应的 id,id 值可以在 Nacos 的控制台获取。并且在添加配置时注意不要选择其他的 namespae,否则将会导致读取不到正确的配置。
支持自定义 Group 的配置
在没有明确指定 ${spring.cloud.nacos.config.group}
配置的情况下, 默认使用的是 DEFAULT_GROUP 。如果需要自定义自己的 Group,可以通过以下配置来实现:
spring.cloud.nacos.config.group=DEVELOP_GROUP
Note:该配置必须放在 bootstrap.properties 文件中。并且在添加配置时 Group 的值一定要和 spring.cloud.nacos.config.group 的配置值一致。
支持自定义扩展的 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
# 配置支持一个应用多个 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
1.4 配置的优先级
Spring Cloud Alibaba Nacos Config 目前提供了三种配置能力从 Nacos 拉取相关的配置。
-
A: 通过
spring.cloud.nacos.config.shared-configs[n].data-id
支持多个共享 Data Id 的配置 -
B: 通过
spring.cloud.nacos.config.extension-configs[n].data-id
的方式支持多个扩展 Data Id 的配置 -
C: 通过内部相关规则(应用名、应用名+ Profile )自动生成相关的 Data Id 配置
当三种方式共同使用时,他们的一个优先级关系是:A < B < C
1.5 @RefreshScope
@Value注解可以获取到配置中心的值,但是无法动态感知修改后的值,需要利用@RefreshScope注解:
@RestController @RefreshScope public class TestController { @Value("${common.age}") private String age; @GetMapping("/common") public String hello() { return age; } }
二、Nacos配置中心架构剖析
2.1 配置中心的架构
配置中心使用demo
@SpringBootApplication public class ServiceNacosConfigApplication { public static void main(String[] args) { try { String serverAddr = "localhost"; String dataId = "nacos-config-demo.yaml"; String group = "DEFAULT_GROUP"; Properties properties = new Properties(); properties.put(PropertyKeyConst.SERVER_ADDR, serverAddr); //获取配置服务 ConfigService configService = NacosFactory.createConfigService(properties); //获取配置 String content = configService.getConfig(dataId, group, 5000); System.out.println(content); //注册监听器 configService.addListener(dataId, group, new Listener() { @Override public void receiveConfigInfo(String configInfo) { System.out.println("===recieve:" + configInfo); } @Override public Executor getExecutor() { return null; } }); //发布配置 //boolean isPublishOk = configService.publishConfig(dataId, group, "content"); //System.out.println(isPublishOk); //发送properties格式 configService.publishConfig(dataId, group, "common.age=30", ConfigType.PROPERTIES.getType()); Thread.sleep(3000); content = configService.getConfig(dataId, group, 5000); System.out.println(content); } catch (Exception e) { } } }
在nacos服务端查看发布的配置
2.2 nacos config client源码分析
2.2.1 获取配置
获取配置的主要方法是 NacosConfigService 类的 getConfig 方法,通常情况下该方法直接从本地文件中取得配置的值,如果本地文件不存在或者内容为空,则再通过 HTTP GET 方法从远端拉取配置,并保存到本地快照中。当通过 HTTP 获取远端配置时,Nacos 提供了两种熔断策略,一是超时时间,二是最大重试次数,默认重试三次。
2.2.2 注册监听器
配置中心客户端会通过对配置项注册监听器达到在配置项变更的时候执行回调的功能。
String getConfigAndSignListener(String dataId, String group, long timeoutMs, Listener listener) throws NacosException; void addListener(String dataId, String group, Listener listener) throws NacosException;
2.2.3 配置长轮询
ClientWorker 通过其下的两个线程池完成配置长轮询的工作,一个是单线程的 executor,每隔 10ms 按照每 3000 个配置项为一批次捞取待轮询的 cacheData 实例,将其包装成为一个 LongPollingTask 提交进入第二个线程池 executorService 处理。
2.3 nacos config server源码分析
2.3.1 配置dump
2.3.2 配置发布
发布配置的代码位于 ConfigController#publishConfig中。集群部署,请求一开始也只会打到一台机器,这台机器将配置插入Mysql中进行持久化。服务端并不是针对每次配置查询都去访问 MySQL ,而是会依赖 dump 功能在本地文件中将配置缓存起来。因此当单台机器保存完毕配置之后,需要通知其他机器刷新内存和本地磁盘中的文件内容,因此它会发布一个名为 ConfigDataChangeEvent 的事件,这个事件会通过 HTTP 调用通知所有集群节点(包括自身),触发本地文件和内存的刷新。
2.3.3 处理长轮询
客户端会有一个长轮询任务,拉取服务端的配置变更,服务端处理逻辑在LongPollingService类中,其中有一个 Runnable 任务名为ClientLongPolling,服务端会将受到的轮询请求包装成一个 ClientLongPolling 任务,该任务持有一个 AsyncContext 响应对象,通过定时线程池延后 29.5s 执行。比客户端 30s 的超时时间提前 500ms 返回是为了最大程度上保证客户端不会因为网络延时造成超时。