This is the 9th day of my participation in the More text Challenge. For details, see more text Challenge

General documentation: Article directory Github: github.com/black-ant

A. The preface

This is the second part of the Seata section, which mainly describes how to complete the configuration of Seata Server processing, Seata startup can refer to Seata startup process, this document mainly contains the following content:

  • Configured scan
  • Load configuration
  • – Dynamic loading of configuration (next article)

2. Configure the scan

Conf/registry. Conf. These two files are loaded by the ConfigurationFactory

Starting point for configuration processing:

Let’s take a look at the starting point of the configuration. As mentioned earlier, when working with main, we worked with ParameterParser, and everything starts there:

  • Step 1: In Server # main, ParameterParser has been initiated
  • Step 2: ParameterParser # init, initiate the build of ConfigurationFactory, and pass in the Mode type
private void init(String[] args) {
  if (StringUtils.isBlank(storeMode)) {
  
    / /...storeMode = ConfigurationFactory.getInstance().getConfig(ConfigurationKeys.STORE_MODE, SERVER_DEFAULT_STORE_MODE); }}Copy the code

PS: Before reading the network configuration, the system obtains the connection information from the local configuration

Configured read classes:

AbstractConfiguration is used to read the configuration. There are several configuration classes:

  • FileConfiguration
  • SpringCloudConfiguration
  • ApolloConfiguration
  • NacosConfiguration
  • ConsulConfiguration
  • EtcdConfiguration
  • ZookeeperConfiguration

2.1 Scan Process

The registry. Conf file is read mainly by the ConfigurationFactory. Here’s the main logic:

2.1.1: Initialization of the configuration class

ConfigurationFactory # load: ConfigurationFactory # load: ConfigationFactory # load: ConfigationFactory # load: ConfigationFactory # load: ConfigationFactory # Load

  • Step 1: Obtain the name of the configuration class in sequence
  • Step 2: If the feature configuration is not set, the name -> Registry is obtained here
  • EnvValue can be thought of as a Profile in Spring, which gives you context-specific configuration
  • Step 4: Use the obtained parameters to build a Configuration object

private static final String SYSTEM_PROPERTY_SEATA_CONFIG_NAME = "seata.config.name";
private static final String REGISTRY_CONF_DEFAULT = "registry";
    
    
private static void load(a) {
        // Step 1: Obtain the name of the configuration class in sequence
        // Get the name of the configuration file from the system, usually null
        String seataConfigName = System.getProperty(SYSTEM_PROPERTY_SEATA_CONFIG_NAME);
        if (seataConfigName == null) {
            // ENV_SEATA_CONFIG_NAME -> SEATA_CONFIG_NAME
            seataConfigName = System.getenv(ENV_SEATA_CONFIG_NAME);
        }
        
        // Step 2: If the feature configuration is not set, the name -> Registry is obtained here
        if (seataConfigName == null) {
            seataConfigName = REGISTRY_CONF_DEFAULT;
        }
        
        // Step 3: envValue can be thought of as a Profile in Spring, which will get the environment specific configuration
        String envValue = System.getProperty(ENV_PROPERTY_KEY);
        if (envValue == null) {
            envValue = System.getenv(ENV_SYSTEM_KEY);
        }
        
        // Step 4: Build the Configuration object with the obtained parameters -> 2.2.2
        Configuration configuration = (envValue == null)?new FileConfiguration(seataConfigName,
                false) : new FileConfiguration(seataConfigName + "-" + envValue, false);
        Configuration extConfiguration = null;
        
        try {
            // Step 5: SPI extension, replace the extended configuration with the provide method of the ExtConfigurationProvider -> Pro:211001
            extConfiguration = EnhancedServiceLoader.load(ExtConfigurationProvider.class).provide(configuration);
        } catch (EnhancedServiceNotFoundException ignore) {

        } catch (Exception e) {
            LOGGER.error("failed to load extConfiguration:{}", e.getMessage(), e);
        }
        // End: if the extended configuration exists, the extended configuration instance is returned; otherwise, the file configuration instance is returned
        CURRENT_FILE_INSTANCE = extConfiguration == null ? configuration : extConfiguration;
}

// Pro:211001 SPI extension modeFor example, with seta-spring-boot-starer, Through SpringBootConfigurationProvider extensions, at this time will pass the application. The properties/application access parameters in yamlCopy the code

2.2.2: Build of the FileConfiguration system


