Nacos源码分析七、NacosConfigAutoConfiguration配置类

x33g5p2x  于2021-12-20 转载在 其他  
字(4.8k)|赞(0)|评价(0)|浏览(637)

Nacos除了Bootstrap的自动配置类,同时也配置了SpringBoot的自动配置:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.alibaba.cloud.nacos.NacosConfigAutoConfiguration,\
com.alibaba.cloud.nacos.endpoint.NacosConfigEndpointAutoConfiguration

NacosConfigEndpointAutoConfiguration这个是端点的,就不分析了。我们看NacosConfigAutoConfiguration:

@Configuration(proxyBeanMethods = false)
@ConditionalOnProperty(name = "spring.cloud.nacos.config.enabled", matchIfMissing = true)
public class NacosConfigAutoConfiguration {

   @Bean
   public NacosConfigProperties nacosConfigProperties(ApplicationContext context) {
      if (context.getParent() != null
            && BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
                  context.getParent(), NacosConfigProperties.class).length > 0) {
         return BeanFactoryUtils.beanOfTypeIncludingAncestors(context.getParent(),
               NacosConfigProperties.class);
      }
      return new NacosConfigProperties();
   }

   @Bean
   public NacosRefreshProperties nacosRefreshProperties() {
      return new NacosRefreshProperties();
   }

   @Bean
   public NacosRefreshHistory nacosRefreshHistory() {
      return new NacosRefreshHistory();
   }

   @Bean
   public NacosConfigManager nacosConfigManager(
         NacosConfigProperties nacosConfigProperties) {
      return new NacosConfigManager(nacosConfigProperties);
   }

   @Bean
   public NacosContextRefresher nacosContextRefresher(
         NacosConfigManager nacosConfigManager,
         NacosRefreshHistory nacosRefreshHistory) {
      // Consider that it is not necessary to be compatible with the previous
      // configuration
      // and use the new configuration if necessary.
      return new NacosContextRefresher(nacosConfigManager, nacosRefreshHistory);
   }

}

首先搞清楚一个,这个配置类是定义在主应用上下文中的,也就是说这里定义的这些bean和NacosConfigBootstrapConfiguration中定义的并不是在同一个容器中。

  1. NacosConfigProperties

NacosConfigProperties是从Bootstrap上下文容器中实例并返回。等于是两个上下文的容器中共存一份。

  1. NacosRefreshProperties

这个已经废弃了。

  1. NacosRefreshHistory

这个类就是用来对获取的配置数据内容用md5加密作为历史记录,然后存在在一个集合里。

public NacosRefreshHistory() {
   try {
      md = MessageDigest.getInstance("MD5");
   }
   catch (NoSuchAlgorithmException e) {
      log.error("failed to initialize MessageDigest : ", e);
   }
}

添加记录:

public void addRefreshRecord(String dataId, String group, String data) {
   records.addFirst(new Record(DATE_FORMAT.get().format(new Date()), dataId, group,
         md5(data), null));
   if (records.size() > MAX_SIZE) {
      records.removeLast();
   }
}

md5方法:

private String md5(String data) {
   if (StringUtils.isEmpty(data)) {
      return null;
   }
   if (null == md) {
      try {
         md = MessageDigest.getInstance("MD5");
      }
      catch (NoSuchAlgorithmException ignored) {
         return "unable to get md5";
      }
   }
   return new BigInteger(1, md.digest(data.getBytes(StandardCharsets.UTF_8)))
         .toString(16);
}

Record类定义:

static class Record {

   private final String timestamp;

   private final String dataId;

   private final String group;

   private final String md5;

   Record(String timestamp, String dataId, String group, String md5,
         Map<String, Object> last) {
      this.timestamp = timestamp;
      this.dataId = dataId;
      this.group = group;
      this.md5 = md5;
   }
   ....
}
  1. NacosContextRefresher

添加监听用的。首先它本身就是一个ApplicationListener监听器,监听ApplicationReadyEvent事件:

@Override
public void onApplicationEvent(ApplicationReadyEvent event) {
   // many Spring context
   if (this.ready.compareAndSet(false, true)) {
      this.registerNacosListenersForApplications();
   }
}

事件触发时,注册Nacos的监听器:

/**
 * register Nacos Listeners.
 */
private void registerNacosListenersForApplications() {
   if (isRefreshEnabled()) {
      for (NacosPropertySource propertySource : NacosPropertySourceRepository
            .getAll()) {
         if (!propertySource.isRefreshable()) {
            continue;
         }
         String dataId = propertySource.getDataId();
         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
						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);
		}
	}
configService添加上面定义的监听器。 这个前面分析过,当长轮询检测到配置变更时会触发监听器的receiveConfigInfo方法,这个在AbstractSharedListener中定义:
@Override
public final void receiveConfigInfo(String configInfo) {
    innerReceive(dataId, group, configInfo);
}

就是调用了模板方法innerReceive

innerReceive方法的实现内容:

  1. 计数。
  2. 添加历史记录。
  3. 主应用上下文中发布RefreshEvent事件。

整理一下NacosConfigBootstrapConfiguration和NacosConfigAutoConfiguration这两个配置类定义的bean:

总结一下:

  • 主应用上下文中和Bootstrap上下文共享同一个NacosConfigProperties对象
  • NacosRefreshHistory用来做配置历史记录的
  • NacosContextRefresher在ApplicationReadyEvent事件时注册各配置的监听器,收到配置变更事件时在主应用上下文中发布RefreshEvent事件。后面我们进一步分析发布RefreshEvent这个事件后配置如何刷新的。

相关文章