Dubbo 3.0: основной процесс инициализации BootStrap

Java задняя часть
Dubbo 3.0: основной процесс инициализации BootStrap

Прежде всего, поделитесь всеми предыдущими статьями, ставьте лайки, добавляйте в избранное и пересылайте три раза подряд. >>>>😜😜😜
Сборник статей:🎁nuggets.capable/post/694164…
Github :👉github.com/black-ant
Резервное копирование CASE:👉git ee.com/ant black/wipe…

Введение

В этой статье представлен только предварительный обзор основного процесса инициализации Bootstrap, а некоторые детали будут проанализированы отдельно.

Dubbo 3.0 использует DubboBootstrap для логики инициализации.Логика запуска DubboBootstrap следующая:

// 核心原理为 ApplicationContextEvent 事件的触发 , 当 SpringApplication 启动后 ,会发布该事件

C- AbstractApplicationContext # refresh : 发布 ApplicationContextEvent 事件
C- DubboBootstrapApplicationListener # onApplicationContextEvent : 监听Application 事件
C- DubboBootstrapApplicationListener # onContextRefreshedEvent
C- DubboBootstrap # start : 开始加载操作 
    

В основном участвует

// 参考类 : 
DubboBeanUtils
ServiceAnnotationPostProcessor
DubboClassPathBeanDefinitionScanner
    
    
// 监听器 : 
DubboBootstrapApplicationListener    

2. Подробное объяснение входа

Инициализация Dubbo в основном проходит через следующие классы: DubboBootstrap —

// Step 1 : DubboBootstrapApplicationListener
public void onApplicationContextEvent(ApplicationContextEvent event) {
	if (DubboBootstrapStartStopListenerSpringAdapter.applicationContext == null) {
		DubboBootstrapStartStopListenerSpringAdapter.applicationContext = event.getApplicationContext();
	}
        // Step 2-1 : 容器启动后初始化 Bootstrap , 看名称这里应该还可以刷新
	if (event instanceof ContextRefreshedEvent) {
		onContextRefreshedEvent((ContextRefreshedEvent) event);
        // 容器关闭逻辑  
	} else if (event instanceof ContextClosedEvent) {
		onContextClosedEvent((ContextClosedEvent) event);
	}
}

// Step 2-1 
private void onContextRefreshedEvent(ContextRefreshedEvent event) {
	if (dubboBootstrap.getTakeoverMode() == BootstrapTakeoverMode.SPRING) {
		dubboBootstrap.start();
	}
}

// Step 2-2 
private void onContextClosedEvent(ContextClosedEvent event) {
	if (dubboBootstrap.getTakeoverMode() == BootstrapTakeoverMode.SPRING) {
		DubboShutdownHook.getDubboShutdownHook().run();
	}
}

3. Процесс запуска DubboBootstrap

3.1 Запустить основной процесс

Стартовая ссылка в основном состоит из следующих ссылок:

Step 1 : 设置启动参数
Step 2 : initialize 初始化
Step 3 : exportServices 输出 Services
Step 4 : referServices 注册 refer Service
Step 5 : onStart() 启动 (异步情况下会创建 Thread 后启动)
    
// 启动 Dubbo 容器环境
dubboBootstrap.start(); 

// Step 1 :Start 逻辑
public DubboBootstrap start() {
    if (started.compareAndSet(false, true)) {
        startup.set(false);
        initialized.set(false);
        shutdown.set(false);
        awaited.set(false);

        initialize();
        // 1. export Dubbo Services
        exportServices();

        // Not only provider register
        if (!isOnlyRegisterProvider() || hasExportedServices()) {
            // 2. export MetadataService
            exportMetadataService();
            // 3. Register the local ServiceInstance if required
            registerServiceInstance();
        }

        referServices();
        if (asyncExportingFutures.size() > 0 || asyncReferringFutures.size() > 0) {
            new Thread(() -> {
                try {
                    this.awaitFinish();
                } catch (Exception e) {
                    logger.warn(NAME + " asynchronous export / refer occurred an exception.");
                }
                startup.set(true);
                onStart();
            }).start();
        } else {
            startup.set(true);
            onStart();
        }

    }
    return this;
}