public FileConfiguration(String name, boolean allowDynamicRefresh) {
    // Step 1: Get the File object from registry.conf
    File file = getConfigFile(name);
    if (file == null) {
        targetFilePath = null;
    } else {
        targetFilePath = file.getPath();
        // Step 2: Load FileConfig, which is bigger than we thought -> PS222001
        fileConfig = FileConfigFactory.load(file, name);
    }

    if (targetFilePath == null) {
        fileConfig = FileConfigFactory.load();
        this.allowDynamicRefresh = false;
    } else {
        targetFileLastModified = new File(targetFilePath).lastModified();
        this.allowDynamicRefresh = allowDynamicRefresh;
    }

    this.name = name;
    
    // Create a connection pool that will be used to initiate ConfigOperateRunnable
    configOperateExecutor = new ThreadPoolExecutor(CORE_CONFIG_OPERATE_THREAD, MAX_CONFIG_OPERATE_THREAD,
                Integer.MAX_VALUE, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>(),
                new NamedThreadFactory("configOperate", MAX_CONFIG_OPERATE_THREAD));
}                  


Copy the code

Step 1: Obtaining the getConfigFile

private File getConfigFile(String name) {
    // Whether to start with file
    boolean filePathCustom = name.startsWith(SYS_FILE_RESOURCE_PREFIX);
    // Get the File path, where is Registry
    String filePath = filePathCustom ? name.substring(SYS_FILE_RESOURCE_PREFIX.length()) : name;
    String decodedPath = URLDecoder.decode(filePath, StandardCharsets.UTF_8.name());
    
    // Get the final File: D:\ Java \study\seata_code\server\target\classes\registry.conf
    File targetFile = getFileFromFileSystem(decodedPath);
}
    
Copy the code

Step 2: FileconfigFactory. load(file, name) Load process

public static FileConfig load(File targetFile, String name) {
    String fileName = targetFile.getName();
    String configType = getConfigType(fileName);
    return loadService(configType, new Class[]{File.class, String.class}, new Object[]{targetFile, name});
}
Copy the code

Step 3: Use EnhancedServiceLoader to load

private static FileConfig loadService(String name, Class[] argsType, Object[] args) {
    FileConfig fileConfig = EnhancedServiceLoader.load(FileConfig.class, name, argsType, args);
    return fileConfig;
}

Copy the code

Step 4: EnhancedServiceLoader # load process

 private S loadExtension(String activateName, ClassLoader loader, Class[] argTypes, Object[] args) {
    // Get the extended configuration type
    loadAllExtensionClass(loader);
    ExtensionDefinition cachedExtensionDefinition = getCachedExtensionDefinition(activateName);
    return getExtensionInstance(cachedExtensionDefinition, loader, argTypes, args);                     
}  

// PS: finally convert File to SimpleFileConfig using constructor. NewInstance (args)

Copy the code

PS222001: FileConfig object details

This object is an interface that includes two implementation classes YamlFileConfig and SimpleFileConfig. As you can see from the following figure, there are many other configurations besides Nacos configuration, such as Java, and so on

2.2.3: loading the configuration

As the ConfigurationFactory. GetInstance () after the initialization, executes getConfig gain parameters, eventually call getLatestConfig:


public String getLatestConfig(String dataId, String defaultValue, long timeoutMills) {

    String value = getConfigFromSysPro(dataId);
    if(value ! =null) {
        return value;
    }
    {"dataId":"config.type","operation":"GET","timeout":true}
    ConfigFuture configFuture = new ConfigFuture(dataId, defaultValue, ConfigOperation.GET, timeoutMills);
    configOperateExecutor.submit(new ConfigOperateRunnable(configFuture));
    Object getValue = configFuture.get();
    return getValue == null ? null : String.valueOf(getValue);
}

Copy the code

The processing of ConfigOperateRunnable

public void run(a) {
    if(configFuture ! =null) {
        if (configFuture.isTimeout()) {
            setFailResult(configFuture);
            return;
        }
        try {
            if (allowDynamicRefresh) {
                long tempLastModified = new File(targetFilePath).lastModified();
                if (tempLastModified > targetFileLastModified) {
                    FileConfig tempConfig = FileConfigFactory.load(new File(targetFilePath), name);
                    if(tempConfig ! =null) { fileConfig = tempConfig; targetFileLastModified = tempLastModified; }}}if (configFuture.getOperation() == ConfigOperation.GET) {
                String result = fileConfig.getString(configFuture.getDataId());
                configFuture.setResult(result);
            } else if (configFuture.getOperation() == ConfigOperation.PUT) {
                configFuture.setResult(Boolean.TRUE);
            } else if (configFuture.getOperation() == ConfigOperation.PUTIFABSENT) {
                configFuture.setResult(Boolean.TRUE);
            } else if(configFuture.getOperation() == ConfigOperation.REMOVE) { configFuture.setResult(Boolean.TRUE); }}catch(Exception e) { setFailResult(configFuture); }}}Copy the code

