Dubbo学习记录(九)--服务导出的确定服务参数流程源码分析

Dubbo服务导出

难度系统 : ⭐⭐⭐⭐⭐

Dubbo服务导出,调用ServiceBean#export(), 再去调用父类ServiceConfig#export()方法,进行服务真正导出;

Dubbo服务导出大体流程

  1. Dubbo的每个Service注解都对应一个ServiceBean对象,每个ServiceBean对象都实现了Spring的ApplicationListener接口,当Spring项目启动结束后,会触发一个上下文刷新事件ContextRefreshEvent事件, 触发的方法是onApplicationEvent方法, ServiceBean的onApplicationEvent方法中,会调用ServiceBean#export(), 再去调用父类ServiceConfig#export()方法进行服务导出
  2. 会确定每个ServiceBean的配置参数,像timeout, protocol等, 这些参数的配置可以在以下的地方进行配置;
    2.1 JVM环境运行参数, 通过-D的方式来指定参数
    2.2 配置中心可以配置 项目的全局配置和应用配置, 相当于一个分布式共享的Dubbo.properties文件;
    2.3 @Service注解可以指定参数;
    2.4 项目中, 指定的dubbo.properties配置文件;
    这么多个地方可以指定配置参数, 在服务导出时,就需要确定每个Service使用的是哪些配置参数,即确定优先级最高的参数;
  3. 加载确定注册中心信息;
  4. 根据配置的Protocol协议启动容器, Dubbo协议对应的Netty, http对应的是tomcat/jetty, 并在接收请求过程中,根据我们的配置接收请求, 如超时事件, 最大连接数等;
  5. 容器启动完后,每个ServiceBean的服务配置会注册到注册中心,同时会注册监听服务配置,监听配置的变更;

主要就是这几个过程, 更简单点 确定服务配置参数 => 启动容器 => 注册到注册中心;

服务概念

  1. 传统三层调用中,Service接口就是服务的定义,而ServiceImpl就是服务的具体是实现;dubbo也是如此;
  2. Service接口 + 分组group + 版本号version 更详细的定义了一个服务;
  3. ip:port/service接口:通过IP端口号远程访问一个服务;
  4. ip:port/service接口 ?group =xxx & version=xxx & timeout=xxx & … : 通过IP端口指定远程服务器, 通过service接口 + 接口配置参数更详细的定义了一个服务,确定服务的唯一性;

如:

127.0.0.1:8080/com.dubbo.demo.DemoService?group=user&version=1.0&timout=5000&applicationName=demo

dubbo使用的是第四种,来唯一定义一个Service服务;

服务导出代码流程总结

  1. 加载确定服务的配置参数;
  2. 加载注册中心URL信息;
  3. 构造服务最终的URL;
  4. 根据协议配置Protolcol启动不同的容器,用来接收请求;
  5. 服务URL注册到注册中心中;
  6. 监听服务配置信息,配置发生变更,则重新进行服务导出;

加载确定服务的配置参数

  • 每个@Service对应一个ServiceBean服务配置实例, 而服务启动过程中,@DubboComponentScan注解执行完毕, 会把每个@Service转换一个个得ServiceBean实例, 并把@Service注解中的配置信息赋值给了ServiceBean实例,包括一些普通值timeout, 还有一些Dubbo配置, 如protocolConfig等;
  • 在Dubbo中,除开可以在@Service注解中给服务配置参数,还有很多地方也可以给服务配置参数
  1. resouce资源文件dubbo.properties: Dubbo会去扫描这些配置文件, 加载为PropertiesConfiguration;
  2. 配置中心:一个分布式的properties文件, 包括两种,一种是全局配置,另一个种是应用配置;在Dubbo的源码中AppExternalConfiguration表示应用配置,ExternalConfiguration表示全局配置。
  3. @Service注解: 对应的AbstractConfig;
  4. JVM环境参数: 通过-D执行配置, 对应的类是SystemConfiguration;
  • 如果这几处地方同时配置了同一个参数, 优先级由高到低为:
    SystemConfig > AppExternalConfiguration > ExternalConfiguration > AbstractConfig > PropertiesConfiguration
    即-D方式配置的参数优先级最高,配置中心次之,注解随后,dubbo.properties最后。

  • 因为配置参数同时可以在多个地方配置,所以服务导出的第一步就是确定服务的配置参数;

