Nacos 配置中心保姆级源码解析(01) 自动刷新

2 篇文章 0 订阅
2 篇文章 0 订阅

nacos-config-spring-boot-start (2.0.2)源码解析

思路

nacos 作为配置中心,其实实现分为两部分,一是位于我们自己服务的客户端, 二是服务端

在这里插入图片描述

一、客户端(client)

springboot 使用nacos配置中心 需要引入jar包 nacos-config-spring-boot-start,所以先从nacos-config-spring-boot-start 的jar 做为入口分析。

  1. 查看jar包结构如下,只有一个jar和依赖啥代码也没有,(初次看到此处的时候有点小蒙圈,咋和平时的不太一样),说明实现放在了其他依赖的jar 里面, 通过spom.xml文件的依赖关系以及pring.provider(无实际作用,提示和规范)文件里面的内容可以看出,其作为配置中心的实现主要放在nacos-config-spring-boot-autoconfigure

    在这里插入图片描述

  2. 重头戏:nacos-config-spring-boot-autoconfigure这个jar,发现里面终于有了源码,快速定位,spring.factories文件

    看启动是后加载了那些bean

    在这里插入图片描述

​ spring.factorise内容如下,主要包含几个bean,初看比较奇怪点就是为啥NacosConfigApplicationContextInitializer被注释掉了 ,(此处小坑)

NacosConfigAutoConfiguration
NacosConfigEnvironmentProcessor
NacosLoggingListener
#还有一个被注释掉的但是最重要的类,
#NacosConfigApplicationContextInitializer

在这里插入图片描述

挨个看启动自动装配的三个bean 做了什么事情

1.NacosConfigAutoConfiguration

​ 主要是通过@Import注解导入了一个类NacosConfigBootBeanDefinitionRegistrar

@ConditionalOnProperty(name = NacosConfigConstants.ENABLED, matchIfMissing = true)
@ConditionalOnMissingBean(name = CONFIG_GLOBAL_NACOS_PROPERTIES_BEAN_NAME)
@EnableConfigurationProperties(value = NacosConfigProperties.class)
@ConditionalOnClass(name = "org.springframework.boot.context.properties.bind.Binder")
@Import(value = { NacosConfigBootBeanDefinitionRegistrar.class })
@EnableNacosConfig
public class NacosConfigAutoConfiguration {

}
1.1NacosConfigBootBeanDefinitionRegistrar

NacosConfigBootBeanDefinitionRegistrar 主要实现了一下 BeanFactoryAware,ImportBeanDefinitionRegistrar,(动态加载和注册bean)

其实目的主要是为了注册:NacosBootConfigurationPropertiesBinder这个类也非常关键,主要是用于配置的属性与我们自定的bean

的绑定与更新

@Configuration
public class NacosConfigBootBeanDefinitionRegistrar
      implements ImportBeanDefinitionRegistrar, BeanFactoryAware {

   @Override
   public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
      DefaultListableBeanFactory defaultListableBeanFactory = (DefaultListableBeanFactory) beanFactory;
       //主要是为了注册NacosBootConfigurationPropertiesBinder
      BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder
            .rootBeanDefinition(NacosBootConfigurationPropertiesBinder.class);
      defaultListableBeanFactory.registerBeanDefinition(
            NacosBootConfigurationPropertiesBinder.BEAN_NAME,
            beanDefinitionBuilder.getBeanDefinition());
   }

   @Override
   public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
         BeanDefinitionRegistry registry) {

   }
}

2.NacosConfigEnvironmentProcessor(环境处理器)

在这里插入图片描述

实现了EnvironmentPostProcessor,实现此接口可以在spring启动时候加载完环境配置后, 通过事件(ApplicationEnvironmentPreparedEvent)的方式最终调用postProcessEnvironment此方法自定义环境的处理

