Dubbo笔记 ㉖ : DubboBootstrap 的服务暴露

文章目录

  • 一、前言
  • 二、DubboBootstrap
    • 1. 构造函数
  • 三、DubboBootstrap#start
    • 1. 服务初始化
      • 1.1 ApplicationModel.iniFrameworkExts()
      • 1.2 startConfigCenter()
      • 1.3 useRegistryAsConfigCenterIfNecessary()
      • 1.4 startMetadataReport();
      • 1.5. loadRemoteConfigs()
      • 1.6 checkGlobalConfigs()
      • 1.7 initMetadataService()
      • 1.8 initMetadataServiceExporter()
      • 1.9 initEventListener()
    • 2. 服务导出
    • 3. 服务自省
    • 4. 服务引用
  • 四、DubboBootstrap#stop
  • 五、2.7.5 流程简介
    • 1. 提供者
      • 1.1 ServiceConfig#export
      • 1.2 ServiceConfig#doExport
      • 1.3 ServiceConfig#doExportUrls
    • 2. 消费者:
      • 2.1 ReferenceConfig#get
      • 2.2 ReferenceConfig#init

一、前言

本系列为个人Dubbo学习笔记,内容基于《深度剖析Apache Dubbo 核心技术内幕》, 过程参考官方源码分析文章,仅用于个人笔记记录。本文分析基于Dubbo2.7.5版本,由于个人理解的局限性,若文中不免出现错误,感谢指正。

系列文章地址:Dubbo源码分析:全集整理


本文基于 Dubbo 2.7.5 版本。关于该部分逻辑,如有需要可参考:

  1. Dubbo笔记 ㉕ : Spring 执行流程概述
  2. Dubbo笔记 ㉖ : DubboBootstrap 的服务暴露
  3. Dubbo笔记 ㉗ : 服务自省-提供者
  4. Dubbo笔记 ㉘ : 服务自省-消费者

在 Dubbo笔记㉕ : Spring 执行流程概述 一文中,我们介绍了 在 Spring 中 Dubbo 的执行流程,其中我们知道了 提供者在启动时的服务发布是在 DubboBootstrap 中完成的。本文我们来看一下 DubboBootstrap 的执行流程。

二、DubboBootstrap

DubboBootstrap 是 Dubbo 服务启动的核心类,在其中完成了Dubbo服务暴露的过程。

1. 构造函数

DubboBootstrap 是单例的,其构造函数如下:

    private DubboBootstrap() {
    	// 获取 配置管理
        configManager = ApplicationModel.getConfigManager();
        // 获取 环境
        environment = ApplicationModel.getEnvironment();

        DubboShutdownHook.getDubboShutdownHook().register();
        // 设置回调,当服务关闭时触发该回调,调用 DubboBootstrap#destroy 来销毁服务。
        ShutdownHookCallbacks.INSTANCE.addCallback(new ShutdownHookCallback() {
            @Override
            public void callback() throws Throwable {
                DubboBootstrap.this.destroy();
            }
        });
    }

三、DubboBootstrap#start

DubboBootstrap#start 的实现如下:

    public DubboBootstrap start() {
    	// cas 保证只启动一次
        if (started.compareAndSet(false, true)) {
        	// 1. 服务配置初始化
            initialize();
            // 2. Dubbo服务导出
            exportServices();

            // Not only provider register
            // 3. 元数据中心服务暴露,当使用服务自省模式时才会执行该部分逻辑
            // 3.1 不仅仅提供者注册 || 存在导出的服务
            if (!isOnlyRegisterProvider() || hasExportedServices()) {
                // 3.2 导出元数据服务
                exportMetadataService();
                // 3.3 如果需要注册本地服务实例
                registerServiceInstance();
            }
			// 4. 服务引用流程
            referServices();
        }
        return this;
    }

下面我们按照注释顺序来看:

1. 服务初始化

