Dubbo Learning Record (9)--Source Code Analysis of Determination of Service Parameter Process for Service Export

Dubbo service export

Difficulty system: ⭐⭐⭐⭐⭐

For Dubbo service export, call ServiceBean#export(), and then call the parent class ServiceConfig#export() method to actually export the service;

Dubbo service export general process

  1. Each Service annotation of Dubbo corresponds to a ServiceBean object. Each ServiceBean object implements the Spring ApplicationListener interface. When the Spring project is started, a context refresh event ContextRefreshEvent event will be triggered. The trigger method is the onApplicationEvent method, ServiceBean onApplicationEvent In the method, ServiceBean#export() will be called, and then the parent class ServiceConfig#export() method will be called to export the service
  2. It will determine the configuration parameters of each ServiceBean, such as timeout, protocol, etc. The configuration of these parameters can be configured in the following places;
    2.1 JVM environment operating parameters, specify parameters by -D
    2.2 The configuration center can configure the global configuration of the project And application configuration, which is equivalent to a distributed shared Dubbo.properties file;
    2.3 @Service annotation can specify parameters;
    2.4 In the project, the specified dubbo.properties configuration file;
    so many places can specify configuration parameters, when the service is exported, It is necessary to determine which configuration parameters are used by each Service, that is, to determine the parameters with the highest priority;
  3. Load and determine the registration center information;
  4. Start the container according to the configured Protocol, the Dubbo protocol corresponds to Netty, http corresponds to tomcat/jetty, and in the process of receiving requests, receive requests according to our configuration, such as timeout events, maximum number of connections, etc.;
  5. After the container is started, the service configuration of each ServiceBean will be registered to the registration center, and at the same time, it will register to monitor the service configuration and monitor the configuration changes;

It is mainly these processes, it is simpler to determine the service configuration parameters => start the container => register to the registration center;

service concept

  1. In the traditional three-tier call, the Service interface is the definition of the service, and the ServiceImpl is the specific implementation of the service; the same is true for dubbo;
  2. Service interface + group group + version number version defines a service in more detail;
  3. ip:port/service interface: remotely access a service through the IP port number;
  4. ip:port/service interface?group =xxx & version=xxx & timeout=xxx & … : Specify the remote server through the IP port, define a service in more detail through the service interface + interface configuration parameters, and determine the uniqueness of the service;

like:

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

Dubbo uses the fourth type to uniquely define a Service service;

Service export code process summary

  1. Load the configuration parameters of the identified service;
  2. Load the registration center URL information;
  3. Construct the final URL of the service;
  4. Configure Protocolcol to start different containers according to the protocol to receive requests;
  5. The service URL is registered in the registration center;
  6. Monitor service configuration information, if the configuration changes, re-export the service;

Load the configuration parameters that determine the service

  • Each @Service corresponds to a ServiceBean service configuration instance, and during the service startup process, when the @DubboComponentScan annotation is executed, each @Service will be converted into a ServiceBean instance one by one, and the configuration information in the @Service annotation will be assigned to the ServiceBean instance , including some common values ​​timeout, and some Dubbo configurations, such as protocolConfig, etc.;
  • In Dubbo, in addition to configuring parameters for the service in the @Service annotation, there are many places where you can also configure parameters for the service
  1. Resource resource file dubbo.properties: Dubbo will scan these configuration files and load them as PropertiesConfiguration;
  2. Configuration center: a distributed properties file, including two types, one is global configuration and the other is application configuration; in Dubbo's source code, AppExternalConfiguration means application configuration, and ExternalConfiguration means global configuration.
  3. @Service annotation: corresponding AbstractConfig;
  4. JVM environment parameters: Execute configuration through -D, and the corresponding class is SystemConfiguration;
  • If the same parameter is configured in these places at the same time, the priority from high to low is:
    SystemConfig > AppExternalConfiguration > ExternalConfiguration > AbstractConfig > PropertiesConfiguration,
    that is, the parameter configured in -D mode has the highest priority, followed by the configuration center, followed by the annotation, dubbo. properties last.

  • Because configuration parameters can be configured in multiple places at the same time, the first step in exporting a service is to determine the configuration parameters of the service;

ServiceConfig#export()

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

ServiceConfig#checkAndUpdateSubConfigs()

Work:

  1. completeCompoundConfigs completion configuration parameters
  2. Obtain application configuration and global configuration from configuration center
  3. Get the ProviderConfig object;
  4. Get the Protocol protocol object, the default is the Dubbo protocol;
  5. Get the ApplicationConfig object;
  6. Obtain Registry service configuration;
  7. Update the ServiceConfig parameter to the configuration with the highest priority;
  8. Check if the current service is a generalized service
  9. Check Stub and Local
  10. Check 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("<dubbo:service interface=\"\" /> 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 is configured, but application, module, registeries, monitor, protocols, configCenter are not configured, and these configurations are obtained from the provider;
  2. The module is configured, but the registries and configCenter are not configured, and these configurations are obtained from the module;
  3. The application is configured, but the registries and monitor are not configured, and these configurations are obtained from the 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. Configure configCenter to obtain relevant attribute information of the configuration center from other locations, such as the address of the configuration center
  2. After updating the properties, get the global configuration from the configuration and apply the configuration;
  3. After getting the configuration, refresh all XXXconfig properties;
    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

Work:

  1. Get all relevant dubbo configurations;
  2. Create a Configuration object representing AbstractConfig;
  3. The value of isConfigCenterFirst is true by default, so the order of priority is: system variable -> configuration center application configuration -> configuration center global configuration -> annotation or definition in xml -> dubbo.properties file;
  4. Use reflection technology to assign the value of the highest priority configuration;
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);
        }
    }