ServiceConfig#export()

    public synchronized void export() {
        //确定配置参数
        checkAndUpdateSubConfigs();
		//...省略部分代码
            // 导出服务
            doExport();
        }
    }

ServiceConfig#checkAndUpdateSubConfigs()

工作:

  1. completeCompoundConfigs补全配置参数
  2. 从配置中心获取应用配置和全局配置
  3. 获取ProviderConfig对象;
  4. 获取Protocol协议对象,默认为Dubbo协议;
  5. 获取ApplicationConfig对象;
  6. 获取Registry服务配置;
  7. 更新ServiceConfig 参数为最高优先级的配置;
  8. 检查当前服务是不是一个泛化服务
  9. 检查Stub和Local
  10. 检查Mock

    public void checkAndUpdateSubConfigs() {

        completeCompoundConfigs();

        // Config Center should always being started first.
        // 从配置中心获取配置,包括应用配置和全局配置
        // 把获取到的配置放入到Environment中的externalConfigurationMap和appExternalConfigurationMap中
        // 并刷新所有的Config属性
        startConfigCenter();

        // 如果没有ProviderConfig对象,则创建一个
        checkDefault();

        // 如果没有单独的配置protocols,那么就从provider获取配置的协议,添加到的ServiceConfig中去
        // 假如程序员在配置文件中配了一个dubbo协议,配置中心的全局配置或应用配置中也配置了一个协议,那么就会被添加到ServiceConfig中
        checkProtocol();


        checkApplication();


        // if protocol is not injvm checkRegistry
        // 如果protocol不是只有injvm协议,表示服务调用不是只在本机jvm里面调用,那就需要用到注册中心
        // 如果protocol是injvm,表示本地调用
        if (!isOnlyInJvm()) {
            checkRegistry();
        }

        // 刷新ServiceConfig
        this.refresh();

        // 如果配了metadataReportConfig,那么就刷新配置
        checkMetadataReport();

        if (StringUtils.isEmpty(interfaceName)) {
            throw new IllegalStateException(" interface not allow null!");
        }

        // 当前服务对应的实现类是一个GenericService,表示没有特定的接口
        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,并判断MethodConfig中对应的方法在接口中是否存在
            checkInterfaceAndMethods(interfaceClass, methods);
            // 实现类是不是该接口类型
            checkRef();
            generic = Boolean.FALSE.toString();
        }
        // local和stub一样,不建议使用了
        if (local != null) {
            // 如果本地存根为true,则存根类为interfaceName + "Local"
            if (Boolean.TRUE.toString().equals(local)) {
                local = interfaceName + "Local";
            }
            // 加载本地存根类
            Class<?> localClass;
            try {
                localClass = ClassUtils.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) {
            // 如果本地存根为true,则存根类为interfaceName + "Stub"
            if (Boolean.TRUE.toString().equals(stub)) {
                stub = interfaceName + "Stub";
            }
            Class<?> stubClass;
            try {
                stubClass = ClassUtils.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);
            }
        }
        // 检查local和stub
        checkStubAndLocal(interfaceClass);
        // 检查mock
        checkMock(interfaceClass);
    }