在 DubboBootstrap#initialize 中完成了 Dubbo的配置检查和初始化,其具体实现如下:

    private void initialize() {
    	//  CAS 防止多次调用
        if (!initialized.compareAndSet(false, true)) {
            return;
        }
		// 1. 初始化 FrameworkExt 扩展类。 FrameworkExt 是 SPI接口,这里获取所有的 实现类并且调用FrameworkExt#initialized 来初始化
        ApplicationModel.iniFrameworkExts();
		// 2. 启用配置中心并刷新本地配置
        startConfigCenter();
		// 3. 在默认是zk作为注册中心时,如果没有配置配置中心,则使用注册中心作为配置中心
        useRegistryAsConfigCenterIfNecessary();
		// 4. 启动元数据中心配置
        startMetadataReport();
		// 5. 加载远程配置
        loadRemoteConfigs();
		// 6. 检查本地配置是否合法
        checkGlobalConfigs();
		// 7. 初始化元数据中心 Service
        initMetadataService();
		// 8. 初始化元数据中心导出类
        initMetadataServiceExporter();
		// 9. 初始化监听器
        initEventListener();
    }

在 Spring 中 Dubbo 的初始化工作和 Main 方法启动基本也是类似的,只不过在执行过程中稍微有些区别。下面我们按照注释来具体说明 :

1.1 ApplicationModel.iniFrameworkExts()

ApplicationModel.iniFrameworkExts() 是执行框架扩展实现类的初始化方法,其实现如下:

    public static void iniFrameworkExts() {
    	// 获取支持的框架扩展FrameworkExt 实现 
        Set<FrameworkExt> exts = ExtensionLoader.getExtensionLoader(FrameworkExt.class).getSupportedExtensionInstances();
        for (FrameworkExt ext : exts) {
        	// 执行初始化方法
            ext.initialize();
        }
    }

其中 FrameworkExt 是 Dubbo 提供的 框架扩展 SPI 接口,继承了 Lifecycle 接口,如下:

@SPI
public interface FrameworkExt extends Lifecycle {
}

...

// Lifecycle  接口实现如下
public interface Lifecycle {

    void initialize() throws IllegalStateException;

    void start() throws IllegalStateException;

    void destroy() throws IllegalStateException;
}

Lifecycle 是 Dubbo 组件的声明周期接口,SPI 扩展实现类时在对应的生命周期会调用对应的方法。

  • Lifecycle#initialize :当一个 SPI 实现类实现 Lifecycle 接口时,在其创建时会调用该方法。
    Dubbo笔记 ㉖ : DubboBootstrap 的服务暴露_第1张图片

  • Lifecycle#start :当一个 SPI 实现类实现 Lifecycle 接口时,在容器刷新结束后会调用该方法。
    在这里插入图片描述

  • Lifecycle#destroy :当一个 SPI 实现类实现 Lifecycle 接口时,在容器销毁时会调用该方法。
    Dubbo笔记 ㉖ : DubboBootstrap 的服务暴露_第2张图片


Dubbo 提供的FrameworkExt 有三个实现 : ConfigManager、Environment、ServiceRepository。我们这里先来看 initialize 方法。只有 Environment#initialize 有具体操作,如下:

	// 加载了配置中心的配置并保存到了  org.apache.dubbo.common.config.Environment 的 map 中。
    @Override
    public void initialize() throws IllegalStateException {
    	// 获取 配置管理
        ConfigManager configManager = ApplicationModel.getConfigManager();
        // 获取默认的配置中心
        Optional<Collection<ConfigCenterConfig>> defaultConfigs = configManager.getDefaultConfigCenter();
        // 如果存在配置中心,则从配置中心获取配置,保存到本地。
        defaultConfigs.ifPresent(configs -> {
            for (ConfigCenterConfig config : configs) {
                this.setExternalConfigMap(config.getExternalConfiguration());
                this.setAppExternalConfigMap(config.getAppExternalConfiguration());
            }
        });
    }

1.2 startConfigCenter()

如果存在配置中心,则会在这里加载了配置中心的配置与本地配置合并,并保存到 environment 中。

    private void startConfigCenter() {
        Collection<ConfigCenterConfig> configCenters = configManager.getConfigCenters();

        if (CollectionUtils.isNotEmpty(configCenters)) {
        	// 复合配置,保存了配置优先级的特性
            CompositeDynamicConfiguration compositeDynamicConfiguration = new CompositeDynamicConfiguration();
            for (ConfigCenterConfig configCenter : configCenters) {
            	// 刷新配置中心配置
                configCenter.refresh();
                ConfigValidationUtils.validateConfigCenterConfig(configCenter);
                // 添加到 复合配置中
                compositeDynamicConfiguration.addConfiguration(prepareEnvironment(configCenter));
            }
            environment.setDynamicConfiguration(compositeDynamicConfiguration);
        }
        // 刷新本地配置
        configManager.refreshAll();
    }

