Dubbo服务提供者Provider启动流程上(三)
Dubbo在配置的提供上,提供了丰富的功能,也考虑了很多场景,具体可以参考官方文档。其中配置的来源也有很多,包括了外部的配置中心,这些配置是如何获取,如何解析,最后如何在dubbo中生效的呢?带着这些疑问,学习一下dubbo provider启动流程。这边还是第一章以API方式入手,入口在serviceConfg#export。
serviceConfig的继承关系如下:
export方法是里面主要是checkAndUpdateSubConfigs和doExport方法。前者实现了外部化配置的初始化过程,以及多个配置优先级问题。后者负责检查和填充配置,最后暴露服务。
本文主要侧重于checkAndUpdateSubConfigs的讲解,服务暴露在下一个章节介绍。
checkAndUpdateSubConfigs
public void checkAndUpdateSubConfigs() { // Use default configs defined explicitly on global configs // 配置优先级 provider > module > application completeCompoundConfigs(); // Config Center should always being started first. startConfigCenter(); // 实例化 ProviderConfig过程,如果 ProviderConfig 不存在,那么就新建一个默认的,并执行 refresh 方法。 checkDefault(); checkApplication(); checkRegistry(); checkProtocol(); // 设置Serviceconfig的变量 this.refresh(); //用于设置 MetadataReportConfig,尝试从配置中加载其配置,就算 MetadataReportConfig 不可用(即 unvalid),也只会报警告。 checkMetadataReport(); if (StringUtils.isEmpty(interfaceName)) { throw new IllegalStateException("<dubbo:service interface=\"\" /> interface not allow null!"); } // 对是否为 GenericService 进行适配(GenricService:泛化调用 是一种 Dubbo 提供的特殊角色,不需要引入jar包, // 直接通过GenericService进行调用,用于服务测试以及API服务网关,后面文章细分析) if (ref instanceof GenericService) { interfaceClass = GenericService.class; if (StringUtils.isEmpty(generic)) { generic = Boolean.TRUE.toString(); } } else { try { interfaceClass = Class.forName(interfaceName, true, Thread.currentThread() .getContextClassLoader()); } catch (ClassNotFoundException e) { throw new IllegalStateException(e.getMessage(), e); } checkInterfaceAndMethods(interfaceClass, methods); checkRef(); generic = Boolean.FALSE.toString(); } if (local != null) { if ("true".equals(local)) { local = interfaceName + "Local"; } Class<?> localClass; try { localClass = ClassHelper.forNameWithThreadContextClassLoader(local); } catch (ClassNotFoundException e) { throw new IllegalStateException(e.getMessage(), e); } if (!interfaceClass.isAssignableFrom(localClass)) { throw new IllegalStateException("The local implementation class " + localClass.getName() + " not implement interface " + interfaceName); } } if (stub != null) { if ("true".equals(stub)) { stub = interfaceName + "Stub"; } Class<?> stubClass; try { stubClass = ClassHelper.forNameWithThreadContextClassLoader(stub); } catch (ClassNotFoundException e) { throw new IllegalStateException(e.getMessage(), e); } if (!interfaceClass.isAssignableFrom(stubClass)) { throw new IllegalStateException("The stub implementation class " + stubClass.getName() + " not implement interface " + interfaceName); } } // 检查Stub是否实现了interfaceClass 和 Mock属性,从而实现不同逻辑调用 checkStubAndLocal(interfaceClass); checkMock(interfaceClass); }
1)completeCompoundConfigs() 这个方法就是定义了取配置的优先级, provider > module > application 也就是说provider的配置存在了 则不会使用module的配置,module的配置存在了则不适用application的配置。
2)startConfigCenter这是配置中心的初始化,这里面如果是API的方式启动dubbo,则配置在serviceconfig中,如果时候spring的方式 则都会初始化在ConfigManager中。
void startConfigCenter() { if (configCenter == null) { // 如果配置了外部的配置中心 则赋值this.configCenter ConfigManager.getInstance().getConfigCenter().ifPresent(cc -> this.configCenter = cc); } if (this.configCenter != null) { // TODO there may have duplicate refresh this.configCenter.refresh(); //这个方法主要就是读取配置中心的配置 /** * 通过SPI机制不同类型配置中心配置 * 读取配置数据存到到Environment的 externalConfigurationMap 和 appExternalConfigurationMap */ prepareEnvironment(); } // 这边会统一刷新 ConfigManager.getInstance().refreshAll(); }
这边先说一下结论,下文会详细介绍。如果配置了configservice配置中心,那么会根据url通过dubbo的SPI来获取对应的配置中心实现,读取的配置信息存放在Environment的externalConfigurationMap和appExternalConfigurationMap中。配置优先级的策略是 SystemConfiguration -> AppExternalConfiguration ->ExternalConfiguration -> AbstractConfig -> PropertiesConfiguration。
如果没有配置configservice配置中心,同时配置了zookeeper注册中心,那么会将zookeeper作为配置中心,并且zookeeper上的配置不会覆盖本地配置,因为AbstractConfig位于AppExternalConfiguration之前,优先级如下SystemConfiguration -> AbstractConfig -> AppExternalConfiguration -> ExternalConfiguration -> PropertiesConfiguration
如果没有配置中心,也没有ZK的注册中心,那就默认会使用一个NopDynamicConfiguration作为外部配置中心实现,其内部啥都不处理。
this.configCenter.refresh()和 prepareEnvironment();将在下文介绍。
3) checkDefault -> createProviderIfAbsent 如果provider不存在,则new一个。
4)checkApplication 与 3)类似,如果application不存在则new一个,同时重新设置系统属性dubbo.service.shutdown.wait.seconds
5)checkRegistry:dubbo是支持多个注册中心的,并存储在List<RegistryConfig>。loadRegistriesFromBackwardConfig方法,如果没有配置register,则会从系统 dubbo.registry.address属性中获取或者找到配置文件路径并加载。convertRegistryIdsToRegistries方法表示,先读取外部化配置的注册配置,如果有,则对比本地配置好的注册信息,将外部的注册信息添加进来,前提是本地的注册信息个数不能超过外部的注册个数。如果没有外部配置,且本地有没有配置,则new。重点看useRegistryForConfigIfNecessary方法
private void useRegistryForConfigIfNecessary() { registries.stream().filter(RegistryConfig::isZookeeperProtocol).findFirst().ifPresent(rc -> { // we use the loading status of DynamicConfiguration to decide whether ConfigCenter has been initiated. // 如果存在zk的注册中心,则DynamicConfiguration没有的情况下,将zookeeper作为外部配置中心 Environment.getInstance().getDynamicConfiguration().orElseGet(() -> { ConfigManager configManager = ConfigManager.getInstance(); ConfigCenterConfig cc = configManager.getConfigCenter().orElse(new ConfigCenterConfig()); cc.setProtocol(rc.getProtocol()); cc.setAddress(rc.getAddress()); /** * 这里有一个细节,在将 registry 变为默认的 ConfigCenter 时候,将优先级设为false,这样就不会让这个配置中心去覆盖本地的配置。 */ cc.setHighestPriority(false); setConfigCenter(cc); // // 从配置中心刷新所有配置 startConfigCenter(); return null; }); }); }
接下来回到第二步2), 这个时候 this.configCenter != null返回true,执行refresh和prepareEnvironment。
refresh方法是抽象基类abstractConfig里面,用于该配置类根据Environment中的混合类compositeConfiguration配置来更新自身的配置数据,
public void refresh() { try { // 获取多个混合配置,包括SystemConfiguration AppExternalConfiguration ExternalConfiguration PropertiesConfiguration CompositeConfiguration compositeConfiguration = Environment.getInstance().getConfiguration(getPrefix(), getId()); // 自己在程序中,或者是在yml或者xml等配置文件中配置。 InmemoryConfiguration config = new InmemoryConfiguration(getPrefix(), getId()); // 将当前类的 信息放入到配置中 实际上是调用get方法来获取 config.addProperties(getMetaData()); // 看 isConfig的顺序,从而加载不同配置 外部配置优先 if (Environment.getInstance().isConfigCenterFirst()) { // The sequence would be: SystemConfiguration -> AppExternalConfiguration ->ExternalConfiguration -> AbstractConfig -> PropertiesConfiguration compositeConfiguration.addConfiguration(3, config); } else { //这个是外部配置不会覆盖本地配置 // The sequence would be: SystemConfiguration -> AbstractConfig -> AppExternalConfiguration -> ExternalConfiguration -> PropertiesConfiguration compositeConfiguration.addConfiguration(1, config); } // loop methods, get override value and set the new value back to method // 循环,从compositeConfiguration中填充参数到当前配置类 Method[] methods = getClass().getMethods(); for (Method method : methods) { if (ClassHelper.isSetter(method)) { try { // 在混合配置中循环遍历获取配合,直到获取到,因为这边是本地配置优先,所以ZK的配置不会覆盖掉本地 String value = StringUtils.trim(compositeConfiguration.getString(extractPropertyName(getClass(), method))); // isTypeMatch() is called to avoid duplicate and incorrect update, for example, we have two 'setGeneric' methods in ReferenceConfig. if (StringUtils.isNotEmpty(value) && ClassHelper.isTypeMatch(method.getParameterTypes()[0], value)) { method.invoke(this, ClassHelper.convertPrimitive(method.getParameterTypes()[0], value)); } } catch (NoSuchMethodException e) { logger.info("Failed to override the property " + method.getName() + " in " + this.getClass().getSimpleName() + ", please make sure every property has getter/setter method provided."); } } } } catch (Exception e) { logger.error("Failed to override ", e); } }
isConfigCenterFirst() = false正是上面cc.setHighestPriority(false);
接下来执行prepareEnvironment,这个过程就是去外部配置拿数据,然后赋值externalConfigurationMap和appExternalConfigurationMap
private void prepareEnvironment() { if (configCenter.isValid()) { if (!configCenter.checkOrUpdateInited()) { return; } // //这一步会使用SPI加载DynamicConfigurationFactory实现类,并根据URL中传入的protocol信息来选择具体的实现类, // 如果是ZK作为注册中心的话 那么会把zk也作为配置中心 DynamicConfiguration dynamicConfiguration = getDynamicConfiguration(configCenter.toUrl()); // //查看配置中信息中dubbo.properties这个文件的配置地址 String configContent = dynamicConfiguration.getConfig(configCenter.getConfigFile(), configCenter.getGroup()); String appGroup = application != null ? application.getName() : null; String appConfigContent = null; // 从配置中心获取 appConfigFile 配置 if (StringUtils.isNotEmpty(appGroup)) { appConfigContent = dynamicConfiguration.getConfig (StringUtils.isNotEmpty(configCenter.getAppConfigFile()) ? configCenter.getAppConfigFile() : configCenter.getConfigFile(), appGroup ); } try { // 将获取到的String 类型的 url类型 数据转化为 key value Environment.getInstance().setConfigCenterFirst(configCenter.isHighestPriority()); Environment.getInstance().updateExternalConfigurationMap(parseProperties(configContent)); Environment.getInstance().updateAppExternalConfigurationMap(parseProperties(appConfigContent)); } catch (IOException e) { throw new IllegalStateException("Failed to parse configurations from Config Center.", e); } } }
6)checkProtocol 协议检查
private void checkProtocol() { // 如果协议protocal没有配置 那就使用provider配置中的协议配置 if (CollectionUtils.isEmpty(protocols) && provider != null) { setProtocols(provider.getProtocols()); } // 这个与5)中convertRegistryIdsToRegistries类似 都是根据外部配置的protocal做检查 convertProtocolIdsToProtocols(); }
7)接下来做的事情如下,主要是检查泛化接口,local接口,stub接口,mock配置等。
// 设置Serviceconfig的变量 this.refresh(); //用于设置 MetadataReportConfig,尝试从配置中加载其配置,就算 MetadataReportConfig 不可用(即 unvalid),也只会报警告。 checkMetadataReport(); if (StringUtils.isEmpty(interfaceName)) { throw new IllegalStateException("<dubbo:service interface=\"\" /> interface not allow null!"); } // 对是否为 GenericService 进行适配(GenricService:泛化调用 是一种 Dubbo 提供的特殊角色,不需要引入jar包, // 直接通过GenericService进行调用,用于服务测试以及API服务网关,后面文章细分析) if (ref instanceof GenericService) { interfaceClass = GenericService.class; if (StringUtils.isEmpty(generic)) { generic = Boolean.TRUE.toString(); } } else { try { // 类加载接口 interfaceClass = Class.forName(interfaceName, true, Thread.currentThread() .getContextClassLoader()); } catch (ClassNotFoundException e) { throw new IllegalStateException(e.getMessage(), e); } // 比较接口中的方法是否都在methodConfig中 checkInterfaceAndMethods(interfaceClass, methods); // 比较实现类是否集成当前接口 checkRef(); // 不是泛化接口 generic = Boolean.FALSE.toString(); } if (local != null) { if ("true".equals(local)) { local = interfaceName + "Local"; } Class<?> localClass; try { localClass = ClassHelper.forNameWithThreadContextClassLoader(local); } catch (ClassNotFoundException e) { throw new IllegalStateException(e.getMessage(), e); } if (!interfaceClass.isAssignableFrom(localClass)) { throw new IllegalStateException("The local implementation class " + localClass.getName() + " not implement interface " + interfaceName); } } if (stub != null) { if ("true".equals(stub)) { // stub默认是接口名字上加上Stub stub = interfaceName + "Stub"; } Class<?> stubClass; try { stubClass = ClassHelper.forNameWithThreadContextClassLoader(stub); } catch (ClassNotFoundException e) { throw new IllegalStateException(e.getMessage(), e); } // stub实现类是否implementation了interfaceClass if (!interfaceClass.isAssignableFrom(stubClass)) { throw new IllegalStateException("The stub implementation class " + stubClass.getName() + " not implement interface " + interfaceName); } } // 检查Stub是否实现了interfaceClass checkStubAndLocal(interfaceClass); //检查Mock配置,从而实现不同逻辑调用 checkMock(interfaceClass); }
至此checkAndUpdateSubConfigs这一步讲完了,下一节就是如何暴露服务了。
,