Configuration center configuration determines priority
Environment.getInstance().getConfiguration(getPrefix(), getId())

getInstance(): Singleton hungry Chinese style;

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: Get the configuration and put it into the List container inside the CompositeConfiguration;

  1. JVM environment variables
  2. Operating system environment variables (I have not touched)
  3. Configuration center APP configuration
  4. Configuration Center Global global configuration
  5. Configuration in dubbo.properties
  6. Return compositeConfiguration instance;

It can be seen that they are obtained separately, and the priority can be initially known in order.
System configuration variables > configuration center > dubbo file configuration;

    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 class: There is a List collection inside to store the configuration;

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

It can be known that: By default, the value of configCenterFirst is true;

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

Therefore, in the following code, insert the configuration representing AbstractConfig into the fourth position of the collection;

    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);
            }
	//...
}

The priority of the final configuration is: System configuration variables > Configuration Center > @Service annotation configuration > dubbo file configuration;

The configuration center configuration assigns the highest priority value
  1. Get all methods of the ServiceConfig configuration class
  2. Traverse Method
  3. Determine whether it is a setter method;
  4. If it is a setter method, use compositeConfiguration.getString(extractPropertyName(getClass(), method) to get the value of the property
    4.1 Traverse the sorted Configuration collection List inside the compositeConfiguration
    4.2 Configuration judges whether the current key is included, and if it is included, it means that it is found first The highest level configuration, return;
  5. assignment via reflection;
  6. If it is not a setter method, it is a setParameters method;
  7. Get the value of the parameter configuration item;
  8. Get the parameter value by calling the getParameters method through reflection;
  9. Merge the value of the parameter, and assign the value by calling the setParameter method through reflection;
   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()

Purpose: Get the configuration of the configuration center;

  1. Obtain dynamic configuration center;
  2. Get the content configContent of the global configuration;
  3. Get the management group name;
  4. If there is a management group, there is an App application configuration, and the content appConfigContent of the application configuration is obtained;
  5. Set the configCenterFirst property value to true;
  6. Parse the global configuration content and assign it to Environment#externalConfigurationMap;
  7. Parse the application configuration content and assign it to 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()

The above steps only refresh the latest value of ConfigCenterConfig; while the values ​​of other configuration classes such as ApplicationConfig, ProtocolConfig, etc. are not the highest priority value, so these configuration classes also need to be refreshed and given the highest priority value;

    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);
    }

The code is very beautifully written, using reflection, so the versatility is very high;
the process is similar to ConfigCenterConfig, and there is no need to repeat the code.

ServiceConfig#checkDefault()

Purpose: If there is no ProviderConfig object, create one

    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

Purpose: get settings ProtocolConfig;
work:

  1. If there is no separate configuration protocols, then get the configured protocol from the provider and add it to the ServiceConfig
  2. If the programmer configures a dubbo protocol in the configuration file, and configures a protocol in the global configuration or application configuration of the configuration center, it will be added to ServiceConfig. 2.1
    Obtain the protocol of the global configuration, according to the prefix "dubbo.protocols. " Obtained from externalConfigurationMap;
    2.2 Obtain the protocol of the application configuration, and obtain it from appExternalConfigurationMap according to the prefix "dubbo.protocols.";
    2.3 Splicing configuration;
    2.4 If there is no configuration in the configuration center, use the default Dubbo protocol configuration to create a default ProtocolConfig Assign values ​​to ConfigManager for management;
    2.5 If there are configurations in the configuration center, create a ProtocolConfig for each configuration, and then hand them over to ConfigManager for management;
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()

If no application is configured, create an ApplicationConfig instance and assign it to 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()

  • The logic of this.refresh() is the same as the refresh code logic of ConfigCenterConfig;
  • The attributes of the ServiceConfig object may have values ​​or may not have values. In this case, attribute values ​​need to be obtained from other locations to overwrite the attributes
  • ServiceConfig itself has many configurations, and the configuration value with the highest priority is obtained by refreshing; for example, timeout, if this value is not configured in the annotation, the configuration will be obtained from other places;
  • The priority of coverage, from large to small, is system variable -> configuration center application configuration -> configuration center global configuration -> annotation or defined in xml -> dubbo.properties file
  • Update the attribute corresponding to the set method on the current ServiceConfig to the highest priority value
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);
        }
    }

At this point, the main process of exporting services to determine service configuration parameters is over. The code is written very nicely, and the versatility is very strong;

Summarize

Determining the configuration parameters is only the first step in exporting the service. Reflection technology is still a must-know technology, which is used at the bottom of the basic framework, which is a heavy responsibility;

Guess you like

Origin blog.csdn.net/yaoyaochengxian/article/details/123488452