Disconf Source Code Analysis in Startup Process Analysis

Keywords: Java xml Zookeeper Spring

Disconf's startup mainly includes two scans and the validity of the XML configuration. There are two chapters altogether. The first chapter mainly introduces the first static scanning process.

From the entry analysis, through Disconf help document, you can see that xml must add the following configuration.

<!-- Use disconf The following configuration must be added -->
<bean id="disconfMgrBean" class="com.baidu.disconf.client.DisconfMgrBean"
      destroy-method="destroy">
    <property name="scanPackage" value="com.demo.disconf"/>
</bean>
<bean id="disconfMgrBean2" class="com.baidu.disconf.client.DisconfMgrBeanSecond"
      init-method="init" destroy-method="destroy">
</bean>

DisconfMgrBean inherits ApplicationContextAware, and disconf overloads postProcessBean Definition Registry () for the first load.

/**
 * The first scan < br />.
 * The Bean definition inside Spring is initialized and executed, which is the highest priority
 */
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {

    // For compatibility
    DisconfCenterHostFilesStore.getInstance().addJustHostFileSet(fileList);

    // Starting from 2.6.23, disconf supports scanPackage to pass multiple package paths, split through "," and then cut the package file and reserve it into the list.
    List<String> scanPackList = StringUtil.parseStringToStringList(scanPackage, SCAN_SPLIT_TOKEN);
    // unique
    Set<String> hs = new HashSet<String>();
    hs.addAll(scanPackList);
    scanPackList.clear();
    scanPackList.addAll(hs);

    // Get DisconfMgr by a single way of static pre-loading and set the application context
    DisconfMgr.getInstance().setApplicationContext(applicationContext);
    // Scan
    DisconfMgr.getInstance().firstScan(scanPackList);

    // register java bean
    registerAspect(registry);
}

Look at DisconfMgr's first Scan () method.

/**
 * First scan, static scan for annotation config
 */
protected synchronized void firstScan(List<String> scanPackageList) {

    // isFirstInit is used to determine the first load, combined with synchronized locking to ensure that
    // This function cannot be called twice
    if (isFirstInit) {
        LOGGER.info("DisConfMgr has been init, ignore........");
        return;
    }

    try {
        // Import configuration
        ConfigMgr.init();
        // registry
        Registry registry = RegistryFactory.getSpringRegistry(applicationContext);

        // Scanner
        scanMgr = ScanFactory.getScanMgr(registry);

        // First scan and storage
        scanMgr.firstScan(scanPackageList);

        // Getting Data/Injection/Watch
        disconfCoreMgr = DisconfCoreFactory.getDisconfCoreMgr(registry);
        disconfCoreMgr.process();

        //
        isFirstInit = true
    } catch (Exception e) {
        LOGGER.error(e.toString(), e);
    }
}

First look at the implementation of ConfigMgr.init(); the configuration file of disconf includes two parts: system self-contained and user configuration, which are parsed and processed by DisClientSysConfig and DisClientConfig respectively. It is also a single implementation.

// Import System Configuration
DisClientSysConfig.getInstance().loadConfig(null);
// Configuration of calibration system
DisInnerConfigHelper.verifySysConfig();
// Import user configuration
DisClientConfig.getInstance().loadConfig(null);
// Verify user configuration
DisInnerConfigHelper.verifyUserConfig();
isInit = true;

First look at all the properties of DisClientSysConfig with a comment @DisInnerConfigAnnotation, and in the later configuration process, they are assigned by reflection.

/**
 * STORE URL
 *
 * @author
 * @since 1.0.0
 */
@DisInnerConfigAnnotation(name = "disconf.conf_server_store_action")
public String CONF_SERVER_STORE_ACTION;

/**
 * STORE URL
 *
 * @author
 * @since 1.0.0
 */
@DisInnerConfigAnnotation(name = "disconf.conf_server_zoo_action")
public String CONF_SERVER_ZOO_ACTION;

/**
 * Get the URL of the number of remote hosts
 *
 * @author
 * @since 1.0.0
 */