ServiceConfig#completeCompoundConfigs()’

  1. 配置了provider, 而没有配置application, module, registeries, monitor ,protocols ,configCenter , 则从provider种获取这些配置;
  2. 配置了module,而没有配置registries, configCenter, 则从module中获取这些配置;
  3. 配置了application, 而没有配置registries , monitor ,则从application中获取这些配置;
 private void completeCompoundConfigs() {
        // 如果配置了provider,那么则从provider中获取信息赋值其他属性,在这些属性为空的情况下
        if (provider != null) {
            if (application == null) {
                setApplication(provider.getApplication());
            }
            if (module == null) {
                setModule(provider.getModule());
            }
            if (registries == null) {
                setRegistries(provider.getRegistries());
            }
            if (monitor == null) {
                setMonitor(provider.getMonitor());
            }
            if (protocols == null) {
                setProtocols(provider.getProtocols());
            }
            if (configCenter == null) {
                setConfigCenter(provider.getConfigCenter());
            }
        }
        // 如果配置了module,那么则从module中获取信息赋值其他属性,在这些属性为空的情况下
        if (module != null) {
            if (registries == null) {
                setRegistries(module.getRegistries());
            }
            if (monitor == null) {
                setMonitor(module.getMonitor());
            }
        }
        // 如果配置了application,那么则从application中获取信息赋值其他属性,在这些属性为空的情况下
        if (application != null) {
            if (registries == null) {
                setRegistries(application.getRegistries());
            }
            if (monitor == null) {
                setMonitor(application.getMonitor());
            }
        }
    }

ServiceConfig#startConfigCenter()

  1. 配置configCenter, 从其他位置获取配置中心的相关属性信息,比如配置中心地址
  2. 更新属性后, 从配置中获取全局配置,应用配置;
  3. 拿到配置后, 刷新所有XXXconfig的属性;
    void startConfigCenter() {
        if (configCenter == null) {
            ConfigManager.getInstance().getConfigCenter().ifPresent(cc -> this.configCenter = cc);
        }

        // 如果配置了ConfigCenter
        if (this.configCenter != null) {

           
            // TODO there may have duplicate refresh
            this.configCenter.refresh();

            // 属性更新后,从远程配置中心获取数据(应用配置,全局配置)
            prepareEnvironment();
        }

        // 从配置中心取到配置数据后,刷新所有的XxConfig中的属性
        ConfigManager.getInstance().refreshAll();
    }
ServiceConfig#refresh

工作:

  1. 获取所有相关的dubbo配置;
  2. 创建一个Configuration对象,代表AbstractConfig;
  3. isConfigCenterFirst的值默认为true,所以优先级的顺序为:系统变量->配置中心应用配置->配置中心全局配置->注解或xml中定义->dubbo.properties文件;
  4. 使用反射技术赋最高优先级别配置的值;
public void refresh() {
        try {
            CompositeConfiguration compositeConfiguration = Environment.getInstance().getConfiguration(getPrefix(), getId());

            // 表示XxConfig对象本身- AbstractConfig
            Configuration config = new ConfigConfigurationAdapter(this);

            if (Environment.getInstance().isConfigCenterFirst()) {
                // The sequence would be: SystemConfiguration -> AppExternalConfiguration -> ExternalConfiguration -> AbstractConfig -> PropertiesConfiguration
                compositeConfiguration.addConfiguration(4, config);
            } else {
                // The sequence would be: SystemConfiguration -> AbstractConfig -> AppExternalConfiguration -> ExternalConfiguration -> PropertiesConfiguration
                compositeConfiguration.addConfiguration(2, config);
            }

            // loop methods, get override value and set the new value back to method
            //
            Method[] methods = getClass().getMethods();
            for (Method method : methods) {
                // 是不是setXX()方法
                if (MethodUtils.isSetter(method)) {
                    // 获取xx配置项的value
                    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) && ClassUtils.isTypeMatch(method.getParameterTypes()[0], value)) {
                        method.invoke(this, ClassUtils.convertPrimitive(method.getParameterTypes()[0], value));
                    }
                  // 是不是setParameters()方法
                } else if (isParametersSetter(method)) {
                    // 获取parameter配置项的value
                    String value = StringUtils.trim(compositeConfiguration.getString(extractPropertyName(getClass(), method)));
                    if (StringUtils.isNotEmpty(value)) {
                        Map<String, String> map = invokeGetParameters(getClass(), this);
                        map = map == null ? new HashMap<>() : map;
                        map.putAll(convert(StringUtils.parseParameters(value), ""));
                        invokeSetParameters(getClass(), this, map);
                    }
                }
            }
        } catch (Exception e) {
            logger.error("Failed to override ", e);
        }
    }