关于配置中心的内容,如有需要详参:Dubbo笔记 ㉒ :配置中心

1.3 useRegistryAsConfigCenterIfNecessary()

这一步的注释很清楚,就不再贴出代码:出于兼容性考虑,当注册协议是zookeeper并且没有明确指定配置中心时,使用注册中心作为默认配置中心。

1.4 startMetadataReport();

初始化元数据中心实例。服务如果配置了元数据中心, 则在此处进行配置初始化

    private void startMetadataReport() {
        ApplicationConfig applicationConfig = getApplication();
		// 获取元数据中心类型,local 或 remote
        String metadataType = applicationConfig.getMetadataType();
        // FIXME, multiple metadata config support.
        Collection<MetadataReportConfig> metadataReportConfigs = configManager.getMetadataConfigs();
        if (CollectionUtils.isEmpty(metadataReportConfigs)) {
        	// 如果配置是 元数据中心是远程 (dubbo.application.metadata-type = remote) && 没配置元数据中心 则抛出异常
            if (REMOTE_METADATA_STORAGE_TYPE.equals(metadataType)) {
                throw new IllegalStateException("No MetadataConfig found, you must specify the remote Metadata Center address when 'metadata=remote' is enabled.");
            }
            // 如果没有配置元数据中心则直接返回
            return;
        }
        // 获取元数据中心配置
        MetadataReportConfig metadataReportConfig = metadataReportConfigs.iterator().next();
        // 校验配置
        ConfigValidationUtils.validateMetadataConfig(metadataReportConfig);
        if (!metadataReportConfig.isValid()) {
            return;
        }
		// MetadataReportInstance 会根据元数据中心的配置,创建一个 MetadataReport 实例。
		// MetadataReportInstance#MetadataReport 是静态的属性,所以对于MetadataReportInstance 来说 只会存在一个 MetadataReport 
        MetadataReportInstance.init(metadataReportConfig.toUrl());
    }

1.5. loadRemoteConfigs()

这一步 对 RegistryConfig 和 ProtocolConfig 的 id 进行了处理。

    private void loadRemoteConfigs() {
        // registry ids to registry configs
        List<RegistryConfig> tmpRegistries = new ArrayList<>();
        // 获取 registry id 
        Set<String> registryIds = configManager.getRegistryIds();
        registryIds.forEach(id -> {
            if (tmpRegistries.stream().noneMatch(reg -> reg.getId().equals(id))) {
            	// 通过 registry id  获取到对应的 RegistryConfig 并刷新配置
                tmpRegistries.add(configManager.getRegistry(id).orElseGet(() -> {
                	// 设置注册中心配置的id
                    RegistryConfig registryConfig = new RegistryConfig();
                    registryConfig.setId(id);
                    registryConfig.refresh();
                    return registryConfig;
                }));
            }
        });
		// 保存经过上述处理的注册中心
        configManager.addRegistries(tmpRegistries);
		// protocol ids 同上
        // protocol ids to protocol configs
        List<ProtocolConfig> tmpProtocols = new ArrayList<>();
        Set<String> protocolIds = configManager.getProtocolIds();
        protocolIds.forEach(id -> {
            if (tmpProtocols.stream().noneMatch(prot -> prot.getId().equals(id))) {
                tmpProtocols.add(configManager.getProtocol(id).orElseGet(() -> {
                    ProtocolConfig protocolConfig = new ProtocolConfig();
                    protocolConfig.setId(id);
                    protocolConfig.refresh();
                    return protocolConfig;
                }));
            }
        });

        configManager.addProtocols(tmpProtocols);
    }

1.6 checkGlobalConfigs()

这一步是检查各个组件的配置是否正确,逻辑比较常规,篇幅所限不再贴出代码。

1.7 initMetadataService()

这里会初始化元数据服务 MetadataService, 当使用服务自省时会注册该服务。

    private void initMetadataService() {
        this.metadataService = getExtension(getMetadataType());
    }

这里会根据 dubbo.application.metadata-type 参数的不同,MetadataService 会选择不同的实现。

MetadataService 的子接口 WritableMetadataService 具有三个实现类 :

  • InMemoryWritableMetadataService : 实现在导出时将 Dubbo 服务的元数据存储在本地内存中。当 metadata-type = local 时加载该类作为元数据服务,服务的元数据信息会保存在本地。
  • RemoteWritableMetadataService : 实现在导出时将 Dubbo 服务的元数据存储在元数据中心。会通过 MetadataReport 将服务元数据写到元数据中心
  • RemoteWritableMetadataServiceDelegate :在 RemoteWritableMetadataServiceDelegate 中,针对于元数据的操作会同时执行 RemoteWritableMetadataService 和 InMemoryWritableMetadataService 对应方法。当 metadata-type = remote 时加载该类作为元数据服务