3.2 Установка параметров запуска

if (started.compareAndSet(false, true)) 
    
startup.set(false);
initialized.set(false);
shutdown.set(false);
awaited.set(false);
    
// 此处有几个主要的操作 : 
1. 原子设置已经启动
2. 设置属性
    
// 主要看一下这4个属性
private AtomicBoolean initialized = new AtomicBoolean(false);
private AtomicBoolean started = new AtomicBoolean(false);
private AtomicBoolean startup = new AtomicBoolean(true);
private AtomicBoolean destroyed = new AtomicBoolean(false);
private AtomicBoolean shutdown = new AtomicBoolean(false) 
    

3.3 инициализировать содержимое инициализации

public void initialize() {
    if (!initialized.compareAndSet(false, true)) {
        return;
    }

    ApplicationModel.initFrameworkExts();

    // 初始化配置
    startConfigCenter();
    loadConfigsFromProps();
    checkGlobalConfigs();

    // 初始化 Service
    startMetadataCenter();
    initMetadataService();
}

3.3.1 ApplicationModel.initFrameworkExts()

// C- DubboBootstrap
public static void initFrameworkExts() {
        Set<FrameworkExt> exts = ExtensionLoader.getExtensionLoader(FrameworkExt.class).getSupportedExtensionInstances();
    	// PIC21005
        for (FrameworkExt ext : exts) {
            ext.initialize();
        }
}


// Step 1 : 通过 SPI 进行插件处理
@SPI
public interface FrameworkExt extends Lifecycle {

}

// 补充 : Dubbo 中的  Lifecycle 对象
- ConfigManager
- Environment
- LifecycleAdapter
- ServiceRepository
- SpringExtensionFactory


// Step 2-1  : ConfigManager 部分
public void initialize() throws IllegalStateException {
        String configModeStr = null;
        try {
            configModeStr = (String) ApplicationModel.getEnvironment().getConfiguration().getProperty(DUBBO_CONFIG_MODE);
            if (StringUtils.hasText(configModeStr)) {
                this.configMode = ConfigMode.valueOf(configModeStr.toUpperCase());
            }
        } catch (Exception e) {
            throw new IllegalArgumentException(msg, e);
        }
}    

// Step 2-2  : Environment 部分
public void initialize() throws IllegalStateException {
        if (initialized.compareAndSet(false, true)) {
            this.propertiesConfiguration = new PropertiesConfiguration();
            this.systemConfiguration = new SystemConfiguration();
            this.environmentConfiguration = new EnvironmentConfiguration();
            this.externalConfiguration = new InmemoryConfiguration("ExternalConfig");
            this.appExternalConfiguration = new InmemoryConfiguration("AppExternalConfig");
            this.appConfiguration = new InmemoryConfiguration("AppConfig");

            loadMigrationRule();
        }
}


// Step 2-3 : ServiceRepository 部分

PIC21005: Список объектов FrameworkExt

Dubbo-Framaket-module.png

3.3.2 Краткое описание startConfigCenter

Этот этап можно разделить на 3 шага:

  1. Были загружены ApplicationConfig и ConfigCenterConfig
  2. Настроить конфигцентрконфиг
  3. Окончательное обновление приложения, монитора, модулей и других компонентов.