@Override
public void postProcessEnvironment(ConfigurableEnvironment environment,
      SpringApplication application) {
	//注册NacosConfigApplicationContextInitializer这个bean
   application.addInitializers(new NacosConfigApplicationContextInitializer(this));
   nacosConfigProperties = NacosConfigPropertiesUtils
         .buildNacosConfigProperties(environment);
   if (enable()) {
      System.out.println(
            "[Nacos Config Boot] : The preload log configuration is enabled");
       //加载配置
      loadConfig(environment);
   }
}

NacosConfigEnvironmentProcessor 主要是做了两件事情

2.1向spring 注册 NacosConfigApplicationContextInitializer

(此处前文就有提到,spring.factorie中就有此bean的配置但是被注释, 原来是此处将bean 注册的) 这bean非常的重要

将当前对象( EnvironmentPostProcessor) 通过构造方法传入 NacosConfigApplicationContextInitializer,因为

NacosConfigApplicationContextInitializer 需要用到nacoe的环境信息, 持有EnvironmentPostProcessor 对象用于执行自动刷新

第二个就是NacosConfigApplicationContextInitialer会注册DataCache的监听,用于配置变更时,修改enviroment中的值

2.2 主要是nacos配置环境的一些处理,加载和保存nacos配置,启动ClientWorker

这里有个非常重要的成员变量 private Function<Properties, ConfigService> builder

定义了获取和创建 ConfigService(用于获取服务端配置的类)这里实际返回的是ConfigSerive的子类NacosConfigService

而NacosConfigService创建的时候会初始化nacos的客户端ClientWorker.

ClientWorker里面代理一个ConfigRpcTransportClient会启动一个线程,创建与服务端的长连接,接受服务端的各种事件通知,其中就包括配置变更事件

public NacosConfigService(Properties properties) throws NacosException {
    ValidatorUtils.checkInitParam(properties);
    
    initNamespace(properties);
    this.configFilterChainManager = new ConfigFilterChainManager(properties);
    ServerListManager serverListManager = new ServerListManager(properties);
    serverListManager.start();
    //初始化nacos客户端,ClientWorker里面会启动一个线程,创建与服务端的长连接,接受服务端的各种事件通知,其中就包括配置变更事件
    this.worker = new ClientWorker(this.configFilterChainManager, serverListManager, properties);
    // will be deleted in 2.0 later versions
    agent = new ServerHttpAgent(serverListManager);
    
}
2.2.1NacosConfigLoader

其中locadConfig方法非常重要,主要用于加载服务端中nacos的配置

public class NacosConfigLoader {

	private final Logger logger = LoggerFactory.getLogger(NacosConfigLoader.class);

	private final NacosConfigProperties nacosConfigProperties;
	private final ConfigurableEnvironment environment;
	private Function<Properties, ConfigService> builder;
	private List<DeferNacosPropertySource> nacosPropertySources = new LinkedList<>();

