DUBBO源码学习(三)v2.7.8-服务的暴露过程

dubbo源码学习历史文章

DUBBO源码学习(一)spi机制
DUBBO源码学习(二)注册中心源码解析

一、DUBBO服务暴露

spring ioc容器启动的时候会同时启动dubbo容器,从2.7.5版本后增加了DubboBootstrapApplicationListener类,负责监听dubbo容器的启动事件:


/**
 * The {@link ApplicationListener} for {@link DubboBootstrap}'s lifecycle when the {@link ContextRefreshedEvent}
 * and {@link ContextClosedEvent} raised
 *
 * @since 2.7.5
 */
public class DubboBootstrapApplicationListener extends OnceApplicationContextEventListener implements Ordered {

    /**
     * The bean name of {@link DubboBootstrapApplicationListener}
     *
     * @since 2.7.6
     */
    public static final String BEAN_NAME = "dubboBootstrapApplicationListener";

    private final DubboBootstrap dubboBootstrap;

    public DubboBootstrapApplicationListener() {
        this.dubboBootstrap = DubboBootstrap.getInstance();
    }

    public DubboBootstrapApplicationListener(ApplicationContext applicationContext) {
        super(applicationContext);
        this.dubboBootstrap = DubboBootstrap.getInstance();
        DubboBootstrapStartStopListenerSpringAdapter.applicationContext = applicationContext;
    }

    /**
     * Spring ioc 容器事件监听
     * @param event
     */
    @Override
    public void onApplicationContextEvent(ApplicationContextEvent event) {
        if (DubboBootstrapStartStopListenerSpringAdapter.applicationContext == null) {
            DubboBootstrapStartStopListenerSpringAdapter.applicationContext = event.getApplicationContext();
        }
        //容器刷新事件
        if (event instanceof ContextRefreshedEvent) {
            onContextRefreshedEvent((ContextRefreshedEvent) event);

        //容器关闭事件
        } else if (event instanceof ContextClosedEvent) {
            onContextClosedEvent((ContextClosedEvent) event);
        }
    }

    private void onContextRefreshedEvent(ContextRefreshedEvent event) {
        dubboBootstrap.start();
    }

    private void onContextClosedEvent(ContextClosedEvent event) {
        DubboShutdownHook.getDubboShutdownHook().run();
    }

    @Override
    public int getOrder() {
        return LOWEST_PRECEDENCE;
    }
}

可以看到DubboBootstrapApplicationListener继承了OnceApplicationContextEventListener类,主要是实现了onApplicationContextEvent 方法,通过此方法监听了同期的刷新与关闭事件,同时也实现了Ordered接口,执行的顺序为最低顺序。

1.1、DUBBO容器的启动

DUBBO容器的启动是由DubboBootstrap来执行的。DubboBootstrap中有比较重要的两个对象,一个是ConfigManager,一个是Environment。
在这里插入图片描述
从上图可以看出,配置加载大概分为两个阶段:

  1. 第一阶段为DubboBootstrap初始化之前,在Spring context启动时解析处理XML配置/注解配置/Java-config 或者是执行API配置代码,创建config bean并且加入到ConfigManager中。
  2. 第二阶段为DubboBootstrap初始化过程,从配置中心读取外部配置,依次处理实例级属性配置和应用级属性配置,最后刷新所有配置实例的属性,也就是属性覆盖。
  • ConfigManager:顾名思义是一个配置中心,有RegistryConfig(注册中心配置)、ConsumerConfig(消费者配置)、ProtocolConfig(协议配置)、ProviderConfig(服务提供者配置)、ApplicationConfig(容器配置)、MonitorConfig(监控中心配置)。
  • Environment:Environment主要处理的与系统配置相关,比如Java系统配置,以及配置中心的配置。

DUBBO容器的启动是通过start方法执行的:

public class DubboBootstrap {

