Spring Cloud Alibaba Nacos Config配置中心源码如何深度解析?
- 内容介绍
- 文章标签
- 相关推荐
本文共计1668个文字,预计阅读时间需要7分钟。
前言+配置文件虽然家家户户都很熟悉,无论什么架构都离不开配置,当然Spring Boot已经大大简化了配置,服务环境也还不错,但管理配置起来还是相当麻烦,每次改完配置都需要重启服务。
前言
配置文件想必大家都很熟悉,无论什么架构都离不开配置,虽然spring boot已经大大简化了配置,但服务环境也好几个,管理配置起来还是很麻烦,并且每次改完配置都需要重启服务,nacos config出现就解决了这些问题,它把配置统一放到服务进行管理,客户端这边进行有需要的获取,可以实时对配置进行修改和发布
如何使用Nacos Config
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.3.12.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-alibaba-dependencies</artifactId> <version>2.2.6.RELEASE</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement>Nacos Config的使用方式:Nacos Config配置中心
spring boot启动容器如何加载nacos config配置文件
这个配置作用是spring在启动之间准备上下文时会启用这个配置来导入nacos相关配置文件,为后续容器启动做准备。
@Configuration(proxyBeanMethods = false) @ConditionalOnProperty(name = "spring.cloud.nacos.config.enabled", matchIfMissing = true) public class NacosConfigBootstrapConfiguration { public NacosConfigBootstrapConfiguration() { } @Bean @ConditionalOnMissingBean // 读取nacos相关配置 public NacosConfigProperties nacosConfigProperties() { return new NacosConfigProperties(); } @Bean @ConditionalOnMissingBean //实例化NacosConfigManager,创建ConfigService,ConfigService是配置读取更新的核心类 public NacosConfigManager nacosConfigManager(NacosConfigProperties nacosConfigProperties) { return new NacosConfigManager(nacosConfigProperties); } @Bean //spring初始化时会调用NacosPropertySourceLocator.locate方法, //nacos通过此方法加载nacos-server配置 public NacosPropertySourceLocator nacosPropertySourceLocator(NacosConfigManager nacosConfigManager) { return new NacosPropertySourceLocator(nacosConfigManager); } }-
NacosConfigProperties:对应我们上面在bootstrap.properties中对应的配置信息
-
NacosConfigManager:持有NacosConfigProperties和ConfigService,ConfigService用来查询发布配置的相关接口。
-
NacosPropertySourceLocator:它实现了PropertySourceLocator ,spring boot启动时调用PropertySourceLocator.locate(env)用来加载配置信息,下面来看相关源码:
至此我们在nacos上配置的properties和yaml文件都载入到spring配置文件中来了,后面可通过context.Environment.getProperty(propertyName)来获取相关配置信息
配置如何随spring boot加载进来我们说完了,接来下来看修改完配置后如何实时刷新
nacos config动态刷新
当nacos config更新后,根据配置中的refresh属性来判断是否刷新配置,配置如下
spring.cloud.nacos.config.ext-config[0].refresh=true首先spring.factories 配置了EnableAutoConfiguration=NacosConfigAutoConfiguration,NacosConfigAutoConfiguration配置类会注入一个NacosContextRefresher,它首先监听了ApplicationReadyEvent,然后注册一个nacos listener用来监听nacos config配置修改后发布一个spring refreshEvent用来刷新配置和应用
public class NacosContextRefresher implements ApplicationListener<ApplicationReadyEvent>, ApplicationContextAware { private final NacosRefreshHistory nacosRefreshHistory; private ApplicationContext applicationContext; private final ConfigService configService; private Map<String, Listener> listenerMap = new ConcurrentHashMap<>(16); @Override public void onApplicationEvent(ApplicationReadyEvent event) { // 只注册一次 if (this.ready.compareAndSet(false, true)) { this.registerNacosListenersForApplications(); } } private void registerNacosListenersForApplications() { if (isRefreshEnabled()) { for (NacosPropertySource propertySource : NacosPropertySourceRepository .getAll()) { // 对应刚才所说的配置 需要配置文件是否需要刷新 if (!propertySource.isRefreshable()) { continue; } String dataId = propertySource.getDataId(); // 注册nacos监听器 registerNacosListener(propertySource.getGroup(), dataId); } } } private void registerNacosListener(final String groupKey, final String dataKey) { String key = NacosPropertySourceRepository.getMapKey(dataKey, groupKey); Listener listener = listenerMap.computeIfAbsent(key, lst -> new AbstractSharedListener() { @Override public void innerReceive(String dataId, String group, String configInfo) { refreshCountIncrement(); // 添加刷新记录 nacosRefreshHistory.addRefreshRecord(dataId, group, configInfo); // todo feature: support single refresh for listening // 发布一个spring refreshEvent事件 对应监听器为RefreshEventListener 该监听器会完成配置的更新应用 applicationContext.publishEvent( new RefreshEvent(this, null, "Refresh Nacos config")); if (log.isDebugEnabled()) { log.debug(String.format( "Refresh Nacos config group=%s,dataId=%s,configInfo=%s", group, dataId, configInfo)); } } }); try { configService.addListener(dataKey, groupKey, listener); } catch (NacosException e) { log.warn(String.format( "register fail for nacos listener ,dataId=[%s],group=[%s]", dataKey, groupKey), e); } } }我们说完了nacos config动态刷新,那么肯定有对应的动态监听,nacos config会监听nacos server上配置的更新状态
nacos config动态监听
一般来说客户端和服务端数据交互无非就两种方式
- pull:客户端主动从服务器拉取数据
- push: 由服务端主动向客户端推送数据
这两种模式优缺点各不一样,pull模式需要考虑的是什么时候向服务端拉取数据,可能会存在数据延迟问题,而push模式需要客户端和服务端维护一个长连接,如果客户端较多会给服务端造成压力,但它的实时性会更好。
nacos采用的是pull模式,但它作了优化,可以看做是pull+push,客户端会轮询向服务端发出一个长连接请求,这个长连接最多30s就会超时,服务端收到客户端的请求会先判断当前是否有配置更新,有则立即返回。
如果没有服务端会将这个请求拿住“hold”29.5s加入队列,最后0.5s再检测配置文件无论有没有更新都进行正常返回,但等待的29.5s期间有配置更新可以提前结束并返回,下面会在源码中讲解具体怎么处理的
Nacos Config动态刷新机制
nacos config 动态刷新流程图
nacos client处理
动态监听的发起是在ConfigService的实现类NacosConfigService的构造方法中,它是对外nacos config api接口,在之前加载配置文件和NacosContextRefresher构造方法中都会获取或创建
@Order(0) public class NacosPropertySourceLocator implements PropertySourceLocator { public NacosPropertySourceLocator(NacosConfigManager nacosConfigManager) { this.nacosConfigManager = nacosConfigManager; this.nacosConfigProperties = nacosConfigManager.getNacosConfigProperties(); } public PropertySource<?> locate(Environment env) { this.nacosConfigProperties.setEnvironment(env); ConfigService configService = this.nacosConfigManager.getConfigService(); //......省略其他 } } public class NacosContextRefresher implements ApplicationListener<ApplicationReadyEvent>, ApplicationContextAware { public NacosContextRefresher(NacosConfigManager nacosConfigManager, NacosRefreshHistory refreshHistory) { this.nacosConfigProperties = nacosConfigManager.getNacosConfigProperties(); this.nacosRefreshHistory = refreshHistory; this.configService = nacosConfigManager.getConfigService(); this.isRefreshEnabled = this.nacosConfigProperties.isRefreshEnabled(); } }这里都会先判断是否已经创建了ConfigServer,没有则实例化一个NacosConfigService,来看它的构造函数
/***************************************** NacosConfigService *****************************************/ public class NacosConfigService implements ConfigService { /** * www.cnblogs.com/zzz-blogs/p/14249126.htmlblog.csdn.net/qw852328952/article/details/112142451
blog.csdn.net/xingxinggua9620/article/details/113563116
本文共计1668个文字,预计阅读时间需要7分钟。
前言+配置文件虽然家家户户都很熟悉,无论什么架构都离不开配置,当然Spring Boot已经大大简化了配置,服务环境也还不错,但管理配置起来还是相当麻烦,每次改完配置都需要重启服务。
前言
配置文件想必大家都很熟悉,无论什么架构都离不开配置,虽然spring boot已经大大简化了配置,但服务环境也好几个,管理配置起来还是很麻烦,并且每次改完配置都需要重启服务,nacos config出现就解决了这些问题,它把配置统一放到服务进行管理,客户端这边进行有需要的获取,可以实时对配置进行修改和发布
如何使用Nacos Config
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.3.12.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-alibaba-dependencies</artifactId> <version>2.2.6.RELEASE</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement>Nacos Config的使用方式:Nacos Config配置中心
spring boot启动容器如何加载nacos config配置文件
这个配置作用是spring在启动之间准备上下文时会启用这个配置来导入nacos相关配置文件,为后续容器启动做准备。
@Configuration(proxyBeanMethods = false) @ConditionalOnProperty(name = "spring.cloud.nacos.config.enabled", matchIfMissing = true) public class NacosConfigBootstrapConfiguration { public NacosConfigBootstrapConfiguration() { } @Bean @ConditionalOnMissingBean // 读取nacos相关配置 public NacosConfigProperties nacosConfigProperties() { return new NacosConfigProperties(); } @Bean @ConditionalOnMissingBean //实例化NacosConfigManager,创建ConfigService,ConfigService是配置读取更新的核心类 public NacosConfigManager nacosConfigManager(NacosConfigProperties nacosConfigProperties) { return new NacosConfigManager(nacosConfigProperties); } @Bean //spring初始化时会调用NacosPropertySourceLocator.locate方法, //nacos通过此方法加载nacos-server配置 public NacosPropertySourceLocator nacosPropertySourceLocator(NacosConfigManager nacosConfigManager) { return new NacosPropertySourceLocator(nacosConfigManager); } }-
NacosConfigProperties:对应我们上面在bootstrap.properties中对应的配置信息
-
NacosConfigManager:持有NacosConfigProperties和ConfigService,ConfigService用来查询发布配置的相关接口。
-
NacosPropertySourceLocator:它实现了PropertySourceLocator ,spring boot启动时调用PropertySourceLocator.locate(env)用来加载配置信息,下面来看相关源码:
至此我们在nacos上配置的properties和yaml文件都载入到spring配置文件中来了,后面可通过context.Environment.getProperty(propertyName)来获取相关配置信息
配置如何随spring boot加载进来我们说完了,接来下来看修改完配置后如何实时刷新
nacos config动态刷新
当nacos config更新后,根据配置中的refresh属性来判断是否刷新配置,配置如下
spring.cloud.nacos.config.ext-config[0].refresh=true首先spring.factories 配置了EnableAutoConfiguration=NacosConfigAutoConfiguration,NacosConfigAutoConfiguration配置类会注入一个NacosContextRefresher,它首先监听了ApplicationReadyEvent,然后注册一个nacos listener用来监听nacos config配置修改后发布一个spring refreshEvent用来刷新配置和应用
public class NacosContextRefresher implements ApplicationListener<ApplicationReadyEvent>, ApplicationContextAware { private final NacosRefreshHistory nacosRefreshHistory; private ApplicationContext applicationContext; private final ConfigService configService; private Map<String, Listener> listenerMap = new ConcurrentHashMap<>(16); @Override public void onApplicationEvent(ApplicationReadyEvent event) { // 只注册一次 if (this.ready.compareAndSet(false, true)) { this.registerNacosListenersForApplications(); } } private void registerNacosListenersForApplications() { if (isRefreshEnabled()) { for (NacosPropertySource propertySource : NacosPropertySourceRepository .getAll()) { // 对应刚才所说的配置 需要配置文件是否需要刷新 if (!propertySource.isRefreshable()) { continue; } String dataId = propertySource.getDataId(); // 注册nacos监听器 registerNacosListener(propertySource.getGroup(), dataId); } } } private void registerNacosListener(final String groupKey, final String dataKey) { String key = NacosPropertySourceRepository.getMapKey(dataKey, groupKey); Listener listener = listenerMap.computeIfAbsent(key, lst -> new AbstractSharedListener() { @Override public void innerReceive(String dataId, String group, String configInfo) { refreshCountIncrement(); // 添加刷新记录 nacosRefreshHistory.addRefreshRecord(dataId, group, configInfo); // todo feature: support single refresh for listening // 发布一个spring refreshEvent事件 对应监听器为RefreshEventListener 该监听器会完成配置的更新应用 applicationContext.publishEvent( new RefreshEvent(this, null, "Refresh Nacos config")); if (log.isDebugEnabled()) { log.debug(String.format( "Refresh Nacos config group=%s,dataId=%s,configInfo=%s", group, dataId, configInfo)); } } }); try { configService.addListener(dataKey, groupKey, listener); } catch (NacosException e) { log.warn(String.format( "register fail for nacos listener ,dataId=[%s],group=[%s]", dataKey, groupKey), e); } } }我们说完了nacos config动态刷新,那么肯定有对应的动态监听,nacos config会监听nacos server上配置的更新状态
nacos config动态监听
一般来说客户端和服务端数据交互无非就两种方式
- pull:客户端主动从服务器拉取数据
- push: 由服务端主动向客户端推送数据
这两种模式优缺点各不一样,pull模式需要考虑的是什么时候向服务端拉取数据,可能会存在数据延迟问题,而push模式需要客户端和服务端维护一个长连接,如果客户端较多会给服务端造成压力,但它的实时性会更好。
nacos采用的是pull模式,但它作了优化,可以看做是pull+push,客户端会轮询向服务端发出一个长连接请求,这个长连接最多30s就会超时,服务端收到客户端的请求会先判断当前是否有配置更新,有则立即返回。
如果没有服务端会将这个请求拿住“hold”29.5s加入队列,最后0.5s再检测配置文件无论有没有更新都进行正常返回,但等待的29.5s期间有配置更新可以提前结束并返回,下面会在源码中讲解具体怎么处理的
Nacos Config动态刷新机制
nacos config 动态刷新流程图
nacos client处理
动态监听的发起是在ConfigService的实现类NacosConfigService的构造方法中,它是对外nacos config api接口,在之前加载配置文件和NacosContextRefresher构造方法中都会获取或创建
@Order(0) public class NacosPropertySourceLocator implements PropertySourceLocator { public NacosPropertySourceLocator(NacosConfigManager nacosConfigManager) { this.nacosConfigManager = nacosConfigManager; this.nacosConfigProperties = nacosConfigManager.getNacosConfigProperties(); } public PropertySource<?> locate(Environment env) { this.nacosConfigProperties.setEnvironment(env); ConfigService configService = this.nacosConfigManager.getConfigService(); //......省略其他 } } public class NacosContextRefresher implements ApplicationListener<ApplicationReadyEvent>, ApplicationContextAware { public NacosContextRefresher(NacosConfigManager nacosConfigManager, NacosRefreshHistory refreshHistory) { this.nacosConfigProperties = nacosConfigManager.getNacosConfigProperties(); this.nacosRefreshHistory = refreshHistory; this.configService = nacosConfigManager.getConfigService(); this.isRefreshEnabled = this.nacosConfigProperties.isRefreshEnabled(); } }这里都会先判断是否已经创建了ConfigServer,没有则实例化一个NacosConfigService,来看它的构造函数
/***************************************** NacosConfigService *****************************************/ public class NacosConfigService implements ConfigService { /** * www.cnblogs.com/zzz-blogs/p/14249126.htmlblog.csdn.net/qw852328952/article/details/112142451
blog.csdn.net/xingxinggua9620/article/details/113563116