配置中心配置确定优先级
Environment.getInstance().getConfiguration(getPrefix(), getId())

getInstance():单例饿汉式;

public class Environment {
	//存放各种配置;
    private Map<String, PropertiesConfiguration> propertiesConfigs = new ConcurrentHashMap<>();
    private Map<String, SystemConfiguration> systemConfigs = new ConcurrentHashMap<>();
    private Map<String, EnvironmentConfiguration> environmentConfigs = new ConcurrentHashMap<>();
    private Map<String, InmemoryConfiguration> externalConfigs = new ConcurrentHashMap<>();
    private Map<String, InmemoryConfiguration> appExternalConfigs = new ConcurrentHashMap<>();

    private static final Environment INSTANCE = new Environment();
    public static Environment getInstance() {
        return INSTANCE;
    }
}

getConfiguration:获取配置,放入CompositeConfiguration 内部的List容器中;

  1. JVM环境变量
  2. 操作系统环境变量(我没接触过)
  3. 配置中心APP配置
  4. 配置中心Global全局配置
  5. dubbo.properties中的配置
  6. 返回compositeConfiguration实例;

可以看出分别获取,按顺序的就可以初步知道优先级了。
系统配置变量 > 配置中心 > dubbo文件配置;

    public CompositeConfiguration getConfiguration(String prefix, String id) {
        CompositeConfiguration compositeConfiguration = new CompositeConfiguration();
        // Config center has the highest priority

        // JVM环境变量
        compositeConfiguration.addConfiguration(this.getSystemConfig(prefix, id));
        // 操作系统环境变量
        compositeConfiguration.addConfiguration(this.getEnvironmentConfig(prefix, id));

        // 配置中心APP配置
        compositeConfiguration.addConfiguration(this.getAppExternalConfig(prefix, id));

        // 配置中心Global配置
        compositeConfiguration.addConfiguration(this.getExternalConfig(prefix, id));

        // dubbo.properties中的配置
        compositeConfiguration.addConfiguration(this.getPropertiesConfig(prefix, id));
        return compositeConfiguration;
    }

CompositeConfiguration类: 内部有一个List集合,存放配置;

public class CompositeConfiguration implements Configuration {
    /**
     * List holding all the configuration
     */
    private List<Configuration> configList = new LinkedList<Configuration>();
}
Environment.getInstance().isConfigCenterFirst()

可以得知: 默认情况configCenterFirst的值为true;

public class Environment {
    private static final Environment INSTANCE = new Environment();
    private boolean configCenterFirst = true;
    public boolean isConfigCenterFirst() {
        return configCenterFirst;
    }
  }  

