Nacos深入浅出(八)
Nacos-spring-context.java
感觉这个后台要比之前的Nacos复杂多了,涉及到很多基础的概念,慢慢看,这个后面慢慢更新解析过程
看到他的目录结构一个是基于注解,一个是XML的解析,之前我们的值基于注解的,我们就看下这个注解的实现过程;
借助ImportBeanDefinitionRegistrar接口实现bean的动态注入
https://www.jianshu.com/p/2b993ced6a4c
NacosBeanDefinitionRegistrar.java
public class NacosBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar, EnvironmentAware, BeanFactoryAware { private Environment environment; private BeanFactory beanFactory; @Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { BeanDefinition annotationProcessor = BeanDefinitionBuilder.genericBeanDefinition( PropertySourcesPlaceholderConfigurer.class).getBeanDefinition(); registry.registerBeanDefinition(PropertySourcesPlaceholderConfigurer.class.getName(), annotationProcessor); AnnotationAttributes attributes = AnnotationAttributes.fromMap( importingClassMetadata.getAnnotationAttributes(EnableNacos.class.getName())); // Register Global Nacos Properties Bean registerGlobalNacosProperties(attributes, registry, environment, GLOBAL_NACOS_PROPERTIES_BEAN_NAME); // Register Nacos Annotation Beans registerNacosAnnotationBeans(registry); // Invoke NacosPropertySourcePostProcessor immediately // in order to enhance the precedence of @NacosPropertySource process invokeNacosPropertySourcePostProcessor(beanFactory); } @Override public void setEnvironment(Environment environment) { this.environment = environment; } public void registerNacosAnnotationBeans(BeanDefinitionRegistry registry) { registerNacosCommonBeans(registry); registerNacosConfigBeans(registry, environment); registerNacosDiscoveryBeans(registry); } @Override public void setBeanFactory(BeanFactory beanFactory) throws BeansException { this.beanFactory = beanFactory; } }
NacosConfigBeanDefinitionRegistrar.java
下面这个操作就是将含有NacosPropertySource注解的类,注册到beanfactory
public class NacosConfigBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar, EnvironmentAware, BeanFactoryAware { private Environment environment; private BeanFactory beanFactory; @Override public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) { AnnotationAttributes attributes = fromMap(metadata.getAnnotationAttributes(EnableNacosConfig.class.getName())); // Register Global Nacos Properties Bean registerGlobalNacosProperties(attributes, registry, environment, CONFIG_GLOBAL_NACOS_PROPERTIES_BEAN_NAME); // Register Nacos Common Beans registerNacosCommonBeans(registry); // Register Nacos Config Beans registerNacosConfigBeans(registry, environment); // Invoke NacosPropertySourcePostProcessor immediately // in order to enhance the precedence of @NacosPropertySource process invokeNacosPropertySourcePostProcessor(beanFactory); } @Override public void setEnvironment(Environment environment) { this.environment = environment; } @Override public void setBeanFactory(BeanFactory beanFactory) throws BeansException { this.beanFactory = beanFactory; } }
invokeNacosPropertySourcePostProcessor(beanFactory);
NacosBeanUtils.java
/** * Invokes {@link NacosPropertySourcePostProcessor} * * @param beanFactory {@link BeanFactory} */ public static void invokeNacosPropertySourcePostProcessor(BeanFactory beanFactory) { NacosPropertySourcePostProcessor postProcessor = beanFactory.getBean(NacosPropertySourcePostProcessor.BEAN_NAME, NacosPropertySourcePostProcessor.class); postProcessor.postProcessBeanFactory((ConfigurableListableBeanFactory) beanFactory); }
NacosPropertySourcePostProcessor.java
@Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { String[] abstractNacosPropertySourceBuilderBeanNames = BeanUtils.getBeanNames(beanFactory, AbstractNacosPropertySourceBuilder.class); this.nacosPropertySourceBuilders = new ArrayList<AbstractNacosPropertySourceBuilder>( abstractNacosPropertySourceBuilderBeanNames.length); for (String beanName : abstractNacosPropertySourceBuilderBeanNames) { this.nacosPropertySourceBuilders.add( beanFactory.getBean(beanName, AbstractNacosPropertySourceBuilder.class)); } this.configServiceBeanBuilder = getConfigServiceBeanBuilder(beanFactory); String[] beanNames = beanFactory.getBeanDefinitionNames(); for (String beanName : beanNames) { processPropertySource(beanName, beanFactory); } }
private void processPropertySource(String beanName, ConfigurableListableBeanFactory beanFactory) { if (processedBeanNames.contains(beanName)) { return; } BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanName); // Build multiple instance if possible List<NacosPropertySource> nacosPropertySources = buildNacosPropertySources(beanName, beanDefinition); // Add Orderly for (NacosPropertySource nacosPropertySource : nacosPropertySources) { addNacosPropertySource(nacosPropertySource); addListenerIfAutoRefreshed(nacosPropertySource); } processedBeanNames.add(beanName); }
private List<NacosPropertySource> buildNacosPropertySources(String beanName, BeanDefinition beanDefinition) { for (AbstractNacosPropertySourceBuilder builder : nacosPropertySourceBuilders) { if (builder.supports(beanDefinition)) { return builder.build(beanName, beanDefinition); } } return Collections.emptyList(); }
public List<NacosPropertySource> build(String beanName, T beanDefinition) { Map<String, Object>[] attributesArray = this.resolveRuntimeAttributesArray(beanDefinition, this.globalNacosProperties); int size = attributesArray == null ? 0 : attributesArray.length; if (size == 0) { return Collections.emptyList(); } else { List<NacosPropertySource> nacosPropertySources = new ArrayList(size); for(int i = 0; i < size; ++i) { Map<String, Object> attributes = attributesArray[i]; if (!CollectionUtils.isEmpty(attributes)) { NacosPropertySource nacosPropertySource = this.doBuild(beanName, beanDefinition, attributesArray[i]); NacosConfigMetadataEvent metadataEvent = this.createMetaEvent(nacosPropertySource, beanDefinition); this.initMetadataEvent(nacosPropertySource, beanDefinition, metadataEvent); this.publishMetadataEvent(metadataEvent); nacosPropertySources. (nacosPropertySource); } } return nacosPropertySources; } }
protected NacosPropertySource doBuild(String beanName, T beanDefinition, Map<String, Object> runtimeAttributes) { // Get annotation metadata String name = (String) runtimeAttributes.get(NAME_ATTRIBUTE_NAME); String dataId = (String) runtimeAttributes.get(DATA_ID_ATTRIBUTE_NAME); String groupId = (String) runtimeAttributes.get(GROUP_ID_ATTRIBUTE_NAME); Map<String, Object> nacosPropertiesAttributes = (Map<String, Object>) runtimeAttributes.get(PROPERTIES_ATTRIBUTE_NAME); Properties nacosProperties = resolveProperties(nacosPropertiesAttributes, environment, globalNacosProperties); String nacosConfig = nacosConfigLoader.load(dataId, groupId, nacosProperties); if (!StringUtils.hasText(nacosConfig)) { if (logger.isWarnEnabled()) { logger.warn(format("There is no content for NacosPropertySource from dataId[%s] , groupId[%s] , properties[%s].", dataId, groupId, valueOf(nacosPropertiesAttributes))); } } if (!StringUtils.hasText(name)) { name = buildDefaultPropertySourceName(dataId, groupId, nacosProperties); } NacosPropertySource nacosPropertySource = new NacosPropertySource(name, nacosConfig); nacosPropertySource.setBeanName(beanName); String beanClassName = beanDefinition.getBeanClassName(); if (StringUtils.hasText(beanClassName)) { nacosPropertySource.setBeanType(resolveClassName(beanClassName, classLoader)); } nacosPropertySource.setGroupId(groupId); nacosPropertySource.setDataId(dataId); nacosPropertySource.setProperties(nacosProperties); initNacosPropertySource(nacosPropertySource, beanDefinition, runtimeAttributes); return nacosPropertySource; }
加载初始化的内容
/** * Load Nacos config vid dataId, groupId and {@link Properties acos Properties} * * @param dataId dataId * @param groupId groupId * @param nacosProperties {@link Properties acos Properties} * @return Nacos config * @throws RuntimeException If {@link ConfigService} creating is failed. */ public String load(String dataId, String groupId, Properties nacosProperties) throws RuntimeException { try { configService = nacosServiceFactory != null ? nacosServiceFactory.createConfigService(nacosProperties) : NacosFactory.createConfigService(nacosProperties); } catch (NacosException e) { throw new RuntimeException("ConfigService can't be created with dataId :" + dataId + " , groupId : " + groupId + " , properties : " + nacosProperties , e); } return NacosUtils.getContent(configService, dataId, groupId); }
这里我们之前应该是漏掉了,我们会过来再看下,这里根据反射的构造函数决定用哪个构造函数初始化;
CacheableEventPublishingNacosServiceFactory.java
public ConfigService createConfigService(Properties properties) throws NacosException { Properties copy = new Properties(); copy.putAll(properties); String cacheKey = NacosUtils.identify(copy); ConfigService configService = (ConfigService)this.configServicesCache.get(cacheKey); if (configService == null) { configService = this.doCreateConfigService(copy); this.configServicesCache.put(cacheKey, configService); } return configService; }
private ConfigService doCreateConfigService(Properties properties) throws NacosException { ConfigService configService = NacosFactory.createConfigService(properties); return new EventPublishingConfigService(configService, properties, this.context, this.nacosConfigListenerExecutor); }
不管nacosServiceFactory是否为空,绕到最后都是通过下面的方式进行初始化
public class ConfigFactory { public ConfigFactory() { } public static ConfigService createConfigService(Properties properties) throws NacosException { try { Class<?> driverImplClass = Class.forName("com.alibaba.nacos.client.config.NacosConfigService"); Constructor constructor = driverImplClass.getConstructor(Properties.class); ConfigService vendorImpl = (ConfigService)constructor.newInstance(properties); return vendorImpl; } catch (Throwable var4) { throw new NacosException(-400, var4.getMessage()); } }
NacosConfigService.java中的构造函数进行初始化,同时也初始化了我们的clientWorker;
public NacosConfigService(Properties properties) throws NacosException { String encodeTmp = properties.getProperty("encode"); if (StringUtils.isBlank(encodeTmp)) { this.encode = "UTF-8"; } else { this.encode = encodeTmp.trim(); } String namespaceTmp = properties.getProperty("namespace"); if (StringUtils.isBlank(namespaceTmp)) { this.namespace = TenantUtil.getUserTenant(); properties.put("namespace", this.namespace); } else { this.namespace = namespaceTmp; properties.put("namespace", this.namespace); } this.agent = new ServerHttpAgent(properties); this.agent.start(); this.worker = new ClientWorker(this.agent, this.configFilterChainManager); }
AnnotationNacosPropertySourceBuilder.java
protected Map<String, Object>[] resolveRuntimeAttributesArray(AnnotatedBeanDefinition beanDefinition, Properties globalNacosProperties) { AnnotationMetadata metadata = beanDefinition.getMetadata(); Set<String> annotationTypes = metadata.getAnnotationTypes(); List<Map<String, Object>> annotationAttributesList = new LinkedList(); Iterator var6 = annotationTypes.iterator(); while(var6.hasNext()) { String annotationType = (String)var6.next(); annotationAttributesList.addAll(this.getAnnotationAttributesList(metadata, annotationType)); } return (Map[])annotationAttributesList.toArray(new Map[0]); }
以上就是一个过程,后面我们在专题分享他这么多的意义,或者说目的;
这样子一套跳转下来,一个注解类的解析就结束了,下面我们就获得了这个类的相关东东,那些value,以及property,后面我们就看下他如何取值,以及后续相关操作!
NacosUtils.java
/** * Get content from {@link ConfigService} via dataId and groupId * * @param configService {@link ConfigService} * @param dataId dataId * @param groupId groupId * @return If available , return content , or <code>null</code> */ public static String getContent(ConfigService configService, String dataId, String groupId) { String content = null; try { content = configService.getConfig(dataId, groupId, DEFAULT_TIMEOUT); } catch (NacosException e) { if (logger.isErrorEnabled()) { logger.error("Can't get content from dataId : " + dataId + " , groupId : " + groupId, e); } } return content; }
NacosConfigService.java
@Override public String getConfig(String dataId, String group, long timeoutMs) throws NacosException { return getConfigInner(namespace, dataId, group, timeoutMs); }
客户端的代码就是通过这个方式去获取的属性值,只要你坚持没有什么是不可能的,加油!
private String getConfigInner(String tenant, String dataId, String group, long timeoutMs) throws NacosException { group = this.null2defaultGroup(group); ParamUtils.checkKeyParam(dataId, group); ConfigResponse cr = new ConfigResponse(); cr.setDataId(dataId); cr.setTenant(tenant); cr.setGroup(group);
// 优先使用本地配置 String content = LocalConfigInfoProcessor.getFailover(this.agent.getName(), dataId, group, tenant); if (content != null) { log.warn(this.agent.getName(), "[get-config] get failover ok, dataId={}, group={}, tenant={}, config={}", new Object[]{dataId, group, tenant, ContentUtils.truncateContent(content)}); cr.setContent(content); this.configFilterChainManager.doFilter((IConfigRequest)null, cr); content = cr.getContent(); return content; } else { try { content = this.worker.getServerConfig(dataId, group, tenant, timeoutMs); cr.setContent(content); this.configFilterChainManager.doFilter((IConfigRequest)null, cr); content = cr.getContent(); return content; } catch (NacosException var9) { if (403 == var9.getErrCode()) { throw var9; } else { log.warn("NACOS-0003", LoggerHelper.getErrorCodeStr("NACOS", "NACOS-0003", "环境问题", "get from server error")); log.warn(this.agent.getName(), "[get-config] get from server error, dataId={}, group={}, tenant={}, msg={}", new Object[]{dataId, group, tenant, var9.toString()}); log.warn(this.agent.getName(), "[get-config] get snapshot ok, dataId={}, group={}, tenant={}, config={}", new Object[]{dataId, group, tenant, ContentUtils.truncateContent(content)}); content = LocalConfigInfoProcessor.getSnapshot(this.agent.getName(), dataId, group, tenant); cr.setContent(content); this.configFilterChainManager.doFilter((IConfigRequest)null, cr); content = cr.getContent(); return content; } } } }
public class LocalConfigInfoProcessor { private static final Logger LOGGER = LogUtils.logger(LocalConfigInfoProcessor.class); static public String getFailover(String serverName, String dataId, String group, String tenant) { File localPath = getFailoverFile(serverName, dataId, group, tenant); if (!localPath.exists() || !localPath.isFile()) { return null; } try { return readFile(localPath); } catch (IOException ioe) { LOGGER.error("[" + serverName + "] get failover error, " + localPath, ioe); return null; } } }
还是这个类中的方法
static File getFailoverFile(String serverName, String dataId, String group, String tenant) { File tmp = new File(LOCAL_SNAPSHOT_PATH, serverName + "_nacos"); tmp = new File(tmp, "data"); if (StringUtils.isBlank(tenant)) { tmp = new File(tmp, "config-data"); } else { tmp = new File(tmp, "config-data-tenant"); tmp = new File(tmp, tenant); } return new File(new File(tmp, group), dataId); }
public String getServerConfig(String dataId, String group, String tenant, long readTimeout) throws NacosException { if (StringUtils.isBlank(group)) { group = Constants.DEFAULT_GROUP; } HttpResult result = null; try { List<String> params = null; if (StringUtils.isBlank(tenant)) { params = Arrays.asList("dataId", dataId, "group", group); } else { params = Arrays.asList("dataId", dataId, "group", group, "tenant", tenant); } result = agent.httpGet(Constants.CONFIG_CONTROLLER_PATH, null, params, agent.getEncode(), readTimeout); } catch (IOException e) { String message = String.format( "[%s] [sub-server] get server config exception, dataId=%s, group=%s, tenant=%s", agent.getName(), dataId, group, tenant); LOGGER.error(message, e); throw new NacosException(NacosException.SERVER_ERROR, e.getMessage()); } switch (result.code) { case HttpURLConnection.HTTP_OK: LocalConfigInfoProcessor.saveSnapshot(agent.getName(), dataId, group, tenant, result.content); return result.content; case HttpURLConnection.HTTP_NOT_FOUND: LocalConfigInfoProcessor.saveSnapshot(agent.getName(), dataId, group, tenant, null); return null; case HttpURLConnection.HTTP_CONFLICT: { LOGGER.error( "[{}] [sub-server-error] get server config being modified concurrently, dataId={}, group={}, " + "tenant={}", agent.getName(), dataId, group, tenant); throw new NacosException(NacosException.CONFLICT, "data being modified, dataId=" + dataId + ",group=" + group + ",tenant=" + tenant); } case HttpURLConnection.HTTP_FORBIDDEN: { LOGGER.error("[{}] [sub-server-error] no right, dataId={}, group={}, tenant={}", agent.getName(), dataId, group, tenant); throw new NacosException(result.code, result.content); } default: { LOGGER.error("[{}] [sub-server-error] dataId={}, group={}, tenant={}, code={}", agent.getName(), dataId, group, tenant, result.code); throw new NacosException(result.code, "http error, code=" + result.code + ",dataId=" + dataId + ",group=" + group + ",tenant=" + tenant); } }
client在取值的话优先通过本地文件的方式去访问,取不到的话再通过http的方式去nacos中去取;这样就是一个取值的闭环了;
我感觉这样主要是client和nacos部署到一起的方式去解决了!
下面我们开始分析Nacos的思想,基本上本篇结束怎个Nacos客户端加载的过程就结束了,启动的时候如何取值,下面我们会分析Nacos发布之后,客户端是如何取值的,
这里我们不妨猜测下,会不会采用了代理模式,我们每次取值的时候,在我们取值前,封装了一部分取值的操作,取值的操作应该就是前面的那套逻辑^_^
加油,go!
没有什么比每天有成长进步更高兴的事情