1.8 initMetadataServiceExporter()

初始化元数据中心服务导出器,MetadataServiceExporter 用于导出 MetadataService,其实现如下:

    private void initMetadataServiceExporter() {
        this.metadataServiceExporter = new ConfigurableMetadataServiceExporter(metadataService);
    }

1.9 initEventListener()

初始化事件监听方法,

	// Dubbo 提供了 DirectEventDispatcher 和 ParallelEventDispatcher两种实现,默认是 DirectEventDispatcher 
    private final EventDispatcher eventDispatcher = EventDispatcher.getDefaultExtension();

    private void initEventListener() {
        // Add current instance into listeners
        addEventListener(this);
    }
    
    public DubboBootstrap addEventListener(EventListener<?> listener) {
        eventDispatcher.addEventListener(listener);
        return this;
    }

EventDispatcher 是 SPI 接口,Dubbo提供了两个实现 :

  • DirectEventDispatcher :使用当前调用线程来进行实现分发,该类是默认事件分发策略。
  • ParallelEventDispatcher : 使用 ForkJoinPool.commonPool(); 线程池来进行事件分发。

DubboBootstrap 是 GenericEventListener 子类,实现了泛化监听的功能。

2. 服务导出

DubboBootstrap#exportServices 开始实际导出服务,其实现如下:

    private void exportServices() {
    	// configManager.getServices() 获取所有需要发布的Dubbo Service
        configManager.getServices().forEach(sc -> {
            // TODO, compatible with ServiceConfig.export()
            ServiceConfig serviceConfig = (ServiceConfig) sc;
            serviceConfig.setBootstrap(this);
			// 如果是异步发布,则交由线程池来发布,并将 Future 保存到 asyncExportingFutures 中
            if (exportAsync) {
                ExecutorService executor = executorRepository.getServiceExporterExecutor();
                Future<?> future = executor.submit(() -> {
                    sc.export();
                });
                asyncExportingFutures.add(future);
            } else {
            	// 服务发出,导出的服务保存到exportedServices 中
                sc.export();
                exportedServices.add(sc);
            }
        });
    }

这里需要注意 configManager.getServices() 获取到的是所有要发布的 dubbo 服务的 ServiceBean。

在 Dubbo笔记㉕ : Spring 执行流程概述 一文中我们说到,在 Spring 中需要暴露的服务会有一个对应的 ServiceBean 注入到容器中,而 ServiceBean 在创建时会调用父类方法AbstractConfig#addIntoConfigManager 将 当前 Bean 添加到 ConfigManager 中。而在这里,则是通过 configManager.getServices() 将所有 的 ServiceBean 拿出来暴露服务。


上面可以看到,服务实际导出过程交由了 ServiceConfig#export 来完成。而 ServiceConfig#export 的导出过程,我们在之前的文章中已经有过分析过 dubbo 2.7.0 版本,:Dubbo笔记 ③ : 服务发布流程 - ServiceConfig#export。

3. 服务自省

这部分的逻辑只有在 dubbo.application.register-consumer = true 或 启用服务自省时才会执行。关于该部分的详细逻辑,我们在服务自省的文章中详细分析。

      	// 3. 元数据中心服务暴露
       // 3.1 dubbo.application.register-consumer = true || 通过 MetadataService 暴露了服务
       if (!isOnlyRegisterProvider() || hasExportedServices()) {
           // 3.2 暴露 MateService 
           exportMetadataService();
           // 3.3 注册 应用 到注册中心
           registerServiceInstance();
       }

关于服务自省,详参:
1 . Dubbo笔记 ㉗ : 服务自省-提供者
2. Dubbo笔记 ㉘ : 服务自省-消费者

4. 服务引用

如果当前这个服务也引用了其他服务,则在这里完成其他服务的引用。