@DisInnerConfigAnnotation(name = "disconf.conf_server_master_num_action")
public String CONF_SERVER_MASTER_NUM_ACTION;

/**
 * Download folders, remote files will be downloaded here
 *
 * @author
 * @since 1.0.0
 */
@DisInnerConfigAnnotation(name = "disconf.local_download_dir")
public String LOCAL_DOWNLOAD_DIR;

The loadConfig method of DisClientSysConfig calls the DisconfAutowareConfig.autowareConfig() method. The default system configuration file is disconf_sys.properties.

The main idea of DisconfAutoware Config is to import the configuration content into Properties through DisconfAutoware Config, and then assign the corresponding properties and configurations of the above DisClientSysConfig by reflection.

Finally, configuration checking is performed by DisInnerConfigHelper.verifySysConfig().

DisClientConfig user configurations are similar to system configurations, with several differences:

  1. DisClientConfig attributes are different
  2. In addition to the default disconf.properties of the system, the read file path can also specify the configuration file by starting parameter "- d".
  3. When the configuration file is read, it is also overwritten by reading system parameters or attributes imported from the command line, which is preferred.

After reading the Config configuration file, continue scanning.

// registry
// Processing Context into a Context Processing Tool
Registry registry = RegistryFactory.getSpringRegistry(applicationContext);

// Scanner
// Get Scanner Tools through Scanner Factory
scanMgr = ScanFactory.getScanMgr(registry);

// First scan and storage
scanMgr.firstScan(scanPackageList);

// Getting Data/Injection/Watch
disconfCoreMgr = DisconfCoreFactory.getDisconfCoreMgr(registry);
disconfCoreMgr.process();

ScanMgrImpl constructor from ScanFactory has a lot of information, and ScanMgrImpl is the center of the scanning module.

public ScanMgrImpl(Registry registry) {
    this.registry = registry;
    // configuration file
    staticScannerMgrList.add(StaticScannerMgrFactory.getDisconfFileStaticScanner());
    // Configuration item
    staticScannerMgrList.add(StaticScannerMgrFactory.getDisconfItemStaticScanner());
    // Non-annotated managed configuration files
    staticScannerMgrList.add(StaticScannerMgrFactory.getDisconfNonAnnotationFileStaticScanner());
}

In addition to the assignment of registry context, there are also three types of configuration scanning tools loaded into the static Scanner MgrList. In the subsequent scanning process, the tools are traversed to handle the configuration on Disconf separately.

Start scanning static files and store the results in the store. (DisconfCenterStore serves as the configuration warehouse center, which will be introduced later)

/**
 * Scan and store (static)
 */
public void firstScan(List<String> packageNameList) throws Exception {
    // Obtaining Scanning Objects and Analyzing Integration
    scanModel = scanStaticStrategy.scan(packageNameList);
    // Increase non-annotated configuration
    scanModel.setJustHostFiles(DisconfCenterHostFilesStore.getInstance().getJustHostFiles());
    // Put in the warehouse
    for (StaticScannerMgr scannerMgr : staticScannerMgrList) {
        // Scanning into warehouse
        scannerMgr.scanData2Store(scanModel);
        // What KEY to Ignore
        scannerMgr.exclude(DisClientConfig.getInstance().getIgnoreDisconfKeySet());
    }
}

ScanStaticModel configures storage objects for scanned content.

ReflectionScanStatic scans files under paths for static annotations and integrates them into ScanStatic Model. ReflectionScanStatic's main way of processing is to get all the annotations supported by Disconf through reflection (see the help document specifically), which will be parsed after the initial scan and assigned to the ScanStaticModel object.

After obtaining the scanModel for static scanning, add a non-annotated configuration, which the official annotation says is abandoned, just compatible.

Finally, the static Scanner MgrList is traversed, scanModel is processed by various scanning tools, and the results are added to the DisconfCenterStore repository.

Look at the implementation of the scanning tool. Take StaticScanner FileMgrImpl for example.