因此,下面代码中, 往集合的第4个位置, 插入代表AbstractConfig的配置;

    public void refresh() {
        try {
            CompositeConfiguration compositeConfiguration = Environment.getInstance().getConfiguration(getPrefix(), getId());

            // 表示XxConfig对象本身- AbstractConfig
            Configuration config = new ConfigConfigurationAdapter(this);

            if (Environment.getInstance().isConfigCenterFirst()) {
                compositeConfiguration.addConfiguration(4, config);
            } else {
                compositeConfiguration.addConfiguration(2, config);
            }
	//...
}

最终配置的优先级为: 系统配置变量 > 配置中心 > @Service注解配置 > dubbo文件配置;

配置中心配置赋最高优先级别的值
  1. 获取ServiceConfig配置类的所有方法
  2. 遍历Method
  3. 判断是否为setter方法;
  4. 如果是setter方法, 则使用compositeConfiguration.getString(extractPropertyName(getClass(), method)获取属性的值
    4.1 遍历compositeConfiguration内部的已经排好序的Configuration集合List
    4.2 Configuration判断是否包含了当前key, 包含就说明找到优先级最高的配置,返回;
  5. 通过反射赋值;
  6. 如果不是setter方法, 是setParameters方法;
  7. 获取parameter配置项的value;
  8. 通过反射调用getParameters方法获取parameter值;
  9. 合并parameter的值, 通过反射调用setParameter方法赋值;
   public void refresh() {
        try {
			//...省略确定优先级别的代码
            Method[] methods = getClass().getMethods();
            for (Method method : methods) {
                // 是不是setXX()方法
                if (MethodUtils.isSetter(method)) {
                    // 获取xx配置项的value
                    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) && ClassUtils.isTypeMatch(method.getParameterTypes()[0], value)) {
                        method.invoke(this, ClassUtils.convertPrimitive(method.getParameterTypes()[0], value));
                    }
                  // 是不是setParameters()方法
                } else if (isParametersSetter(method)) {
                    // 获取parameter配置项的value
                    String value = StringUtils.trim(compositeConfiguration.getString(extractPropertyName(getClass(), method)));
                    if (StringUtils.isNotEmpty(value)) {
                        Map<String, String> map = invokeGetParameters(getClass(), this);
                        map = map == null ? new HashMap<>() : map;
                        map.putAll(convert(StringUtils.parseParameters(value), ""));
                        invokeSetParameters(getClass(), this, map);
                    }
                }
            }
        } catch (Exception e) {
        //...
        }
    }
prepareEnvironment()

目的: 获取配置中心的配置;

  1. 获取动态配置中心;
  2. 获取全局配置的内容configContent ;
  3. 获取管理组名;
  4. 如果存在管理组,则有App应用配置, 获取应用配置的内容appConfigContent ;
  5. 设置configCenterFirst属性值为true;
  6. 解析全局配置内容,并赋值给Environment#externalConfigurationMap;
  7. 解析应用配置内容, 并赋值给Environment#appExternalConfigurationMap
private void prepareEnvironment() {
        if (configCenter.isValid()) {
            if (!configCenter.checkOrUpdateInited()) {
                return;
            }

            // 动态配置中心,管理台上的配置中心
            DynamicConfiguration dynamicConfiguration = getDynamicConfiguration(configCenter.toUrl());

            // 如果是zookeeper,获取的就是/dubbo/config/dubbo/dubbo.properties节点中的内容
            String configContent = dynamicConfiguration.getProperties(configCenter.getConfigFile(), configCenter.getGroup());

            String appGroup = application != null ? application.getName() : null;
            String appConfigContent = null;
            if (StringUtils.isNotEmpty(appGroup)) {
                // 获取的就是/dubbo/config/dubbo-demo-consumer-application/dubbo.properties节点中的内容
                // 这里有bug
                appConfigContent = dynamicConfiguration.getProperties
                        (StringUtils.isNotEmpty(configCenter.getAppConfigFile()) ? configCenter.getAppConfigFile() : configCenter.getConfigFile(),
                         appGroup
                        );
            }
            try {
                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);
            }
        }
    }
ConfigManager.getInstance().refreshAll()

上面的步骤只是刷新ConfigCenterConfig的最新值;而其他配置类例如ApplicationConfig, ProtocolConfig等的值不是最高优先级别的值, 因此这些配置类也需要刷新, 赋最高优先级别的值;

    public void refreshAll() {
        // refresh all configs here,
        getApplication().ifPresent(ApplicationConfig::refresh);
        getMonitor().ifPresent(MonitorConfig::refresh);
        getModule().ifPresent(ModuleConfig::refresh);

        getProtocols().values().forEach(ProtocolConfig::refresh);
        getRegistries().values().forEach(RegistryConfig::refresh);
        getProviders().values().forEach(ProviderConfig::refresh);
        getConsumers().values().forEach(ConsumerConfig::refresh);
    }

代码写得非常漂亮, 使用反射,因此通用性很高;
过程类似ConfigCenterConfig, 不重复码了。

ServiceConfig#checkDefault()