	/**
     * Dubbo的启动
     */
    public DubboBootstrap start() {
        //通过cas原子操作代替加锁
        if (started.compareAndSet(false, true)) {
            ready.set(false);

            //初始化操作
            initialize();
            if (logger.isInfoEnabled()) {
                logger.info(NAME + " is starting...");
            }
            
            // Dubbo Services服务暴露
            exportServices();

            // 是否配置了元数据中心的url
            if (!isOnlyRegisterProvider() || hasExportedServices()) {
                // export MetadataService 暴露本地的元数据服务
                exportMetadataService();
                // 将本地serviceInstance注册到注册中心
                registerServiceInstance();
            }

            //处理ReferenceConfig对象
            referServices();

            //service异步暴露后的返回处理
            if (asyncExportingFutures.size() > 0) {
                new Thread(() -> {
                    try {
                        this.awaitFinish();
                    } catch (Exception e) {
                        logger.warn(NAME + " exportAsync occurred an exception.");
                    }
                    ready.set(true);
                    if (logger.isInfoEnabled()) {
                        logger.info(NAME + " is ready.");
                    }
                    //spi机制,获取所有DubboBootstrapStartStopListener,本包下会拿到DubboBootstrapStartStopListenerSpringAdapter类
                    ExtensionLoader<DubboBootstrapStartStopListener> exts = getExtensionLoader(DubboBootstrapStartStopListener.class);
                    //发布DubboBootstrapStatedEvent事件
                    exts.getSupportedExtensionInstances().forEach(ext -> ext.onStart(this));
                }).start();
            } else {
                //service同步暴露处理,与上一步类似
                ready.set(true);
                if (logger.isInfoEnabled()) {
                    logger.info(NAME + " is ready.");
                }
                ExtensionLoader<DubboBootstrapStartStopListener> exts = getExtensionLoader(DubboBootstrapStartStopListener.class);
                exts.getSupportedExtensionInstances().forEach(ext -> ext.onStart(this));
            }
            if (logger.isInfoEnabled()) {
                logger.info(NAME + " has started.");
            }
        }
        return this;
    }
    
    
    /**
     * 暴露服务
     */
    private void exportServices() {
        // 创建的每个ServiceConfig对象都添加到configManager,下面获取所有的ServiceConfig对象并遍历
        configManager.getServices().forEach(sc -> {
            ServiceConfig serviceConfig = (ServiceConfig) sc;
            serviceConfig.setBootstrap(this);

            //异步暴露,使用线程池暴露服务
            if (exportAsync) {
                ExecutorService executor = executorRepository.getServiceExporterExecutor();
                Future<?> future = executor.submit(() -> {
                    try {
                        //暴露服务的动作
                        exportService(serviceConfig);
                    } catch (Throwable t) {
                        logger.error("export async catch error : " + t.getMessage(), t);
                    }
                });
                asyncExportingFutures.add(future);
            } else {
                //暴露服务
                exportService(serviceConfig);
            }
        });
    }

    
    private void exportService(ServiceConfig sc) {
        if (exportedServices.containsKey(sc.getServiceName())) {
            throw new IllegalStateException("There are multiple ServiceBean instances with the same service name: [" +
                    sc.getServiceName() + "], instances: [" +
                    exportedServices.get(sc.getServiceName()).toString() + ", " +
                    sc.toString() + "]. Only one service can be exported for the same triple (group, interface, version), " +
                    "please modify the group or version if you really need to export multiple services of the same interface.");
        }
        //服务暴露
        sc.export();
        //服务暴露后放入缓存,防止重复暴露
        exportedServices.put(sc.getServiceName(), sc);
    }

}

二、serviceConfig服务暴露

在这里插入图片描述
老图新用,可以看到dubbo的十层协议的第二层,就是serviceConfig.export()。通过serviceConfig.export()方法,我们可以将dubbo的服务通过不同的protocol暴露到注册中心中。

public class ServiceConfig<T> extends ServiceConfigBase<T> {
    
