zoukankan      html  css  js  c++  java
  • 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这一步讲完了,下一节就是如何暴露服务了。

  • 相关阅读:
    {Notes}{Latex}{multirow}
    [Reship] Mean Shift 算法介绍
    {Notes}{LaTeX}{enumerate}
    This is a test.
    js中的执行环境和作用域链
    js的预解析
    js笔试题一套(未完待续)
    使用setTimeout 来实现setInterval的效果
    ie6 ie7下报脚本错误"Expected identifier, string or number" 的原因和解决方法
    【雕爷学编程】Arduino动手做(63)---TCS3200D颜色识别传感器
  • 原文地址:https://www.cnblogs.com/gaojy/p/15705386.html
Copyright © 2011-2022 走看看