Inheriting from the StaticScanner Mgr interface, the scanData2Store() and exclude() methods are implemented. scanData2Store() scans the data and writes it to the warehouse. exclude() removes the overlooked configuration specified by Config from the warehouse.

@Override
public void scanData2Store(ScanStaticModel scanModel) {
    // Conversion Profile
    List<DisconfCenterBaseModel> disconfCenterFiles = getDisconfFiles(scanModel);
    DisconfStoreProcessorFactory.getDisconfStoreFileProcessor().transformScanData(disconfCenterFiles);
}

First, the scanModel is converted back to List < DisconfCenterBaseModel > (File and Item are converted differently, depending on the source code). The file configuration is DisconfCenterFile, which is inherited from DisconfCenterBaseModel. Then it relies on DisconfStoreFileProcessorImpl, a tool for processing warehouse configuration files, to process and submit the configuration files to the warehouse.

Take DisconfStore File Processor Impl for example.

The warehouse tool inherits the DisconfStoreProcessor interface, transformScanData() traverses the warehouse configuration elements, calls DisconfCenterStore.getInstance() to get the single DisconfCenterStore, and calls storeOneFile() to configure the storage.

/**
 * Store a configuration file
 */
public void storeOneFile(DisconfCenterBaseModel disconfCenterBaseModel) {
    DisconfCenterFile disconfCenterFile = (DisconfCenterFile) disconfCenterBaseModel;
    String fileName = disconfCenterFile.getFileName();
    if (confFileMap.containsKey(fileName)) {
        LOGGER.warn("There are two same fileName key!!!! " + fileName);
        DisconfCenterFile existCenterFile = confFileMap.get(fileName);
        // If both annotated and non-annotated modes are used at the same time, XML reload is also used when modifying.
        if (disconfCenterFile.isTaggedWithNonAnnotationFile()) {
            // This parameter is used to determine whether it is non-annotated (managed), set to true, and reload xml when configuration updates occur?
            existCenterFile.setIsTaggedWithNonAnnotationFile(true);
        }
    } else {
        confFileMap.put(fileName, disconfCenterFile);
    }
}

The implementation of the scanning tool StaticScanner ItemMgrImpl and File is similar, and there will also be a warehouse tool DisconfStore ItemProcessor Impl for storage.

For the non-annotated scanning tool StaticScanner NonAnnotation FileMgrImpl, there will be no elements to be processed by the processor during the current boot process. The use of this tool will be described later.

Continue with the first scan, so far all annotation configurations have been loaded into the warehouse, which configuration classes, configuration items and so on are included in the warehouse. Based on these elements, configuration values can be obtained from Disconf server, injected into Bean, and monitored for configuration updates and dynamic Reload.

// Getting Data/Injection/Watch
disconfCoreMgr = DisconfCoreFactory.getDisconfCoreMgr(registry);
disconfCoreMgr.process();

DisconfCoreFactory is a factory class of DisconfCoreMgr. DisconfCoreMgr includes Fetcher Mgr download module and WatchMgrImpl module. It mainly relies on Zookeeper and Restful generic tool classes in DisconfCore.

DisconfCoreMgrImpl is the core processor, and the constructor assigns values.

private List<DisconfCoreProcessor> disconfCoreProcessorList = new ArrayList<DisconfCoreProcessor>();
// Monitor
private WatchMgr watchMgr = null;
// Grabber
private FetcherMgr fetcherMgr = null;
// registry
private Registry registry = null;
public DisconfCoreMgrImpl(WatchMgr watchMgr, FetcherMgr fetcherMgr, Registry registry) {
    this.watchMgr = watchMgr;
    this.fetcherMgr = fetcherMgr;
    this.registry = registry;
    //Here add the processor of configuration item and configuration file.
    DisconfCoreProcessor disconfCoreProcessorFile = DisconfCoreProcessorFactory.getDisconfCoreProcessorFile(watchMgr, fetcherMgr, registry);
    disconfCoreProcessorList.add(disconfCoreProcessorFile);
    DisconfCoreProcessor disconfCoreProcessorItem = DisconfCoreProcessorFactory.getDisconfCoreProcessorItem(watchMgr, fetcherMgr, registry);
    disconfCoreProcessorList.add(disconfCoreProcessorItem);
}

