Dubbo源码阅读-服务导出

本文详细探讨了Dubbo服务导出的过程,包括前置工作如配置检查和URL装配,服务导出到本地和远程的步骤,以及Invoker的创建过程。在服务导出时,Dubbo检查export配置,支持多协议和多注册中心,通过ProxyFactory的JavassistProxyFactory创建Invoker,最终完成服务的本地和远程导出。
摘要由CSDN通过智能技术生成

1.概述

本篇文章,我们来研究一下 Dubbo 导出服务的过程。Dubbo 服务导出过程大致可分为三个部分:第一部分是前置工作,主要用于检查参数,组装 URL。第二部分是导出服务,包含导出服务到本地 (JVM),和导出服务到远程两个过程。第三部分是向注册中心注册服务,用于服务发现。本篇文章将会对这三个部分代码进行详细的分析。

2.源码

服务导出的入口为:DubboBootstrap.exportServices(),首先会遍历解析完成的所有的ServiceConfig,然后进行服务的导出。


    private void exportServices() {
        configManager.getServices().forEach(sc -> {
            // TODO, compatible with ServiceConfig.export()
            ServiceConfig serviceConfig = (ServiceConfig) sc;
            serviceConfig.setBootstrap(this);

            if (exportAsync) {
                ExecutorService executor = executorRepository.getServiceExporterExecutor();
                Future<?> future = executor.submit(() -> {
                    // 导出服务
                    sc.export();
                    exportedServices.add(sc);
                });
                asyncExportingFutures.add(future);
            } else {
                sc.export();
                exportedServices.add(sc);
            }
        });
    }

ServiceConfig.export()


    public synchronized void export() {
        // 通过export判断是不是要暴露
        if (!shouldExport()) {
            return;
        }
        // 如果bootstrap没有初始化,则初始化
        if (bootstrap == null) {
            bootstrap = DubboBootstrap.getInstance();
            bootstrap.init();
        }
        // 校验和更新一些子配置,例如:设置默认provider、设置默认protocol、检查ref是不是为空,接口类型是不是匹配等
        checkAndUpdateSubConfigs();

        //init serviceMetadata
        serviceMetadata.setVersion(getVersion());
        serviceMetadata.setGroup(getGroup());
        serviceMetadata.setDefaultGroup(getGroup());
        serviceMetadata.setServiceType(getInterfaceClass());
        serviceMetadata.setServiceInterfaceName(getInterface());
        serviceMetadata.setTarget(getRef());
        // 延迟暴露
        if (shouldDelay()) {
            DELAY_EXPORT_EXECUTOR.schedule(this::doExport, getDelay(), TimeUnit.MILLISECONDS);
        } else {
            // 执行export
            doExport();
        }
        // 调度已经暴露事件
        exported();
    }

2.1 前置工作

前置工作主要包含两个部分,分别是配置检查,以及 URL 装配(dubbo通过采用 URL 作为配置信息的统一格式,所有扩展点都通过传递 URL 携带配置信息)。

在导出服务之前,Dubbo 需要检查用户的配置是否合理,或者为用户补充缺省默认配置。

2.1.1 检查配置

export 方法对两项配置进行了检查,并根据配置执行相应的动作。首先是 export 配置,这个配置决定了是否导出服务。有时候我们只是想本地启动服务进行一些调试工作,我们并不希望把本地启动的服务暴露出去给别人调用。此时,我们可通过配置 export 禁止服务导出

<dubbo:provider export="false" />

 // 通过export判断是不是要暴露
        if (!shouldExport()) {
            return;
        }

校验和更新一些子配置,例如:设置默认provider、设置默认protocol、检查ref是不是为空,接口类型是不是匹配等,配置检查并非本文重点,因此接下来重点介绍doExportUrls 方法


    private void checkAndUpdateSubConfigs() {
        // Use default configs defined explicitly with global scope
        completeCompoundConfigs();
        // 设置默认provider
        checkDefault();
        // 设置默认protocol
        checkProtocol();
        // init some null configuration.
        List<ConfigInitializer> configInitializers = ExtensionLoader.getExtensionLoader(ConfigInitializer.class)
                .getActivateExtension(URL.valueOf("configInitializer://"), (String[]) null);
        configInitializers.forEach(e -> e.initServiceConfig(this));

        // if protocol is not injvm checkRegistry
        if (!isOnlyInJvm()) {
            checkRegistry();
        }
        // 刷新类属性,通过set方法或者@Paramter注解设置类属性
        this.refresh();

        if (StringUtils.isEmpty(interfaceName)) {
            throw new IllegalStateException("<dubbo:service interface=\"\" /> interface not allow null!");
        }
        // 泛化处理服务和普通服务类
        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);
            }
            checkInterfaceAndMethods(interfaceClass, getMethods());
            // 检查ref是不是为空,接口类型是不是匹配
            checkRef();
            generic = Boolean.FALSE.toString();
        }
        if (local != null) {
            if ("true".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) {
            if ("true".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);
            }
        }
        checkStubAndLocal(interfaceClass);
        ConfigValidationUtils.checkMock(interfaceClass, this);
        ConfigValidationUtils.validateServiceConfig(this);
        postProcessConfig();
    }

导出服务


    protected synchronized void doExport() {
        if (unexported) {
            throw new IllegalStateException("The service " + interfaceClass.getName() + " has already unexported!");
        }
        if (exported) {
            return;
        }
        exported = true;

        if (StringUtils.isEmpty(path)) {
            path = interfaceName;
        }
        // 导出服务
        doExportUrls();
    }

2.1.2 多协议多注册中心导出服务

Dubbo 允许我们使用不同的协议导出服务,也允许我们向多个注册中心注册服务。Dubbo 在 doExportUrls 方法中对多协议,多注册中心进行了支持。相关代码如下:


    @SuppressWarnings({"unchecked", "rawtypes"})
    private void doExportUrls() {

        // 加载注册中心链接
        List<URL> registryURLs = ConfigValidationUtils.loadRegistries(this, true);
  
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值