    @Override
    public synchronized void export() {
        if (bootstrap == null) {
            bootstrap = DubboBootstrap.getInstance();
            if (null != this.getRegistry()) {
                //获取注册中心,上一篇文章有分析如何获取注册中心
                bootstrap.registries(this.getRegistries());
            }
            
            //初始化操作
            bootstrap.initialize();
        }

        //检查并且更新配置
        checkAndUpdateSubConfigs();

        initServiceMetadata(provider);
        serviceMetadata.setServiceType(getInterfaceClass());
        serviceMetadata.setTarget(getRef());
        serviceMetadata.generateServiceKey();

        if (!shouldExport()) {
            return;
        }

        //延时暴露
        if (shouldDelay()) {
            DELAY_EXPORT_EXECUTOR.schedule(this::doExport, getDelay(), TimeUnit.MILLISECONDS);
        } else {
            doExport();
        }

        exported();
    }

     
}

2.1、dubbo服务配置初始化

在boostrap初始化的时候,需要执行initialize方法,可以看到这都是dubbo服务相关配置的初始化操作。

 	/**
     * 初始化操作
     */
    public void initialize() {
        //原子性操作,目的是为了只初始化一次
        if (!initialized.compareAndSet(false, true)) {
            return;
        }

        //初始化FrameworkExt实现类,这里会调用Environment的initialize方法
        ApplicationModel.initFrameworkExts();

        //启动配置中心
        startConfigCenter();

        //加载远程配置
        loadRemoteConfigs();

        //验证全局配置
        checkGlobalConfigs();

        //启动元数据配置中心
        // @since 2.7.8
        startMetadataCenter();

        //初始化dubbo本地服务
        initMetadataService();

        if (logger.isInfoEnabled()) {
            logger.info(NAME + " has been initialized!");
        }
    }

2.2、checkAndUpdateSubConfigs()

这个方法看起来只是一个简单的方法校验,但是实际上里面做了很多的东西,包括泛化调用的判断以及本地存根的判断等。

private void checkAndUpdateSubConfigs() {
        //  使用显示配置的provider、module、application 来进行一些全局配置,其优先级为 ServiceConfig > provider > module > application
        completeCompoundConfigs();

        // 校验配置中心。这里会通过ConfigManager启动配置中心,并加载外部化配置。
        checkDefault();

        // 检查protocol是否存在,不存在则使用默认dubbo协议
        checkProtocol();
        // spi获取自定义的ConfigInitializer 可以通过此方式加载自定义serviceConfig配置
        List<ConfigInitializer> configInitializers = ExtensionLoader.getExtensionLoader(ConfigInitializer.class)
                .getActivateExtension(URL.valueOf(CONFIG_INITIALIZER_PROTOCOL), (String[]) null);
        configInitializers.forEach(e -> e.initServiceConfig(this));

        // 如果协议不是inJvm,检查address的合法性
        if (!isOnlyInJvm()) {
            checkRegistry();
        }
        //将Environment变量注入到当前对象的配置中
        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 = TRUE_VALUE;
            }
        } else {
            try {
                //反射获取接口
                interfaceClass = Class.forName(interfaceName, true, Thread.currentThread()
                        .getContextClassLoader());
            } catch (ClassNotFoundException e) {
                throw new IllegalStateException(e.getMessage(), e);
            }
            //校验接口方法的存在于配置
            checkInterfaceAndMethods(interfaceClass, getMethods());
            // 校验实现类的存在于是否继承该接口
            checkRef();
            generic = FALSE_VALUE;
        }
        //已过时 废弃
        if (local != null) {
            if (TRUE_VALUE.equals(local)) {
                local = interfaceName + LOCAL_SUFFIX;
            }
            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);
            }
        }

        /*
         * 本地存根  配置<dubbo:service interface="com.foo.BarService" stub="true" />
         * 远程服务后,客户端通常只剩下接口,而实现全在服务器端,但提供方有些时候想在客户端也执行部分逻辑,
         * 比如:做 ThreadLocal 缓存,提前验证参数,调用失败后伪造容错数据等等,此时就需要在 API 中带上 Stub,
         * 客户端生成 Proxy 实例,会把 Proxy 通过构造函数传给 Stub 1,然后把 Stub 暴露给用户,Stub 可以决定要不要去调 Proxy。
         */
        if (stub != null) {
            if (TRUE_VALUE.equals(stub)) {
                stub = interfaceName + STUB_SUFFIX;
            }
            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();
    }


	private void postProcessConfig() {
        //spi,自定义方法
        List<ConfigPostProcessor> configPostProcessors = ExtensionLoader.getExtensionLoader(ConfigPostProcessor.class)
                .getActivateExtension(URL.valueOf(CONFIG_POST_PROCESSOR_PROTOCOL), (String[]) null);
        configPostProcessors.forEach(component -> component.postProcessServiceConfig(this));
    }