目的: 如果没有ProviderConfig对象,则创建一个

    private void checkDefault() {
        createProviderIfAbsent();
    }
    private void createProviderIfAbsent() {
        if (provider != null) {
            return;
        }
        setProvider(
                ConfigManager.getInstance()
                        .getDefaultProvider()
                        .orElseGet(() -> {
                        	//创建ProviderConfig实例,并赋最高优先级别的值;
                            ProviderConfig providerConfig = new ProviderConfig();
                            providerConfig.refresh();
                            return providerConfig;
                        })
        );
    }
    //给配置管理器设置Provider;
    public void setProvider(ProviderConfig provider) {
        ConfigManager.getInstance().addProvider(provider);
        this.provider = provider;
    }   

ServiceConfig#checkProtocol

目的:获取设置ProtocolConfig;
工作:

  1. 如果没有单独的配置protocols,那么就从provider获取配置的协议,添加到的ServiceConfig中去
  2. 假如程序员在配置文件中配了一个dubbo协议,配置中心的全局配置或应用配置中也配置了一个协议,那么就会被添加到ServiceConfig中
    2.1 获取全局配置的protocol, 根据前缀"dubbo.protocols." 从externalConfigurationMap中获取;
    2.2 获取应用配置的protocol, 根据前缀"dubbo.protocols." 从appExternalConfigurationMap中获取;
    2.3 拼接配置;
    2.4 如果配置中心没有配置,则使用默认的Dubbo协议配置, 创建一个默认的ProtocolConfig 赋值给ConfigManager管理;
    2.5 如果配置中心有配置,则分别创建每个配置创建一个ProtocolConfig, 然后一起交给ConfigManager管理;
private void checkProtocol() {
		//如果没有单独的配置protocols,那么就从provider获取配置的协议,添加到的ServiceConfig中去
        if (CollectionUtils.isEmpty(protocols) && provider != null) {
            setProtocols(provider.getProtocols());
        }
        convertProtocolIdsToProtocols();
    }

    private void convertProtocolIdsToProtocols() {

        if (StringUtils.isEmpty(protocolIds) && CollectionUtils.isEmpty(protocols)) {
            List<String> configedProtocols = new ArrayList<>();

            // 从配置中心的全局配置获取dubbo.protocols.的配置项值
            configedProtocols.addAll(getSubProperties(Environment.getInstance()
                    .getExternalConfigurationMap(), PROTOCOLS_SUFFIX));

            // 从配置中心的应用配置获取dubbo.protocols.的配置项值
            configedProtocols.addAll(getSubProperties(Environment.getInstance()
                    .getAppExternalConfigurationMap(), PROTOCOLS_SUFFIX));

            // 用,号join所有的protocol
            protocolIds = String.join(",", configedProtocols);
        }

        if (StringUtils.isEmpty(protocolIds)) {
            // 如果配置中心没有配置协议,就取默认的协议
            if (CollectionUtils.isEmpty(protocols)) {
                setProtocols(
                        ConfigManager.getInstance().getDefaultProtocols()
                                .filter(CollectionUtils::isNotEmpty)
                                .orElseGet(() -> {
                                    ProtocolConfig protocolConfig = new ProtocolConfig();
                                    protocolConfig.refresh();
                                    return new ArrayList<>(Arrays.asList(protocolConfig));
                                })
                );
            }
        } else {
            // 如果配置了
            String[] arr = COMMA_SPLIT_PATTERN.split(protocolIds);
            List<ProtocolConfig> tmpProtocols = CollectionUtils.isNotEmpty(protocols) ? protocols : new ArrayList<>();

            // 把从配置中心配置的协议添加到服务的协议列表中去
            Arrays.stream(arr).forEach(id -> {
                if (tmpProtocols.stream().noneMatch(prot -> prot.getId().equals(id))) {
                    tmpProtocols.add(ConfigManager.getInstance().getProtocol(id).orElseGet(() -> {
                        ProtocolConfig protocolConfig = new ProtocolConfig();
                        protocolConfig.setId(id);
                        protocolConfig.refresh();
                        return protocolConfig;
                    }));
                }
            });
            if (tmpProtocols.size() > arr.length) {
                throw new IllegalStateException("Too much protocols found, the protocols comply to this service are :" + protocolIds + " but got " + protocols
                        .size() + " registries!");
            }
            setProtocols(tmpProtocols);
        }
    }