对于一个Dubbo服务,我们可以通过注解的形式来引用

  1. 通过 @Reference 注解引用

        @Reference(group = "spring", version = "2.0.0")
        private ProviderService providerService;
    
  2. 通过 XML 的形式引用

    <dubbo:reference id="ProviderService" interface="com.kingfish.service.ProviderService" group="spring" version="2.0.0" />
    
  3. 通过 @Bean 注解方式引用

        @Bean
        public ReferenceBean<ProviderService> referenceConfig(){
            ReferenceBean<ProviderService> referenceConfig = new ReferenceBean<>();
            referenceConfig.setInterface(ProviderService.class);
            referenceConfig.setVersion("2.0.0");
            referenceConfig.setGroup("spring");
            return referenceConfig;
        }
    

对于第一种情况则直接通过 ReferenceAnnotationBeanPostProcessor 来完成引用,而第三种情况则是通过这里的代码完成。(第二种 XML 形式我没看逻辑

当 ReferenceBean 在 Spring容器中创建时会调用 AbstractConfig#addIntoConfigManager 将自身添加到 ConfigManager 的配置中。这里通过 configManager.getReferences() 获取到该 ReferenceBean 进行服务引用。具体代码如下:

    private void referServices() {
    	// 获取 引用缓存
        if (cache == null) {
            cache = ReferenceConfigCache.getCache();
        }
		// 遍历当前应用引用的 dubbo 服务
        configManager.getReferences().forEach(rc -> {
            // TODO, compatible with  ReferenceConfig.refer()
            ReferenceConfig referenceConfig = (ReferenceConfig) rc;
            referenceConfig.setBootstrap(this);
			// 是否延迟初始化,默认 true,不需要延迟
            if (rc.shouldInit()) {
            	// 异步 或 异步加载引用实例。
                if (referAsync) {
                    CompletableFuture<Object> future = ScheduledCompletableFuture.submit(
                            executorRepository.getServiceExporterExecutor(),
                            () -> cache.get(rc)
                    );
                    asyncReferringFutures.add(future);
                } else {
                	// 获取 Dubbo引用的 代理类,这里调用的是 ReferenceConfigCache#get(ReferenceConfigBase)
                    cache.get(rc);
                }
            }
        });
    }

其中 ReferenceConfigCache#get(ReferenceConfigBase) 的实现如下:

    public <T> T get(ReferenceConfigBase<T> referenceConfig) {
    	// 获取引用的服务 key
        String key = generator.generateKey(referenceConfig);
        Class<?> type = referenceConfig.getInterfaceClass();

        proxies.computeIfAbsent(type, _t -> new ConcurrentHashMap());

        ConcurrentMap<String, Object> proxiesOfType = proxies.get(type);
        proxiesOfType.computeIfAbsent(key, _k -> {
        	// 获取引用服务的代理类
            Object proxy = referenceConfig.get();
            // 缓存引用实例
            referredReferences.put(key, referenceConfig);
            return proxy;
        });
		// 返回引用实例
        return (T) proxiesOfType.get(key);
    }

我们这里看到最终还是通过 Object proxy = referenceConfig.get(); 来获取的 Dubbo引用的服务代理类,关于 ReferenceConfig#get 的内容,我们在之前分析 2.7.0 的文章中已经进行过详细解析,如有需要。详参 :Dubbo笔记 ⑧ : 消费者启动流程 - ReferenceConfig#get

四、DubboBootstrap#stop

当 Spring容器销毁时触发该方法,该方法则是完成一些销毁工作,具体不再赘述。

    public DubboBootstrap stop() throws IllegalStateException {
        destroy();
        return this;
    }
    
    public void destroy() {
        if (started.compareAndSet(true, false)
                && destroyed.compareAndSet(false, true)) {
            unregisterServiceInstance();
            unexportMetadataService();
            unexportServices();
            unreferServices();

            destroyRegistries();
            destroyProtocols();
            destroyServiceDiscoveries();

            clear();
            shutdown();
            release();
        }
    }

五、2.7.5 流程简介

通过我们上面的分析可以知道,提供者的服务导出是通过 ServiceConfig#export 方法实现。 消费者的服务引用是通过 ReferenceConfig#get 方法实现。关于这两个方法的执行流程我们在之前的文章中分析过 Dubbo2.7.0 版本的逻辑 (Main 方法执行版本),2.7.5 版本 (Spring 执行版本)的执行逻辑与之相差不大,下面简要介绍一下 Spring 2.7.5 版本下的执行过程。

这里仅仅是展示了一点流程,如果需要详细阅读,可参考 Dubbo源码分析:全集整理

1. 提供者

1.1 ServiceConfig#export

    public synchronized void export() {
    	// 不应暴露直接返回服务
        if (!shouldExport()) {
            return;
        }	
		// bootstrap  没有初始化则先初始化
        if (bootstrap == null) {
            bootstrap = DubboBootstrap.getInstance();
            bootstrap.init();
        }
		// 检查并更新配置
        checkAndUpdateSubConfigs();

        //init serviceMetadata
        // 初始化 service 元数据信息,暴露版本、分组、接口、暴露方法及入参等信息
        serviceMetadata.setVersion(version);
        serviceMetadata.setGroup(group);
        serviceMetadata.setDefaultGroup(group);
        serviceMetadata.setServiceType(getInterfaceClass());
        serviceMetadata.setServiceInterfaceName(getInterface());
        serviceMetadata.setTarget(getRef());
		// 进行服务发布
        if (shouldDelay()) {
            DELAY_EXPORT_EXECUTOR.schedule(this::doExport, getDelay(), TimeUnit.MILLISECONDS);
        } else {
            doExport();
        }
    }

这一步主要增加了 DubboBootstrap 的初始化验证 和 ServiceMetadata 的赋值

1.2 ServiceConfig#doExport

    protected synchronized void doExport() {
		....
		// 进行服务暴露
        doExportUrls();
        // dispatch a ServiceConfigExportedEvent since 2.7.4
        // 分发暴露事件,ServiceNameMappingListener 对 ServiceConfigExportedEvent 事件进行监听
        dispatch(new ServiceConfigExportedEvent(this));
    }

注: dispatch(new ServiceConfigExportedEvent(this)); 会分发 ServiceConfigExportedEvent 事件。而 ServiceNameMappingListener 对该事件进行了监听。ServiceNameMappingListener#onEvent 实现如下:

    @Override
    public void onEvent(ServiceConfigExportedEvent event) {
        ServiceConfig serviceConfig = event.getServiceConfig();
        List<URL> exportedURLs = serviceConfig.getExportedUrls();
        exportedURLs.forEach(url -> {
            String serviceInterface = url.getServiceInterface();
            String group = url.getParameter(GROUP_KEY);
            String version = url.getParameter(VERSION_KEY);
            String protocol = url.getProtocol();
            // 在配置中心上映射服务,以 zk 为例,其映射的节点为 dubbo/config/mapping/com.kingfish.service.ProviderService/simple-provider
            // 即 dubbo/config/mapping/{接口全路径}/{应用名称}
            serviceNameMapping.map(serviceInterface, group, version, protocol);
        });
    }

这里 serviceNameMapping.map(serviceInterface, group, version, protocol); 将当前 接口和应用映射到了 zk 上,如下图:
Dubbo笔记 ㉖ : DubboBootstrap 的服务暴露_第3张图片

1.3 ServiceConfig#doExportUrls

    private void doExportUrls() {
        ServiceRepository repository = ApplicationModel.getServiceRepository();
        ServiceDescriptor serviceDescriptor = repository.registerService(getInterfaceClass());
        // 注册服务提供者
        repository.registerProvider(
                getUniqueServiceName(),
                ref,
                serviceDescriptor,
                this,
                serviceMetadata
        );
		// 获取注册中心地址
        List<URL> registryURLs = ConfigValidationUtils.loadRegistries(this, true);

        for (ProtocolConfig protocolConfig : protocols) {
            String pathKey = URL.buildKey(getContextPath(protocolConfig)
                    .map(p -> p + "/" + path)
                    .orElse(path), group, version);
            // In case user specified path, register service one more time to map it to path.
            // 注册 Dubbo Service
            repository.registerService(pathKey, interfaceClass);
            // TODO, uncomment this line once service key is unified
            serviceMetadata.setServiceKey(pathKey);
            // 进行服务暴露
            doExportUrlsFor1Protocol(protocolConfig, registryURLs);
        }
    }

关于 ServiceConfig#doExportUrlsFor1Protocol 的内容基本和 2.7.0 没有差异,详参 Dubbo笔记 ④ : 服务发布流程 - doExportUrlsFor1Protocol


下面内容来源 : dubbo解析-ServiceRepository功能及属性详解

ServiceRepository是存储了所有服务端发布的服务、客户端需要访问的服务,通过ServiceRepository可以获取所有本dubbo实例发布的服务和引用的服务。

ServiceRepository是通过ApplicationModel.getServiceRepository方法创建或者获取的。
里面包括三个字段:services、consumers、providers。作用分别是:

  1. services:类型是ConcurrentMap,key是远程服务的接口名,ServiceDescriptor是服务描述符对象,ServiceDescriptor记录了服务接口的Class对象,接口名,服务接口每个方法的名字、入参类型、返回值类型等详细信息,ServiceDescriptor可以理解为记录了远程服务接口的详细描述。客户端、服务端在启动的时候都会调用ServiceRepository.registerService方法将ServiceDescriptor对象注册到services中。下面两个属性需要的ServiceDescriptor对象也是从services中获取的。

  2. consumers:类型是ConcurrentMap,key是serviceKey,serviceKey是由服务接口+“:”+group+“:”+version组成的,ConsumerModel中也有serviceKey,除了serviceKey之外还有ServiceDescriptor、ReferenceConfig对象、MethodConfig配置信息、可以访问远程服务提供者的Invoker对象。在客户端启动的时候,dubbo会调用ReferenceConfig的init方法,在init方法里面调用ServiceRepository.registerConsumer方法,该方法会创建ConsumerModel对象,并将其注册到ServiceRepository中。之后,dubbo可以访问该consumers属性,获取所有的客户端需要访问的远程服务信息。

  3. providers:类型是ConcurrentMap,key是serviceKey,serviceKey是由服务接口+“:”+group+“:”+version组成的,ProviderModel中也有serviceKey,除了serviceKey之外还有ServiceDescriptor、ServiceConfig对象以及对外提供服务的spring bean对象。在服务端启动暴露服务的时候,dubbo会调用ServiceConfig的doExportUrls方法,在doExportUrls方法里面调用ServiceRepository.registerProvider方法,该方法会创建ProviderModel对象,并将其注册到ServiceRepository中。之后,dubbo可以访问该providers属性,获取所有服务端发布的服务信息。

2. 消费者:

2.1 ReferenceConfig#get

    public synchronized T get() {
        if (destroyed) {
            throw new IllegalStateException("The invoker of ReferenceConfig(" + url + ") has already destroyed!");
        }
        if (ref == null) {
            init();
        }
        return ref;
    }

2.2 ReferenceConfig#init

    public synchronized void init() {
        if (initialized) {
            return;
        }
		// bootstrap 初始化
        if (bootstrap == null) {
            bootstrap = DubboBootstrap.getInstance();
            bootstrap.init();
        }

        checkAndUpdateSubConfigs();

        //init serivceMetadata
        // 初始化 serviceMetadata信息
        serviceMetadata.setVersion(version);
        serviceMetadata.setGroup(group);
        serviceMetadata.setDefaultGroup(group);
        serviceMetadata.setServiceType(getActualInterface());
        serviceMetadata.setServiceInterfaceName(interfaceName);
        // TODO, uncomment this line once service key is unified
        serviceMetadata.setServiceKey(URL.buildKey(interfaceName, group, version));

        checkStubAndLocal(interfaceClass);
        ConfigValidationUtils.checkMock(interfaceClass, this);

        Map<String, String> map = new HashMap<String, String>();

		... 服务参数解析,保存到 map 中
		// 将参数信息保存到 serviceMetadata#attachments 中
        serviceMetadata.getAttachments().putAll(map);
		// 注册消费者
        ServiceRepository repository = ApplicationModel.getServiceRepository();
        ServiceDescriptor serviceDescriptor = repository.registerService(interfaceClass);
        repository.registerConsumer(
                serviceMetadata.getServiceKey(),
                attributes,
                serviceDescriptor,
                this,
                null,
                serviceMetadata);
		// 创建提供者代理类
        ref = createProxy(map);

        serviceMetadata.setTarget(ref);
        serviceMetadata.addAttribute(PROXY_CLASS_REF, ref);
        // 保存提供者代理类的引用关系
        repository.lookupReferredService(serviceMetadata.getServiceKey()).setProxyObject(ref);

        initialized = true;

        // dispatch a ReferenceConfigInitializedEvent since 2.7.4
        // 分发 ReferenceConfigInitializedEvent 事件
        dispatch(new ReferenceConfigInitializedEvent(this, invoker));
    }


以上:内容部分参考
https://blog.csdn.net/weixin_38308374/article/details/105938319
如有侵扰,联系删除。 内容仅用于自我记录学习使用。如有错误,欢迎指正

你可能感兴趣的:(#,Dubbo笔记篇,dubbo,spring)