private void startConfigCenter() {

    // Step 1 : 处理对应的 Config , 此处简单一点说分为2个类型 , 很好的思路 , 后面深入了解一下
    
    // > 类型一 : 存在class 对应配置
    // 1. 通过 class 类型去 newInstance 实例化对应类型的 Class 对象
    // 2. 调用对应的 refush 进行刷新处理 (TODO : 后续分析 , 就是配置的重写流程)
    // 3. 缓存到 configManager 中
    
    // > 类型二 : 不存在对应的配置
    // 1. 直接从 ApplicationModel 中获取 Environment
    // 2. newInstance 实例化后放在 configManager 中
    loadConfigs(ApplicationConfig.class);
    loadConfigs(ConfigCenterConfig.class);
 
 
    useRegistryAsConfigCenterIfNecessary();

    // check Config Center -> PS:332001
    Collection<ConfigCenterConfig> configCenters = configManager.getConfigCenters();
    if (CollectionUtils.isEmpty(configCenters)) {
        // 通常配置了注册中心这里都会有值 , 如果为空 , 此处 new 了一个基础实现
    } else {
        for (ConfigCenterConfig configCenterConfig : configCenters) {
            // Step 2 : 对 configCenterConfig 进行配置 , 后续深入
            configCenterConfig.refresh();
            ConfigValidationUtils.validateConfigCenterConfig(configCenterConfig);
        }
    }

    if (CollectionUtils.isNotEmpty(configCenters)) {
        CompositeDynamicConfiguration compositeDynamicConfiguration = new CompositeDynamicConfiguration();
        
        // 这里要对 environment 环节进行配置
        for (ConfigCenterConfig configCenter : configCenters) {
            // Pass config from ConfigCenterBean to environment
            environment.updateExternalConfigMap(configCenter.getExternalConfiguration());
            environment.updateAppExternalConfigMap(configCenter.getAppExternalConfiguration());

            // Fetch config from remote config center
            compositeDynamicConfiguration.addConfiguration(prepareEnvironment(configCenter));
        }
        environment.setDynamicConfiguration(compositeDynamicConfiguration);
    }
    
    // Step 3 : 此处对 Application , Monitor , Modules 等多个组件进行最后的刷新 TODO
    configManager.refreshAll();
}

Дополнение: Роль объекта ConfigCenterConfig

Этот объект содержит базовую информацию о конфигурации соединения, такую ​​как протокол, адрес, порт, кластер, пространство имен и т. д.

Гибкое использование объекта ConfigCenterConfig может интегрировать различные реестры.

Дополнение: PS:332001 Структура

image.png

3.3.3 Краткое описание loadConfigsFromProps

Это аналогично предыдущему, loadConfigs — это чтение конфигурации конфигурационного файла через специальный префикс

Я видел несколько проектов с открытым исходным кодом, загружающих идеи для файлов конфигурации раньше,В принципе так же, все они различаются по классу и загружаются по префиксу, которые будут проанализированы отдельно позже

private void loadConfigsFromProps() {

    // application config has load before starting config center
    // load dubbo.applications.xxx
    loadConfigs(ApplicationConfig.class);

    // load dubbo.modules.xxx
    loadConfigs(ModuleConfig.class);

    // load dubbo.monitors.xxx
    loadConfigs(MonitorConfig.class);

    // load dubbo.metricses.xxx
    loadConfigs(MetricsConfig.class);

    // load multiple config types:
    // load dubbo.protocols.xxx
    loadConfigs(ProtocolConfig.class);

    // load dubbo.registries.xxx
    loadConfigs(RegistryConfig.class);

    // load dubbo.providers.xxx
    loadConfigs(ProviderConfig.class);

    // load dubbo.consumers.xxx
    loadConfigs(ConsumerConfig.class);

    // load dubbo.metadata-report.xxx
    loadConfigs(MetadataReportConfig.class);

    // config centers has bean loaded before starting config center
    //loadConfigs(ConfigCenterConfig.class);

}

3.3.4 Краткое описание checkGlobalConfigs

В этой ссылке проверка конфигурации и обработка портов будут выполняться с помощью ConfigValidationUtils и, наконец, обновляться с помощью обновления.