3. Load the configuration

In the previous step, we created the ConfigurationFactory. During the creation process, we scanned the Configuration file, and then we created the Configuration object with buildConfiguration:

3.1 Loading entries of the configuration

// Take the Nacos configuration as an example:
private static Configuration buildConfiguration(a) {
    	// Get the data type from File -> get the data type from above
        String configTypeName = CURRENT_FILE_INSTANCE.getConfig(
                ConfigurationKeys.FILE_ROOT_CONFIG + ConfigurationKeys.FILE_CONFIG_SPLIT_CHAR
                        + ConfigurationKeys.FILE_ROOT_TYPE);

        if (StringUtils.isBlank(configTypeName)) {
            throw new NotSupportYetException("config type can not be null");
        }
    	// Get the Config type -> Nacos
        ConfigType configType = ConfigType.getType(configTypeName);

        Configuration extConfiguration = null;
        Configuration configuration;
    	// Do special processing on File
        if (ConfigType.File == configType) {
            String pathDataId = String.join(ConfigurationKeys.FILE_CONFIG_SPLIT_CHAR,
                    ConfigurationKeys.FILE_ROOT_CONFIG, FILE_TYPE, NAME_KEY);
            String name = CURRENT_FILE_INSTANCE.getConfig(pathDataId);
            configuration = new FileConfiguration(name);
            try {
                extConfiguration = EnhancedServiceLoader.load(ExtConfigurationProvider.class).provide(configuration);
            } catch (EnhancedServiceNotFoundException ignore) {

            } catch (Exception e) {
                LOGGER.error("failed to load extConfiguration:{}", e.getMessage(), e); }}else {
            // 3.2 -> Processing through the ConfigurationProvider
            configuration = EnhancedServiceLoader
                    .load(ConfigurationProvider.class, Objects.requireNonNull(configType).name()).provide();
        }
    
    
        try {
            Configuration configurationCache;
            if (null! = extConfiguration) { configurationCache = ConfigurationCache.getInstance().proxy(extConfiguration); }else {
                // 3.3 ConfigurationCache agent
                configurationCache = ConfigurationCache.getInstance().proxy(configuration);
            }
            if (null != configurationCache) {
                extConfiguration = configurationCache;
            }
        } catch (EnhancedServiceNotFoundException ignore) {

        } catch (Exception e) {
            LOGGER.error("failed to load configurationCacheProvider:{}", e.getMessage(), e);
        }
        return null == extConfiguration ? configuration : extConfiguration;
    }  


/ / Step 4. NacosConfigurationProvider processing
@LoadLevel(name = "Nacos", order = 1)
public class NacosConfigurationProvider implements ConfigurationProvider {
    @Override
    public Configuration provide(a) {
        returnNacosConfiguration.getInstance(); }}Copy the code

ConfigurationProvider

3.2 NacosConfiguration configuration

We look at the entire processing in a NacosConfiguration, which is actually typed:

// NacosConfiguration member variable
private static volatile ConfigService configService; / / note that volatile

// NacosConfiguration constructor
private NacosConfiguration(a) {
    if (configService == null) {
        try {
            // Step 1: Build ConfigService
            configService = NacosFactory.createConfigService(getConfigProperties());
            // Step 2: Initialize Seata Config
            initSeataConfig();
        } catch (NacosException e) {
            throw newRuntimeException(e); }}}Copy the code

Step 1 : NacosFactory.createConfigService(getConfigProperties()); -> PS:32001

GetConfigProperties () getConfigProperties() getConfigProperties() getConfigProperties() getConfigProperties() getConfigProperties() getConfigProperties()