	public NacosConfigLoader(NacosConfigProperties nacosConfigProperties,
			ConfigurableEnvironment environment,
			Function<Properties, ConfigService> builder) {
		this.nacosConfigProperties = nacosConfigProperties;
		this.environment = environment;
		this.builder = builder;
	}

    
	public void loadConfig() {
        //构建nacos远程配置请求需要的属性对象(ncose 地址,名称空间,用户信息...等)
		Properties globalProperties = buildGlobalNacosProperties();
		MutablePropertySources mutablePropertySources = environment.getPropertySources();
        //获取远程配置
		List<NacosPropertySource> sources = reqGlobalNacosConfig(globalProperties,
				nacosConfigProperties.getType());
		for (NacosConfigProperties.Config config : nacosConfigProperties.getExtConfig()) {
			List<NacosPropertySource> elements = reqSubNacosConfig(config,
					globalProperties, config.getType());
			sources.addAll(elements);
		}
		if (nacosConfigProperties.isRemoteFirst()) {
			for (ListIterator<NacosPropertySource> itr = sources.listIterator(sources.size()); itr.hasPrevious();) {
				mutablePropertySources.addAfter(
						StandardEnvironment.SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, itr.previous());
			}
		} else {
			for (NacosPropertySource propertySource : sources) {
				mutablePropertySources.addLast(propertySource);
			}
		}
	}

//从远程nacos 服务端根据dataId获取nacos的中的配置
private NacosPropertySource[] reqNacosConfig(Properties configProperties,
      String[] dataIds, String groupId, ConfigType type, boolean isAutoRefresh) {
   final NacosPropertySource[] propertySources = new NacosPropertySource[dataIds.length];
   for (int i = 0; i < dataIds.length; i++) {
      if (StringUtils.isEmpty(dataIds[i])) {
         continue;
      }
      // Remove excess Spaces
      final String dataId = environment.resolvePlaceholders(dataIds[i].trim());
      final String config = NacosUtils.getContent(builder.apply(configProperties),
            dataId, groupId);
      final NacosPropertySource nacosPropertySource = new NacosPropertySource(
            dataId, groupId,
            buildDefaultPropertySourceName(dataId, groupId, configProperties),
            config, type.getType());
      nacosPropertySource.setDataId(dataId);
      nacosPropertySource.setType(type.getType());
      nacosPropertySource.setGroupId(groupId);
      nacosPropertySource.setAutoRefreshed(isAutoRefresh);
      logger.info("load config from nacos, data-id is : {}, group is : {}",
            nacosPropertySource.getDataId(), nacosPropertySource.getGroupId());
      propertySources[i] = nacosPropertySource;
      DeferNacosPropertySource defer = new DeferNacosPropertySource(
            nacosPropertySource, configProperties, environment);
      nacosPropertySources.add(defer);
   }
   return propertySources;
}
    
}

3.ClientWorker

3.1startInternal

启动线程监听一个长度为1的阻塞队列,并且执行监听,当阻塞队列中有值时候立马执行配置监听, 当阻塞队列中无值5秒执行一次监听并通知监听者

listenExecutebell中加入值的情况l(istenExecutebell.offer):

​ 1.service RPC通知

​ 2.注册或者移除DtaCache监听器

public void startInternal() throws NacosException {
    executor.schedule(new Runnable() {
        @Override
        public void run() {
            while (!executor.isShutdown() && !executor.isTerminated()) {
                try {
                    //当阻塞队列中有值时候立马执行配置监听, 当阻塞队列中无值5秒执行一次配置监听,并通知监听者
                    listenExecutebell.poll(5L, TimeUnit.SECONDS);
                    if (executor.isShutdown() || executor.isTerminated()) {
                        continue;
                    }
                    executeConfigListen();
                } catch (Exception e) {
                    LOGGER.error("[ rpc listen execute ] [rpc listen] exception", e);
                }
            }
        }
    }, 0L, TimeUnit.MILLISECONDS);
    
}
3.2executeConfigListen

executeConfigListen主要做了如下几个事情

1.向着Service注册或者移除DateCached的监听(requestProxy)

2.刷新发生变更的DataCache 的内容,自动刷新配置值(refreshContentAndCheck)

->ClienWork.refreshContentAndCheck

->CacheData.checkListenerMd5

->CacheData.safeNotifyListener

->DelegatingEventPublishingListener.receiveConfigInfo