private void checkGlobalConfigs() {
    // check config types (ignore metadata-center)
    List<Class<? extends AbstractConfig>> multipleConfigTypes = Arrays.asList(
        ApplicationConfig.class,
        ProtocolConfig.class,
        RegistryConfig.class,
        MetadataReportConfig.class,
        ProviderConfig.class,
        ConsumerConfig.class,
        MonitorConfig.class,
        ModuleConfig.class,
        MetricsConfig.class,
        SslConfig.class);

    for (Class<? extends AbstractConfig> configType : multipleConfigTypes) {
        // 通过 ConfigValidationUtils 对配置进行校验
        checkDefaultAndValidateConfigs(configType);
    }

    // check port conflicts
    // 
    Map<Integer, ProtocolConfig> protocolPortMap = new LinkedHashMap<>();
    for (ProtocolConfig protocol : configManager.getProtocols()) {
        Integer port = protocol.getPort();
        if (port == null || port == -1) {
            continue;
        }
        
        // 此处如果存在端口冲突 , 这里会直接抛出异常
        ProtocolConfig prevProtocol = protocolPortMap.get(port);
        if (prevProtocol != null) {
            throw new IllegalStateException
        }
        protocolPortMap.put(port, protocol);
    }

    // check reference and service
    // 这里正式开始处理 reference 和 DubboService , TODO : 后面单章分析
    for (ReferenceConfigBase<?> reference : configManager.getReferences()) {
        reference.refresh();
    }
    for (ServiceConfigBase service : configManager.getServices()) {
        service.refresh();
    }
}

3.3.5 Краткое описание startMetadataCenter

Вот обработка метаданных, метаданные - это новая функция, вы можете использовать префикс "dubbo.metadata-report" в конфигурационном файле для установки свойств MetadataReportConfig

Что такое метаданные:

  • Метаданные предназначены для снижения нагрузки на реестр, и часть содержимого, хранящегося в реестре, помещается в центр метаданных.
  • Данные в центре метаданных предназначены только для вашего собственного использования, и нет необходимости обновлять изменения.
  • Нет необходимости в мониторинге и уведомлении, что значительно снижает потребление производительности
private void startMetadataCenter() {

    useRegistryAsMetadataCenterIfNecessary();

    ApplicationConfig applicationConfig = getApplication();

    String metadataType = applicationConfig.getMetadataType();
    // FIXME, multiple metadata config support.
    Collection<MetadataReportConfig> metadataReportConfigs = configManager.getMetadataConfigs();
    if (CollectionUtils.isEmpty(metadataReportConfigs)) {
        //... 这里配置了远程注册中心时默认都会有数据 , 没有会抛出异常
    }

    for (MetadataReportConfig metadataReportConfig : metadataReportConfigs) {
        // <dubbo:metadata-report address="zookeeper://127.0.0.1:2181" id="metadata-center-zookeeper-2181" />
        ConfigValidationUtils.validateMetadataConfig(metadataReportConfig);
        if (!metadataReportConfig.isValid()) {
            continue;
        }
        
        // 通过 instance 进行实例化的处理
        MetadataReportInstance.init(metadataReportConfig);
    }
}

3.3.6 Краткое описание службы initMetadataService

    private void initMetadataService() {
//        startMetadataCenter();
        this.metadataService = getDefaultExtension();
        this.metadataServiceExporter = new ConfigurableMetadataServiceExporter(metadataService);
    }

3.4 exportServices

Подробности смотрите в системе регистрации сервиса

3.5 referServices

Дополнительные сведения см. в разделе Система обнаружения служб.

3.6 onStart вызывает расширение слушателя

private void onStart() {
	ExtensionLoader<DubboBootstrapStartStopListener> exts = getExtensionLoader(DubboBootstrapStartStopListener.class);
	exts.getSupportedExtensionInstances().forEach(ext -> ext.onStart(this));
}

Суммировать

Эта статья предназначена в основном для подведения итогов и предварительного понимания всех концепций.Позже мы будем постепенно улучшать детали всего процесса, а также изучение и использование его кода.

image.png