checkApplication()

如果没有配置application,则创建一个ApplicationConfig实例,赋值给ConfigManager;

protected void checkApplication() {
        // for backward compatibility
        createApplicationIfAbsent();
		//...省略不重要代码
    }
 private void createApplicationIfAbsent() {
        if (this.application != null) {
            return;
        }
        ConfigManager configManager = ConfigManager.getInstance();
        setApplication(
                configManager
                        .getApplication()
                        .orElseGet(() -> {
                            ApplicationConfig applicationConfig = new ApplicationConfig();
                            applicationConfig.refresh();
                            return applicationConfig;
                        })
        );
    }
    

ServiceConfig#refresh()

  • this.refresh()逻辑和ConfigCenterConfig的刷新代码逻辑一样的;
  • ServiceConfig对象的属性可能是有值的,也可能是没有值的,这时需要从其他位置获取属性值,来进行属性的覆盖
  • ServiceConfig本身有很多配置, 通过刷新获取最高优先级的配置值;例如timeout, 如果注解上没有配置这个值,就会从其他地方获取配置;
  • 覆盖的优先级,从大到小为系统变量->配置中心应用配置->配置中心全局配置->注解或xml中定义->dubbo.properties文件
  • 将当前ServiceConfig上的set方法所对应的属性更新为优先级最高的值
public void refresh() {
        try {
            //TODO : 都没启动configCenter,配置中心的配置是怎么拿到的????
            CompositeConfiguration compositeConfiguration = Environment.getInstance().getConfiguration(getPrefix(), getId());

            // 表示XxConfig对象本身- AbstractConfig
            Configuration config = new ConfigConfigurationAdapter(this);

            if (Environment.getInstance().isConfigCenterFirst()) {
                // The sequence would be: SystemConfiguration -> AppExternalConfiguration -> ExternalConfiguration -> AbstractConfig -> PropertiesConfiguration
                compositeConfiguration.addConfiguration(4, config);
            } else {
                // The sequence would be: SystemConfiguration -> AbstractConfig -> AppExternalConfiguration -> ExternalConfiguration -> PropertiesConfiguration
                compositeConfiguration.addConfiguration(2, config);
            }

            // loop methods, get override value and set the new value back to method
            //
            Method[] methods = getClass().getMethods();
            for (Method method : methods) {
                // 是不是setXX()方法
                if (MethodUtils.isSetter(method)) {
                    // 获取xx配置项的value
                    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) && ClassUtils.isTypeMatch(method.getParameterTypes()[0], value)) {
                        method.invoke(this, ClassUtils.convertPrimitive(method.getParameterTypes()[0], value));
                    }
                  // 是不是setParameters()方法
                } else if (isParametersSetter(method)) {
                    // 获取parameter配置项的value
                    String value = StringUtils.trim(compositeConfiguration.getString(extractPropertyName(getClass(), method)));
                    if (StringUtils.isNotEmpty(value)) {
                        Map<String, String> map = invokeGetParameters(getClass(), this);
                        map = map == null ? new HashMap<>() : map;
                        map.putAll(convert(StringUtils.parseParameters(value), ""));
                        invokeSetParameters(getClass(), this, map);
                    }
                }
            }
        } catch (Exception e) {
            logger.error("Failed to override ", e);
        }
    }

到这里,主要的服务导出确定服务配置参数过程就完了。代码写得非常nice, 通用性非常强;

总结

确定配置参数仅仅只是服务导出的第一步, 反射技术还是必须要会的技术, 基本框架底层都使用, 任重倒运;

你可能感兴趣的:(dubbo,rpc,java,dubbo)