The DisconfFile Core Processor List contains processors for DisconfFile Core Processor Impl and DisconfItemCore Processor Impl. This step is similar to the previous Scan processing for File and Item. The processor implements the DisconfCore Processor interface.

disconfCoreMgr.process(); traverses the two processors and calls processAllItems() for processing. See the specific tasks of the processor, DisconfFile Core Processor Impl for example.

@Override
public void processAllItems() {

    /**
     * Configuration file list processing
     * disconfStoreProcessor The implementation is DisconfStoreFileProcessorImpl
     */
    for (String fileName : disconfStoreProcessor.getConfKeySet()) {
        // Get all configuration file names
        processOneItem(fileName);
    }
}

@Override
public void processOneItem(String key) {
    // Get the elements in the warehouse
    DisconfCenterFile disconfCenterFile = (DisconfCenterFile) disconfStoreProcessor.getConfData(key);
    try {
        updateOneConfFile(key, disconfCenterFile);
    } catch (Exception e) {
        LOGGER.error(e.toString(), e);
    }
}

/**
 * Update a configuration file, download, inject into the warehouse, Watch three steps
 */
private void updateOneConfFile(String fileName, DisconfCenterFile disconfCenterFile) throws Exception {

    if (disconfCenterFile == null) {
        throw new Exception("cannot find disconfCenterFile " + fileName);
    }

    String filePath = fileName;
    Map<String, Object> dataMap = new HashMap<String, Object>();

    //
    // To open disconf, you need to download it remotely, otherwise you can download it locally.
    //
    if (DisClientConfig.getInstance().ENABLE_DISCONF) {

        //
        // Download configuration
        //
        try {
            // Get the configuration path first, which you can see when loading.
            String url = disconfCenterFile.getRemoteServerUrl();
            // This method mainly downloads documents through url. The implementation of this method will download and move the configuration files to specified paths.
            filePath = fetcherMgr.downloadFileFromServer(url, fileName, disconfCenterFile.getFileDir());

        } catch (Exception e) {

            //
            // Download failed, try using local configuration
            //
            LOGGER.error(e.toString(), e);
            LOGGER.warn("using local properties in class path: " + fileName);

            // change file path
            filePath = fileName;
        }
        LOGGER.debug("download ok.");
    }

    try {
        // FileTypeProcessor Utils configures the processor, reads the configuration values to Map according to the type and file path of the configuration items. There are *, xml, properties in total. At present, only the property type can be parsed and returned to Map.
        dataMap = FileTypeProcessorUtils.getKvMap(disconfCenterFile.getSupportFileTypeEnum(),
                disconfCenterFile.getFilePath());
    } catch (Exception e) {
        LOGGER.error("cannot get kv data for " + filePath, e);
    }

    //
    // Inject into warehouse
    //
    disconfStoreProcessor.inject2Store(fileName, new DisconfValue(null, dataMap));
    LOGGER.debug("inject ok.");

    //
    // Opening disconf requires watch
    //
    if (DisClientConfig.getInstance().ENABLE_DISCONF) {
        //
        // Watch
        //
        DisConfCommonModel disConfCommonModel = disconfStoreProcessor.getCommonModel(fileName);
        if (watchMgr != null) {
            watchMgr.watchPath(this, disConfCommonModel, fileName, DisConfigTypeEnum.FILE,
                    GsonUtils.toJson(disconfCenterFile.getKV()));
            LOGGER.debug("watch ok.");
        } else {
            LOGGER.warn("cannot monitor {} because watch mgr is null", fileName);
        }
    }
}

Look at the processing of inject2Store():