可以看到仅仅这一处方法,就有两个地方用到spi加载自定义方法执行的方式,体现了dubbo的高度灵活性。
在这里插入图片描述
再简单讲下stub,其工作流程其实与apo的流程类似:

  1. 服务消费者发起调用
  2. 如果服务消费者端存在本地存根 Stub 的话,会先执行本地存根
  3. 本地存根 Stub 持有远程服务的 Proxy 对象,Stub 在执行的时候,会先执行自己的逻辑 (before),然后通过 Proxy 发起远程调用,最后在返回过程之前也会执行自己的逻辑 (after-returning)
  4. 如果远程服务的 Proxy 对象在执行过程中抛出了 exception,会执行服务消费端的本地伪装 Mock 的逻辑 (after-throwing),返回容错数据,从而达到服务降级的目的

2.3、doExport -> doExportUrls服务暴露

	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();
        bootstrap.setReady(true);
    }


	private void doExportUrls() {
        //存储了所有服务端发布的服务、客户端需要访问的服务,通过ServiceRepository可以获取所有本dubbo实例发布的服务和引用的服务。
        ServiceRepository repository = ApplicationModel.getServiceRepository();
        //存储了服务接口的Class对象,接口名,服务接口每个方法的名字、入参类型、返回值类型等详细信息.
        ServiceDescriptor serviceDescriptor = repository.registerService(getInterfaceClass());
        //存储了provider相关信息
        repository.registerProvider(
                getUniqueServiceName(),
                ref,
                serviceDescriptor,
                this,
                serviceMetadata
        );

        // 获取所有注册中心的URL
        List<URL> registryURLs = ConfigValidationUtils.loadRegistries(this, true);

        int protocolConfigNum = protocols.size();
        //所有协议处理
        for (ProtocolConfig protocolConfig : protocols) {
            // pathKey = group/contextpath/path:version
            String pathKey = URL.buildKey(getContextPath(protocolConfig)
                    .map(p -> p + "/" + path)
                    .orElse(path), group, version);
            repository.registerService(pathKey, interfaceClass);
            // 根据协议暴露,注册到各个注册中心
            doExportUrlsFor1Protocol(protocolConfig, registryURLs, protocolConfigNum);
        }
    }

2.4、doExportUrlsFor1Protocol根据不同协议暴露服务