->DelegatingEventPublishingListener.publishEvent ( 发送NacosConfigReceivedEvent

->NacosValueAnnotationBeanPostProcessor.onApplicationEvent (接收NacosConfigReceivedEventm,刷新nacosValue注解上的值)

通知所有的CacheData变化的所有的监听者,处理监听事件

CacheData的监听者处理拿到最新的内容执行对应的逻辑, 当没有客户端主动通知或者监听者变更的时候5秒执行一次

比如NacosValue注解注册的实现

1.1.CacheData监听器拿到最新的配置值,刷新spring envoroment 中的值

1.2.发送配置变更事件,通过NacosValueAnnotationBeanPostProcessor 刷新配置属性值

public void executeConfigListen() {
    
    Map<String, List<CacheData>> listenCachesMap = new HashMap<String, List<CacheData>>(16);
    Map<String, List<CacheData>> removeListenCachesMap = new HashMap<String, List<CacheData>>(16);
    long now = System.currentTimeMillis();
    boolean needAllSync = now - lastAllSyncTime >= ALL_SYNC_INTERNAL;
   	//循环所有的CacheData 初始化listenCachesMap,(拿到所有CacheData注册的监听器)
    for (CacheData cache : cacheMap.get().values()) {
		//................................................
    }
    
    boolean hasChangedKeys = false;
    //
    if (!listenCachesMap.isEmpty()) {
        for (Map.Entry<String, List<CacheData>> entry : listenCachesMap.entrySet()) {
            String taskId = entry.getKey();
            List<CacheData> listenCaches = entry.getValue();
            //构建注册监听对象,
            ConfigBatchListenRequest configChangeListenRequest = buildConfigRequest(listenCaches);
            configChangeListenRequest.setListen(true);
            try {
                RpcClient rpcClient = ensureRpcClient(taskId);
                //像nacos service 注册监听
                ConfigChangeBatchListenResponse configChangeBatchListenResponse = (ConfigChangeBatchListenResponse) requestProxy(
                        rpcClient, configChangeListenRequest);
                if (configChangeBatchListenResponse != null && configChangeBatchListenResponse.isSuccess()) {
                    
                    Set<String> changeKeys = new HashSet<String>();
                    //handle changed keys,notify listener
                    if (!CollectionUtils.isEmpty(configChangeBatchListenResponse.getChangedConfigs())) {
                        hasChangedKeys = true;
                        for (ConfigChangeBatchListenResponse.ConfigContext changeConfig : configChangeBatchListenResponse
                                .getChangedConfigs()) {
                            String changeKey = GroupKey
                                    .getKeyTenant(changeConfig.getDataId(), changeConfig.getGroup(),
                                            changeConfig.getTenant());
                            changeKeys.add(changeKey);
                            boolean isInitializing = cacheMap.get().get(changeKey).isInitializing();
                            //刷新配置,->CacheData.safeNotifyListener 通知监听者执行监听回调
                            refreshContentAndCheck(changeKey, !isInitializing);
                        }
                        
                    }
                    
                    //handler content configs
                    for (CacheData cacheData : listenCaches) {
                        String groupKey = GroupKey
                                .getKeyTenant(cacheData.dataId, cacheData.group, cacheData.getTenant());
                        if (!changeKeys.contains(groupKey)) {
                            //sync:cache data md5 = server md5 && cache data md5 = all listeners md5.
                            synchronized (cacheData) {
                                if (!cacheData.getListeners().isEmpty()) {
                                    cacheData.setSyncWithServer(true);
                                    continue;
                                }
                            }
                        }
                        
                        cacheData.setInitializing(false);
                    }
                    
                }
            } catch (Exception e) {
                
                LOGGER.error("Async listen config change error ", e);
                try {
                    Thread.sleep(50L);
                } catch (InterruptedException interruptedException) {
                    //ignore
                }
            }
        }
    }
    
    if (!removeListenCachesMap.isEmpty()) {
        for (Map.Entry<String, List<CacheData>> entry : removeListenCachesMap.entrySet()) {
            String taskId = entry.getKey();
            List<CacheData> removeListenCaches = entry.getValue();
            ConfigBatchListenRequest configChangeListenRequest = buildConfigRequest(removeListenCaches);
            configChangeListenRequest.setListen(false);
            try {
                RpcClient rpcClient = ensureRpcClient(taskId);
                boolean removeSuccess = unListenConfigChange(rpcClient, configChangeListenRequest);
                if (removeSuccess) {
                    for (CacheData cacheData : removeListenCaches) {
                        synchronized (cacheData) {
                            if (cacheData.getListeners().isEmpty()) {
                                ClientWorker.this
                                        .removeCache(cacheData.dataId, cacheData.group, cacheData.tenant);
                            }
                        }
                    }
                }
                
            } catch (Exception e) {
                LOGGER.error("async remove listen config change error ", e);
            }
            try {
                Thread.sleep(50L);
            } catch (InterruptedException interruptedException) {
                //ignore
            }
        }
    }
    
    if (needAllSync) {
        lastAllSyncTime = now;
    }
    //If has changed keys,notify re sync md5.
    if (hasChangedKeys) {
        notifyListenConfig();
    }
}

2.代理了一个内部类ConfigRpcTransportClient, ConfigRpcTransportClient的功能主要是与客户端维持长连接,接受客户端的的各种RPC事件回调 ,启动ClientWork的时候会调用initRpcClientHandler方法 与nacose服务端建立长连接,nacos服务端如果配置发生变成会发送配置变更事件,异步处理配置变更,最终通过此长连接发送变更的配置内容

/**
*	此方法会注册各种事件处理器,处理不同的服务端的RPC请求
*/
private void initRpcClientHandler(final RpcClient rpcClientInner) {
    
    /*
    * 配置变更处理器
     * Register Config Change /Config ReSync Handler
     */
    rpcClientInner.registerServerRequestHandler((request) -> {
        if (request instanceof ConfigChangeNotifyRequest) {
            ConfigChangeNotifyRequest configChangeNotifyRequest = (ConfigChangeNotifyRequest) request;
            LOGGER.info("[{}] [server-push] config changed. dataId={}, group={},tenant={}",
                    rpcClientInner.getName(), configChangeNotifyRequest.getDataId(),
                    configChangeNotifyRequest.getGroup(), configChangeNotifyRequest.getTenant());
            String groupKey = GroupKey
                    .getKeyTenant(configChangeNotifyRequest.getDataId(), configChangeNotifyRequest.getGroup(),
                            configChangeNotifyRequest.getTenant());
            
            CacheData cacheData = cacheMap.get().get(groupKey);
            if (cacheData != null) {
                cacheData.setSyncWithServer(false);
                //触发事件通知,ClientWork的executeConfigListen立即执行
                //更新@NacosValue注解值
                notifyListenConfig();
            }
            return new ConfigChangeNotifyResponse();
        }
        return null;
    });
    
    rpcClientInner.registerServerRequestHandler((request) -> {
        if (request instanceof ClientConfigMetricRequest) {
            ClientConfigMetricResponse response = new ClientConfigMetricResponse();
            response.setMetrics(getMetrics(((ClientConfigMetricRequest) request).getMetricsKeys()));
            return response;
        }
        return null;
    });
    
    rpcClientInner.registerConnectionListener(new ConnectionEventListener() {
        
        @Override
        public void onConnected() {
            LOGGER.info("[{}] Connected,notify listen context...", rpcClientInner.getName());
            notifyListenConfig();
        }
        
        @Override
        public void onDisConnect() {
            String taskId = rpcClientInner.getLabels().get("taskId");
            LOGGER.info("[{}] DisConnected,clear listen context...", rpcClientInner.getName());
            Collection<CacheData> values = cacheMap.get().values();
            
            for (CacheData cacheData : values) {
                if (StringUtils.isNotBlank(taskId)) {
                    if (Integer.valueOf(taskId).equals(cacheData.getTaskId())) {
                        cacheData.setSyncWithServer(false);
                    }
                } else {
                    cacheData.setSyncWithServer(false);
                }
            }
        }
        
    });
    
    rpcClientInner.serverListFactory(new ServerListFactory() {
        @Override
        public String genNextServer() {
            return ConfigRpcTransportClient.super.serverListManager.getNextServerAddr();
            
        }
        
        @Override
        public String getCurrentServer() {
            return ConfigRpcTransportClient.super.serverListManager.getCurrentServerAddr();
            
        }
        
        @Override
        public List<String> getServerList() {
            return ConfigRpcTransportClient.super.serverListManager.serverUrls;
            
        }
    });
    
    NotifyCenter.registerSubscriber(new Subscriber() {
        @Override
        public void onEvent(Event event) {
            rpcClientInner.onServerListChange();
        }
        
        @Override
        public Class<? extends Event> subscribeType() {
            return ServerlistChangeEvent.class;
        }
    });
}

另外一个非常重要的类是:NacosConfigLoader 用于从远程服务端获取配置

public class NacosConfigEnvironmentProcessor
		implements EnvironmentPostProcessor, Ordered {
	private final Logger logger = LoggerFactory
			.getLogger(NacosConfigEnvironmentProcessor.class);
	private final CacheableEventPublishingNacosServiceFactory nacosServiceFactory = CacheableEventPublishingNacosServiceFactory
			.getSingleton();
	private final Map<String, ConfigService> serviceCache = new HashMap<>(8);
    //nacos配置中心属性的来源,项目中配置的dataId,对应一个DeferNacosPropertySource
    //在执行loadConfig方法的时候初始化
	private final LinkedList<NacosConfigLoader.DeferNacosPropertySource> deferPropertySources = new LinkedList<>();
	private NacosConfigProperties nacosConfigProperties;

private Function<Properties, ConfigService> builder = properties -> {
   try {
       //获取缓存的key ,namespace,serverAddress,contextPath,clusterName,endpoint,accessKey,secretKey,encode
      final String key = NacosUtils.identify(properties);
      if (serviceCache.containsKey(key)) {
         return serviceCache.get(key);
      }
       //通过反射的方式获取client包中的 ConfigService,并缓存,缓存的key为nacos的属性信息
      final ConfigService configService = NacosFactory
            .createConfigService(properties);
      serviceCache.put(key, configService);
       
      return nacosServiceFactory.deferCreateService(configService, properties);
   }
   catch (NacosException e) {
      throw new NacosBootConfigException(
            "ConfigService can't be created with properties : " + properties, e);
   }
};
}

4.NacosConfigApplicationContextInitializer

​ 前面说到在spring.factories 文件中,NacosConfigApplicationContextInitializer 被注释掉了,但是在NacosConfigEnvironmentProcessor 环境处理器中又注册了此类,猜测目的是为了让NacoseConfigApplicationContextInitializer初始化在NacosConfigEnvironmentProcessor 类之后,主要是从两个方面验证此猜想。

  1. 逻辑上NacosConfigEnvironmentProcessor 是处理和初始化环境相关的,需要放在第一步。
  2. NacosConfigApplicationContextInitializer 需要对象需要持有NacosConfigEnvironmentProcessor ,对环境进行监听和处理,所以NacosConfigApplicationContextInitializer 对象需要在NacosConfigEnvironmentProcessor初始化以后才能开始初始化。
public class NacosConfigApplicationContextInitializer
		implements ApplicationContextInitializer<ConfigurableApplicationContext> {

	private final Logger logger = LoggerFactory
			.getLogger(NacosConfigApplicationContextInitializer.class);
	private final NacosConfigEnvironmentProcessor processor;
	private final CacheableEventPublishingNacosServiceFactory singleton = CacheableEventPublishingNacosServiceFactory
			.getSingleton();
	private final Function<Properties, ConfigService> builder = properties -> {
		try {
			return singleton.createConfigService(properties);
		}
		catch (NacosException e) {
			throw new NacosBootConfigException(
					"ConfigService can't be created with properties : " + properties, e);
		}
	};
	private ConfigurableEnvironment environment;
	private NacosConfigProperties nacosConfigProperties;
	
    //构造方法,持有NacosConfigEnvironmentProcessor 对象
	public NacosConfigApplicationContextInitializer(
			NacosConfigEnvironmentProcessor configEnvironmentProcessor) {
		this.processor = configEnvironmentProcessor;
	}

因为NacosConfigApplicationContextInitializer 实现了ApplicationContextInitializer 接口。所以在spring 容器刷新的时候会执行其initialize方法,initialize主要是为了添加dataId配置(DeferNacosPropertySource)的监听器,也就是DateChe的监听器, ClientWork会默认5秒会回调一次需要执行的监听器,如遇到service主动通知会立即通知

configLoader.addListenerIfAutoRefreshed();

->NacosPropertySourcePostProcessor.addListenerIfAutoRefreshed

->NacosConfigService.addListener

->ClientWorker.addTenantListeners

->ClientWorker.addCacheDataIfAbsent (创建和缓存CacheData,并且返回) cache.setSyncWithServer(false);

->CacheData.addListener (添加监听)

public void initialize(ConfigurableApplicationContext context) {
   singleton.setApplicationContext(context);
   environment = context.getEnvironment();
   nacosConfigProperties = NacosConfigPropertiesUtils
         .buildNacosConfigProperties(environment);
    //创建NacosConfigLoader 配置加载器
   final NacosConfigLoader configLoader = new NacosConfigLoader(
         nacosConfigProperties, environment, builder);
   if (!enable()) {
      logger.info("[Nacos Config Boot] : The preload configuration is not enabled");
   }
   else {

      // If it opens the log level loading directly will cache
      // DeferNacosPropertySource release

      if (processor.enable()) {
         processor.publishDeferService(context);
         configLoader
               .addListenerIfAutoRefreshed(processor.getDeferPropertySources());
      }
      else {
         configLoader.loadConfig();
          //添加监听,最终走到NacosPropertySourcePostProcessor.addListenerIfAutoRefreshed
          //为每一个dataId添加一个监听器
          //监听器中会将配置变更的内容通过MutablePropertySources的方式,将spring环境(environment)中的值进行替换
          //后续,nacosValue注解后置处理器,监听处理的时候,也是直接从spring环境中取值替换
         configLoader.addListenerIfAutoRefreshed();
      }
   }

   final ConfigurableListableBeanFactory factory = context.getBeanFactory();
   if (!factory
         .containsSingleton(NacosBeanUtils.GLOBAL_NACOS_PROPERTIES_BEAN_NAME)) {
      factory.registerSingleton(NacosBeanUtils.GLOBAL_NACOS_PROPERTIES_BEAN_NAME,
            configLoader.buildGlobalNacosProperties());
   }
}
4.1NacosPropertySourcePostProcessor

addListenerIfAutoRefreshed做了两件事情

1.定义监听(属性变化后,替换spring enveroment 的值,方便NacosValueAnnotationBeanPostProcessor 从环境取值替换)

2.注册监听

最终通过EventPublishingConfigService 注册 DelegatingEventPublishingListener

public static void addListenerIfAutoRefreshed(
      final NacosPropertySource nacosPropertySource, final Properties properties,
      final ConfigurableEnvironment environment) {

   if (!nacosPropertySource.isAutoRefreshed()) { // Disable Auto-Refreshed
      return;
   }

   final String dataId = nacosPropertySource.getDataId();
   final String groupId = nacosPropertySource.getGroupId();
   final String type = nacosPropertySource.getType();
   final NacosServiceFactory nacosServiceFactory = getNacosServiceFactoryBean(
         beanFactory);

   try {
	//这里创建的是 EventPublishingConfigService
      ConfigService configService = nacosServiceFactory
            .createConfigService(properties);

      Listener listener = new AbstractListener() {

         @Override
         public void receiveConfigInfo(String config) {
            String name = nacosPropertySource.getName();
            NacosPropertySource newNacosPropertySource = new NacosPropertySource(
                  dataId, groupId, name, config, type);
            newNacosPropertySource.copy(nacosPropertySource);
            MutablePropertySources propertySources = environment
                  .getPropertySources();
            // replace NacosPropertySource
            propertySources.replace(name, newNacosPropertySource);
         }
      };

      if (configService instanceof EventPublishingConfigService) {
         ((EventPublishingConfigService) configService).addListener(dataId,
               groupId, type, listener);
      }
      else {
         configService.addListener(dataId, groupId, listener);
      }

   }
   catch (NacosException e) {
      throw new RuntimeException(
            "ConfigService can't add Listener with properties : " + properties,
            e);
   }
}

NacosValueAnnotationBeanPostProcessor

public class NacosValueAnnotationBeanPostProcessor
      extends AbstractAnnotationBeanPostProcessor implements BeanFactoryAware,
      EnvironmentAware, ApplicationListener<NacosConfigReceivedEvent> {
          
          //监听配置变更事件,替换@NacosValue中更改的值
          public void onApplicationEvent(NacosConfigReceivedEvent event) {
		// In to this event receiver, the environment has been updated the
		// latest configuration information, pull directly from the environment
		// fix issue #142
		for (Map.Entry<String, List<NacosValueTarget>> entry : placeholderNacosValueTargetMap
				.entrySet()) {
			String key = environment.resolvePlaceholders(entry.getKey());
			String newValue = environment.getProperty(key);

			if (newValue == null) {
				continue;
			}
			List<NacosValueTarget> beanPropertyList = entry.getValue();
			for (NacosValueTarget target : beanPropertyList) {
				String md5String = MD5Utils.md5Hex(newValue, "UTF-8");
				boolean isUpdate = !target.lastMD5.equals(md5String);
				if (isUpdate) {
					target.updateLastMD5(md5String);
					Object evaluatedValue = resolveNotifyValue(target.nacosValueExpr, key, newValue);
					if (target.method == null) {
						setField(target, evaluatedValue);
					}
					else {
						setMethod(target, evaluatedValue);
					}
				}
			}
		}
	}
      }

NacosValue自动刷新流程

在这里插入图片描述

  • 8
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Nacos配置中心控制台的源码分析可以帮助我们深入理解其实现细节和工作原理。以下是一个大致的源码分析过程: 1. 入口类分析:首先,我们需要找到Nacos配置中心控制台的入口类。该类通常是一个Spring Boot应用的启动类,负责初始化和启动整个应用。我们可以查找包含main方法的类,或者在启动脚本中找到应用的入口点。 2. 依赖分析:接下来,我们需要分析应用所依赖的第三方库和框架。查看应用的pom.xml文件或者build.gradle文件,可以获取到所依赖的各个库和对应版本。这些依赖通常包括Spring框架、Nacos客户端等。 3. 配置加载与解析Nacos配置中心控制台需要加载和解析配置,包括数据库配置、Nacos服务地址配置等。我们可以查找相关的配置文件或者代码片段,了解配置的加载和解析过程。 4. 控制器与路由:控制台通常提供了一些Web接口供前端调用。我们可以查找控制器类,分析其中的方法和注解,了解各个接口的功能和路由规则。 5. 页面模板与前端交互:配置中心控制台通常包含一些页面模板和与前端的交互逻辑。我们可以查找相关的HTML、CSS和JavaScript文件,分析页面的结构和交互逻辑。 6. 调用Nacos API:控制台需要与Nacos服务器进行通信,调用Nacos的API获取和修改配置信息。我们可以查找相关的API调用,了解控制台是如何与Nacos服务器进行通信的。 通过以上分析,我们可以逐步了解Nacos配置中心控制台的实现细节和工作原理。需要注意的是,具体的源码分析过程会因项目结构和代码风格而有所不同。以上只是一个大致的指导,具体分析还需根据实际情况来进行。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值