private static Properties getConfigProperties(a) {
    // Step 1: Prepare the Properties object
    Properties properties = new Properties();
    
    // Step 2: Determine whether the System has the endpoint and serverAddr attributes
    if(System.getProperty(ENDPOINT_KEY) ! =null) {
        properties.setProperty(ENDPOINT_KEY, System.getProperty(ENDPOINT_KEY));
        properties.put(ACCESS_KEY, Objects.toString(System.getProperty(ACCESS_KEY), ""));
        properties.put(SECRET_KEY, Objects.toString(System.getProperty(SECRET_KEY), ""));
    } else if(System.getProperty(PRO_SERVER_ADDR_KEY) ! =null) {
        properties.setProperty(PRO_SERVER_ADDR_KEY, System.getProperty(PRO_SERVER_ADDR_KEY));
    } else {
        // Step 3: Obtain the Nacos Address path
        String address = FILE_CONFIG.getConfig(getNacosAddrFileKey());
        if(address ! =null) { properties.setProperty(PRO_SERVER_ADDR_KEY, address); }}if(System.getProperty(PRO_NAMESPACE_KEY) ! =null) {
        properties.setProperty(PRO_NAMESPACE_KEY, System.getProperty(PRO_NAMESPACE_KEY));
    } else {
        // Step 4: Set the namespace
        String namespace = FILE_CONFIG.getConfig(getNacosNameSpaceFileKey());
        if (namespace == null) {
            namespace = DEFAULT_NAMESPACE;
        }
        properties.setProperty(PRO_NAMESPACE_KEY, namespace);
    }
    String userName = StringUtils.isNotBlank(System.getProperty(USER_NAME)) 
        ? System.getProperty(USER_NAME)
        : FILE_CONFIG.getConfig(getNacosUserName());
    if (StringUtils.isNotBlank(userName)) {
        String password = StringUtils.isNotBlank(System.getProperty(PASSWORD)) 
            ? System.getProperty(PASSWORD)
            : FILE_CONFIG.getConfig(getNacosPassword());
        if(StringUtils.isNotBlank(password)) { properties.setProperty(USER_NAME, userName); properties.setProperty(PASSWORD, password); }}/ / is usually inherited configuration to {" namespace ":" ", "serverAddr" : "127.0.0.1:8848"}
    return properties;
}

Copy the code

PS: This is not getting the configuration from Nacos!

Step 2: Run initSeataConfig


private static void initSeataConfig(a) {
    try {
        // nacosDataId -> seataServer.properties
        String nacosDataId = getNacosDataId();
        
        Step 1: Obtain Nacos configuration from NacosConfigService (getNacosGroup -> SEATA_GROUP)
        String config = configService.getConfig(nacosDataId, getNacosGroup(), DEFAULT_CONFIG_TIMEOUT);
        if (StringUtils.isNotBlank(config)) {
            try (Reader reader 
                    = new InputStreamReader(new ByteArrayInputStream(config.getBytes()), StandardCharsets.UTF_8)) {
                // Stream processing, loaded into Properties
                seataConfig.load(reader);
            }
            NacosListener nacosListener = new NacosListener(nacosDataId, null);
            // Add NacosListener, which is Listernr for dynamic notificationconfigService.addListener(nacosDataId, getNacosGroup(), nacosListener); }}catch (NacosException | IOException e) {
        LOGGER.error("init config properties error", e); }}Copy the code

Add-on one: NacosConfigService

The class is com. Alibaba. Nacos. Client. Config tool, we to analysis the configuration of dynamic follow-up processing

3.3 ConfigurationCache

Now, this is kind of interesting, because you can also cache this way, by proxying the get method, you can actually cache the configuration

private static final ConcurrentHashMap<String, ObjectWrapper> CONFIG_CACHE = new ConcurrentHashMap<>();


public Configuration proxy(Configuration originalConfiguration) {
    return (Configuration)Enhancer.create(Configuration.class,
        (MethodInterceptor)(proxy, method, args, methodProxy) -> {
            // Check that the getmethod is not LatestConfig
            if(method.getName().startsWith(METHOD_PREFIX) && ! method.getName().equalsIgnoreCase(METHOD_LATEST_CONFIG)) { String rawDataId = (String)args[0];
                ObjectWrapper wrapper = CONFIG_CACHE.get(rawDataId);
                // Get the parameter name
                String type = method.getName().substring(METHOD_PREFIX.length());
                if(! ObjectWrapper.supportType(type)) { type =null;
                }
                if (null == wrapper) {
                    Object result = method.invoke(originalConfiguration, args);
                    // ObjectWrapper -> wrapper, data only exists in the cache when it is not empty
                    if(result ! =null) {
                        wrapper = newObjectWrapper(result, type); CONFIG_CACHE.put(rawDataId, wrapper); }}return wrapper == null ? null : wrapper.convertData(type);
            }
            // If there is no cache, use the original method
            return method.invoke(originalConfiguration, args);
    });
}

Copy the code

conclusion

This article goes a little deeper than the previous one.

The appendix

When searching for information, I found a very good picture. I read it for a long time while perfecting this article. I strongly recommend you to have a look at the original document, which is much clearer than ME.

PS: The brain says I can do it, the hand says you can do a P