public void inject2Store(String fileName, DisconfValue disconfValue) {

    // Get the configuration center object
    DisconfCenterFile disconfCenterFile = getInstance().getConfFileMap().get(fileName);
    // Check whether it exists
    if (disconfCenterFile == null) {
        LOGGER.error("cannot find " + fileName + " in store....");
        return;
    }
    if (disconfValue == null || disconfValue.getFileData() == null) {
        LOGGER.error("value is null for {}", fileName);
        return;
    }
    // Store and store the values of dataMap on the attributes of an object
    Map<String, FileItemValue> keMap = disconfCenterFile.getKeyMaps();
    if (keMap.size() > 0) {
        for (String fileItem : keMap.keySet()) {

            Object object = disconfValue.getFileData().get(fileItem);
            if (object == null) {
                LOGGER.error("cannot find {} to be injected. file content is: {}", fileItem,
                        disconfValue.getFileData().toString());
                continue;
            }

            // Set values by type
            try {

                Object value = keMap.get(fileItem).getFieldValueByType(object);
                keMap.get(fileItem).setValue(value);

            } catch (Exception e) {
                LOGGER.error("inject2Store filename: " + fileName + " " + e.toString(), e);
            }
        }
    }
    // Used XML configuration
    if (disconfCenterFile.isTaggedWithNonAnnotationFile()) {
        if (disconfCenterFile.getSupportFileTypeEnum().equals(SupportFileTypeEnum.PROPERTIES)) {
            // If you configure it with XML, you need to reload data to bean s using spring reload
            ReloadConfigurationMonitor.reload();
        }
        disconfCenterFile.setAdditionalKeyMaps(disconfValue.getFileData());
    }
}

It is also clear that the state of the stored objects in the warehouse will be judged, and then the corresponding object values will be stored. For the processing of XML configuration, I will not introduce it for the time being, but I will introduce it later.

After the configuration processing in the warehouse is completed, if you need to monitor the update of the configuration file, you need to monitor it through Watch. The object of monitoring is DisConfCommonModel, which is composed of configuration app name, version number, env environment and other information. The disConfCommonModel was stored in the warehouse object when it converted the configuration file in scanData2Store().

Let's look at the last line of code:

watchMgr.watchPath(this, disConfCommonModel, fileName, DisConfigTypeEnum.FILE,
                    GsonUtils.toJson(disconfCenterFile.getKV()));

We mainly look at the first two parameters, the second is the monitoring object just mentioned, the first parameter is this itself.

public void watchPath(DisconfCoreProcessor disconfCoreMgr, DisConfCommonModel disConfCommonModel, String keyName,
                          DisConfigTypeEnum disConfigTypeEnum, String value) throws Exception {
        // Newly build
        String monitorPath = makeMonitorPath(disConfigTypeEnum, disConfCommonModel, keyName, value);
        // Monitoring
        NodeWatcher nodeWatcher =
                new NodeWatcher(disconfCoreMgr, monitorPath, keyName, disConfigTypeEnum, new DisconfSysUpdateCallback(),
                        debug);
        nodeWatcher.monitorMaster();
    }

Because distributed consistency is achieved through zookeeper, the node model needs to convert disConfCommonModel objects and other information into directory path monitorPath.

NodeWatcher is the implementation of the zookeeper api interface Watcher. You can see that the disconfCoreMgr will be set to the NodeWatcher property. When Watcher monitors messages in configuration updates, the execution code is to call the reload() method of new DisconfSysUpdateCallback(), and the reload() method will call the updateConfOnCallback () method of disconfCoreMgr. The specific callback operation is introduced later.

The above is the implementation logic of DisconfFile Core Processor Impl, and the logic of DisconfFItem Core Processor Impl is similar.

Back to the first static scan entry, and finally the bean injection.

registerAspect(registry);

Manually inject the DisconfAspectJ.class object. Through AOP interception, it is used to obtain configuration files and configuration items.

At the end of the first static scan, the analysis process has some simple code implementation and tracking, without detailed explanation, you can view the source code by yourself.

Please indicate the source for reprinting.
Author: wuxiwei
Origin: https://www.cnblogs.com/wxw16/p/10701673.html

Posted by gozbay.com on Sun, 05 May 2019 08:16:39 -0700