private void doExportUrlsFor1Protocol(ProtocolConfig protocolConfig, List<URL> registryURLs, int protocolConfigNum) {
        String name = protocolConfig.getName();
        if (StringUtils.isEmpty(name)) {
            name = DUBBO;
        }

        //参数配置
        Map<String, String> map = new HashMap<String, String>();
        map.put(SIDE_KEY, PROVIDER_SIDE);

        ServiceConfig.appendRuntimeParameters(map);
        AbstractConfig.appendParameters(map, getMetrics());
        AbstractConfig.appendParameters(map, getApplication());
        AbstractConfig.appendParameters(map, getModule());
        AbstractConfig.appendParameters(map, provider);
        AbstractConfig.appendParameters(map, protocolConfig);
        AbstractConfig.appendParameters(map, this);
        MetadataReportConfig metadataReportConfig = getMetadataReportConfig();
        if (metadataReportConfig != null && metadataReportConfig.isValid()) {
            map.putIfAbsent(METADATA_KEY, REMOTE_METADATA_STORAGE_TYPE);
        }
        //methods 配置解析,<dubbo:method />
        if (CollectionUtils.isNotEmpty(getMethods())) {
            for (MethodConfig method : getMethods()) {
                // 添加 MethodConfig 对象的字段信息到 map 中,键 = 方法名.属性名。
                // 比如存储 <dubbo:method name="sayHello" retries="2"> 对应的 MethodConfig,
                // 键 = sayHello.retries,map = {"sayHello.retries": 2}
                AbstractConfig.appendParameters(map, method, method.getName());
                String retryKey = method.getName() + RETRY_SUFFIX;
                if (map.containsKey(retryKey)) {
                    String retryValue = map.remove(retryKey);
                    // 设置重试次数
                    if (FALSE_VALUE.equals(retryValue)) {
                        map.put(method.getName() + RETRIES_SUFFIX, ZERO_VALUE);
                    }
                }
                List<ArgumentConfig> arguments = method.getArguments();
                if (CollectionUtils.isNotEmpty(arguments)) {
                    for (ArgumentConfig argument : arguments) {
                        if (argument.getType() != null && argument.getType().length() > 0) {
                            //所有的接口方法
                            Method[] methods = interfaceClass.getMethods();
                            if (methods.length > 0) {
                                for (int i = 0; i < methods.length; i++) {
                                    String methodName = methods[i].getName();
                                    // target the method, and get its signature
                                    if (methodName.equals(method.getName())) {
                                        Class<?>[] argtypes = methods[i].getParameterTypes();
                                        // argument.getIndex() 默认值是 -1 ,如果这里不为-1,说明用户不仅设置了type还设置了index
                                        if (argument.getIndex() != -1) {
                                            // 检测 ArgumentConfig 中的 type 属性与方法参数列表
                                            if (argtypes[argument.getIndex()].getName().equals(argument.getType())) {
                                                // 添加 ArgumentConfig信息到缓存
                                                AbstractConfig
                                                        .appendParameters(map, argument, method.getName() + "." + argument.getIndex());
                                            } else {
                                                throw new IllegalArgumentException(
                                                        "Argument config error : the index attribute and type attribute not match :index :" +
                                                                argument.getIndex() + ", type:" + argument.getType());
                                            }
                                        } else {
                                            // index =-1 则需要遍历所有参数列表,获取到指定type 类型的参数
                                            for (int j = 0; j < argtypes.length; j++) {
                                                // 从参数类型列表中查找类型名称为 argument.type 的参数
                                                Class<?> argclazz = argtypes[j];
                                                if (argclazz.getName().equals(argument.getType())) {
                                                    AbstractConfig.appendParameters(map, argument, method.getName() + "." + j);
                                                    if (argument.getIndex() != -1 && argument.getIndex() != j) {
                                                        throw new IllegalArgumentException(
                                                                "Argument config error : the index attribute and type attribute not match :index :" +
                                                                        argument.getIndex() + ", type:" + argument.getType());
                                                    }
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        } else if (argument.getIndex() != -1) {
                            AbstractConfig.appendParameters(map, argument, method.getName() + "." + argument.getIndex());
                        } else {
                            throw new IllegalArgumentException(
                                    "Argument config must set index or type attribute.eg: <dubbo:argument index='0' .../> or <dubbo:argument type=xxx .../>");
                        }

                    }
                }
            } // end of methods for
        }

        //如果是泛化调用,设置对于应参数
        if (ProtocolUtils.isGeneric(generic)) {
            map.put(GENERIC_KEY, generic);
            map.put(METHODS_KEY, ANY_VALUE);
        } else {
            //版本
            String revision = Version.getVersion(interfaceClass, version);
            if (revision != null && revision.length() > 0) {
                map.put(REVISION_KEY, revision);
            }
            // 获取所有方法,使用逗号拼接存入缓存
            String[] methods = methods(this.interfaceClass);
            if (methods.length == 0) {
                logger.warn("No method found in service interface " + interfaceClass.getName());
                map.put(METHODS_KEY, ANY_VALUE);
            } else {
                map.put(METHODS_KEY, StringUtils.join(new HashSet<String>(Arrays.asList(methods)), ","));
            }
        }

        /**
         * Here the token value configured by the provider is used to assign the value to ServiceConfig#token
         */
        //如果接口使用了token验证,则对token处理
        if (ConfigUtils.isEmpty(token) && provider != null) {
            token = provider.getToken();
        }

        if (!ConfigUtils.isEmpty(token)) {
            if (ConfigUtils.isDefault(token)) {
                map.put(TOKEN_KEY, UUID.randomUUID().toString());
            } else {
                map.put(TOKEN_KEY, token);
            }
        }
        //init serviceMetadata attachments
        serviceMetadata.getAttachments().putAll(map);

        // 获取dubbo暴露的地址端口
        String host = findConfigedHosts(protocolConfig, registryURLs, map);
        Integer port = findConfigedPorts(protocolConfig, name, map, protocolConfigNum);

        //拼接url
        URL url = new URL(name, host, port, getContextPath(protocolConfig).map(p -> p + "/" + path).orElse(path), map);

        // SPI处理,可以自定义url拼接规则
        if (ExtensionLoader.getExtensionLoader(ConfiguratorFactory.class)
                .hasExtension(url.getProtocol())) {
            url = ExtensionLoader.getExtensionLoader(ConfiguratorFactory.class)
                    .getExtension(url.getProtocol()).getConfigurator(url).configure(url);
        }


        String scope = url.getParameter(SCOPE_KEY);
        // scope 为SCOPE_NONE 则不导出服务
        if (!SCOPE_NONE.equalsIgnoreCase(scope)) {

            // scope 不为SCOPE_NONE 则导出本地服务
            if (!SCOPE_REMOTE.equalsIgnoreCase(scope)) {
                // 本地服务导出
                exportLocal(url);
            }
            //scope 不为SCOPE_LOCAL则导出远程服务
            if (!SCOPE_LOCAL.equalsIgnoreCase(scope)) {
                // 需要判断服务注册中心是否存在
                if (CollectionUtils.isNotEmpty(registryURLs)) {
                    for (URL registryURL : registryURLs) {
                        if (SERVICE_REGISTRY_PROTOCOL.equals(registryURL.getProtocol())) {
                            url = url.addParameterIfAbsent(REGISTRY_TYPE_KEY, SERVICE_REGISTRY_TYPE);
                        }

                        //if protocol is only injvm ,not register
                        if (LOCAL_PROTOCOL.equalsIgnoreCase(url.getProtocol())) {
                            continue;
                        }
                        // 是否动态,该字段标识是有自动管理服务提供者的上线和下线,若为false 则人工管理
                        url = url.addParameterIfAbsent(DYNAMIC_KEY, registryURL.getParameter(DYNAMIC_KEY));
                        // 监控中心配置
                        URL monitorUrl = ConfigValidationUtils.loadMonitor(this, registryURL);
                        if (monitorUrl != null) {
                            url = url.addParameterAndEncoded(MONITOR_KEY, monitorUrl.toFullString());
                        }
                        if (logger.isInfoEnabled()) {
                            if (url.getParameter(REGISTER_KEY, true)) {
                                logger.info("Register dubbo service " + interfaceClass.getName() + " url " + url + " to registry " +
                                        registryURL);
                            } else {
                                logger.info("Export dubbo service " + interfaceClass.getName() + " to url " + url);
                            }
                        }

                        // 代理配置解析
                        String proxy = url.getParameter(PROXY_KEY);
                        if (StringUtils.isNotEmpty(proxy)) {
                            registryURL = registryURL.addParameter(PROXY_KEY, proxy);
                        }

                        // 远程服务导出,把实现类包装成invoker
                        Invoker<?> invoker = PROXY_FACTORY.getInvoker(ref, (Class) interfaceClass,
                                registryURL.addParameterAndEncoded(EXPORT_KEY, url.toFullString()));

                        // DelegateProviderMetaDataInvoker 用于持有 Invoker 和 ServiceConfig
                        DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this);

                        // 暴露服务,并生成 Exporter,注册到注册中心
                        Exporter<?> exporter = PROTOCOL.export(wrapperInvoker);
                        exporters.add(exporter);
                    }
                } else {
                    if (logger.isInfoEnabled()) {
                        logger.info("Export dubbo service " + interfaceClass.getName() + " to url " + url);
                    }
                    // 如果没有注册中心,则采用直连方式,即没有服务注册和监听的过程
                    Invoker<?> invoker = PROXY_FACTORY.getInvoker(ref, (Class) interfaceClass, url);
                    DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this);

                    //暴露服务,注册到注册中心
                    Exporter<?> exporter = PROTOCOL.export(wrapperInvoker);
                    exporters.add(exporter);
                }

                // 元数据存储
                MetadataUtils.publishServiceDefinition(url);
            }
        }
        this.urls.add(url);
    }

方法比较长,值得关注的有两个方法:

  • 获取invoker:Invoker<?> invoker = PROXY_FACTORY.getInvoker(ref, (Class) interfaceClass, url);
  • 服务暴露:Exporter<?> exporter = PROTOCOL.export(wrapperInvoker)

2.5、获取invoker

在 Dubbo 中,Invoker 是一个非常重要的模型。在服务提供端,以及服务引用端均会出现 Invoker。Dubbo 官方文档中对
Invoker 进行了说明,这里引用一下。 Invoker 是实体域,它是 Dubbo
的核心模型,其它模型都向它靠扰,或转换成它,它代表一个可执行体,可向它发起 invoke
调用,它有可能是一个本地的实现,也可能是一个远程的实现,也可能一个集群实现。

主要是通过ProxyFactory接口去获取invoker,默认的实现类是JavassistProxyFactory
在这里插入图片描述

public class JavassistProxyFactory extends AbstractProxyFactory {

    @Override
    @SuppressWarnings("unchecked")
    public <T> T getProxy(Invoker<T> invoker, Class<?>[] interfaces) {
        return (T) Proxy.getProxy(interfaces).newInstance(new InvokerInvocationHandler(invoker));
    }

    @Override
    public <T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) {
        // 为目标类创建 Wrapper
        final Wrapper wrapper = Wrapper.getWrapper(proxy.getClass().getName().indexOf('$') < 0 ? proxy.getClass() : type);
        // 创建匿名 Invoker 类对象,并实现 doInvoke 方法。
        return new AbstractProxyInvoker<T>(proxy, type, url) {
            @Override
            protected Object doInvoke(T proxy, String methodName,
                                      Class<?>[] parameterTypes,
                                      Object[] arguments) throws Throwable {
                // 调用 Wrapper 的 invokeMethod 方法,invokeMethod 最终会调用目标方法
                return wrapper.invokeMethod(proxy, methodName, parameterTypes, arguments);
            }
        };
    }

}

可以看到,AbstractProxyInvoker通过invoker方法调用了 Wrapper 的 invokeMethod 方法,invokeMethod 最终会调用目标方法,并返回了wapper后的Invoker对象,而且可以看到最后返回的是一个包装后的CompletableFuture对象,可以进行异步处理:

public abstract class AbstractProxyInvoker<T> implements Invoker<T> {
    
    /**
     * invoke 执行方法
     * @param invocation
     * @return
     * @throws RpcException
     */
    @Override
    public Result invoke(Invocation invocation) throws RpcException {
        try {
            // wrapper的invokeMethod方法
            Object value = doInvoke(proxy, invocation.getMethodName(), invocation.getParameterTypes(), invocation.getArguments());
            CompletableFuture<Object> future = wrapWithFuture(value);
            CompletableFuture<AppResponse> appResponseFuture = future.handle((obj, t) -> {
                AppResponse result = new AppResponse(invocation);
                if (t != null) {
                    if (t instanceof CompletionException) {
                        result.setException(t.getCause());
                    } else {
                        result.setException(t);
                    }
                } else {
                    result.setValue(obj);
                }
                return result;
            });
            return new AsyncRpcResult(appResponseFuture, invocation);
        } catch (InvocationTargetException e) {
            if (RpcContext.getContext().isAsyncStarted() && !RpcContext.getContext().stopAsync()) {
                logger.error("Provider async started, but got an exception from the original method, cannot write the exception back to consumer because an async result may have returned the new thread.", e);
            }
            return AsyncRpcResult.newDefaultAsyncResult(null, e.getTargetException(), invocation);
        } catch (Throwable e) {
            throw new RpcException("Failed to invoke remote proxy method " + invocation.getMethodName() + " to " + getUrl() + ", cause: " + e.getMessage(), e);
        }
    }
    
}

2.6、远程服务暴露

Exporter<?> exporter = PROTOCOL.export(wrapperInvoker);
获取到上一步获取的wapper后,就可以开始进行远程服务的暴露,PROTOCOL.export(wrapperInvoker)会调用到Protocol接口的export方法。首先会调用到RegistryProtocol.export()

public class RegistryProtocol implements Protocol {
    
	/**
     * 暴露服务
     */
    @Override
    public <T> Exporter<T> export(final Invoker<T> originInvoker) throws RpcException {
        //注册中心url以 zookeeper 注册中心为例,得到的示例 URL 如下:
        // zookeeper://127.0.0.1:2181/com.alibaba.dubbo.registry.RegistryService?application=demo-provider&dubbo=2.0.2&export=dubbo%3A%2F%2F172.17.48.52%3A20880%2Fcom.alibaba.dubbo.demo.DemoService%3Fanyhost%3Dtrue%26application%3Ddemo-provider
        URL registryUrl = getRegistryUrl(originInvoker);
        //导出服务url
        URL providerUrl = getProviderUrl(originInvoker);

        // 获取订阅 URL,比如:
        // provider://172.17.48.52:20880/com.alibaba.dubbo.demo.DemoService?category=configurators&check=false&anyhost=true&application=demo-provider&dubbo=2.0.2&generic=false&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello
        final URL overrideSubscribeUrl = getSubscribedOverrideUrl(providerUrl);
        // 创建监听器
        final OverrideListener overrideSubscribeListener = new OverrideListener(overrideSubscribeUrl, originInvoker);
        overrideListeners.put(overrideSubscribeUrl, overrideSubscribeListener);

        providerUrl = overrideUrlWithConfig(providerUrl, overrideSubscribeListener);
        // 启动netty服务导出服务
        final ExporterChangeableWrapper<T> exporter = doLocalExport(originInvoker, providerUrl);

        //获取注册中心实现类,如ZookeeperRegistry
        final Registry registry = getRegistry(originInvoker);

        // 获取已注册的服务提供者 URL,比如:
        // dubbo://172.17.48.52:20880/com.alibaba.dubbo.demo.DemoService?anyhost=true&application=demo-provider&dubbo=2.0.2&generic=false&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello
        final URL registeredProviderUrl = getUrlToRegistry(providerUrl, registryUrl);

        // 标注是否需要延期发布
        boolean register = providerUrl.getParameter(REGISTER_KEY, true);
        if (register) {
            //注册服务
            registry.register(registeredProviderUrl);
        }

        // register stated url on provider model
        registerStatedUrl(registryUrl, registeredProviderUrl, register);


        exporter.setRegisterUrl(registeredProviderUrl);
        // 向注册中心进行订阅 override 数据
        exporter.setSubscribeUrl(overrideSubscribeUrl);

        // 注册中心订阅,上一篇有分析
        registry.subscribe(overrideSubscribeUrl, overrideSubscribeListener);

        //监听器通知,可以通过spi自定义
        notifyExport(exporter);
        //Ensure that a new exporter instance is returned every time export
        return new DestroyableExporter<>(exporter);
    }
    
}

对于服务注册的这块内容,可以参考上一篇文章。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值