Dubbo 通信流程 - 服务的创建、导出与注册

创建服务供远程客户端调用

定义接口并实现

// 服务接口
public interface HelloService {
    String sayHello(String name);
}
import org.apache.dubbo.config.annotation.DubboService;

// 服务实现类
@DubboService
public class HelloServiceImpl implements HelloService {
    @Override
    public String sayHello(String name) {
        return "Hello, " + name;
    }
}

配置 Dubbo

# 应用名称
dubbo.application.name=hello-service-provider
# 注册中心地址,这里使用 Zookeeper
dubbo.registry.address=zookeeper://127.0.0.1:2181
# 协议和端口
dubbo.protocol.name=dubbo
dubbo.protocol.port=20880

通过上述两步,便可以将 HelloService 提供给客户端调用。

Dubbo 服务的创建、导出与注册

ServiceBean 的注册

Dubbo 的 ServiceAnnotationPostProcessor 实现了 Spring 的 BeanDefinitionRegistryPostProcessor 接口,Spring 会在 Bean 定义加载到 BeanFactory 之后、Bean 实例化之前调用该接口的 postProcessBeanDefinitionRegistry 方法。

postProcessBeanDefinitionRegistry 和 postProcessBeanFactory 的区别?
postProcessBeanDefinitionRegistry:调用时机相对较早。在 Spring 容器启动过程中,当所有的 Bean 定义信息(BeanDefinition)加载完成后,但 Bean 实例还未开始创建时,Spring 会优先调用 BeanDefinitionRegistryPostProcessor 的 postProcessBeanDefinitionRegistry 方法。这使得开发者可以在这个阶段对 BeanDefinitionRegistry 进行操作,动态地添加、修改或删除 Bean 定义。
postProcessBeanFactory:在 postProcessBeanDefinitionRegistry 方法调用之后执行。当所有的 BeanDefinitionRegistryPostProcessor 处理完毕,且 BeanDefinition 信息可能已经被修改后,Spring 会调用 BeanFactoryPostProcessor 的 postProcessBeanFactory 方法,对 ConfigurableListableBeanFactory 进行处理。

postProcessBeanDefinitionRegistry 方法调用 scanServiceBeans 方法。该方法扫描出哪些类注解了 @DubboService。

    private void scanServiceBeans(Set<String> packagesToScan, BeanDefinitionRegistry registry) {
		...
        DubboClassPathBeanDefinitionScanner scanner =
                new DubboClassPathBeanDefinitionScanner(registry, environment, resourceLoader);

        BeanNameGenerator beanNameGenerator = resolveBeanNameGenerator(registry);
        scanner.setBeanNameGenerator(beanNameGenerator);
        for (Class<? extends Annotation> annotationType : serviceAnnotationTypes) {
        	// 将 DubboService.class 和 Service.class 添加到 includeFilter 中,该 includeFilter 用于后续筛选添加了这些注解的类
            scanner.addIncludeFilter(new AnnotationTypeFilter(annotationType));
        }

        ScanExcludeFilter scanExcludeFilter = new ScanExcludeFilter();
        scanner.addExcludeFilter(scanExcludeFilter);

        for (String packageToScan : packagesToScan) {

            // avoid duplicated scans
            if (servicePackagesHolder.isPackageScanned(packageToScan)) {
                if (logger.isInfoEnabled()) {
                    logger.info("Ignore package who has already bean scanned: " + packageToScan);
                }
                continue;
            }

            if (AotWithSpringDetector.useGeneratedArtifacts()) {
                scanner.setIncludeAnnotationConfig(false);
            }
            // 将添加了 @Service 注解的类注册为 Bean
            scanner.scan(packageToScan);

            // Finds all BeanDefinitionHolders of @Service whether @ComponentScan scans or not.
            Set<BeanDefinitionHolder> beanDefinitionHolders =
                    findServiceBeanDefinitionHolders(scanner, packageToScan, registry, beanNameGenerator);

            if (!CollectionUtils.isEmpty(beanDefinitionHolders)) {
                if (logger.isInfoEnabled()) {
                    List<String> serviceClasses = new ArrayList<>(beanDefinitionHolders.size());
                    for (BeanDefinitionHolder beanDefinitionHolder : beanDefinitionHolders) {
                        serviceClasses.add(
                                beanDefinitionHolder.getBeanDefinition().getBeanClassName());
                    }
                    logger.info("Found " + beanDefinitionHolders.size()
                            + " classes annotated by Dubbo @Service under package [" + packageToScan + "]: "
                            + serviceClasses);
                }

                for (BeanDefinitionHolder beanDefinitionHolder : beanDefinitionHolders) {
                	// 构造 ServiceBean 并注册到 Spring 容器中
                    processScannedBeanDefinition(beanDefinitionHolder);
                    servicePackagesHolder.addScannedClass(
                            beanDefinitionHolder.getBeanDefinition().getBeanClassName());
                }
            } else {
                if (logger.isWarnEnabled()) {
                    logger.warn(
                            CONFIG_NO_ANNOTATIONS_FOUND,
                            "No annotations were found on the class",
                            "",
                            "No class annotated by Dubbo @DubboService or @Service was found under package ["
                                    + packageToScan + "], ignore re-scanned classes: "
                                    + scanExcludeFilter.getExcludedCount());
                }
            }

            servicePackagesHolder.addScannedPackage(packageToScan);
        }
    }

上述方法中,scanner.scan(packageToScan) 和 processScannedBeanDefinition(beanDefinitionHolder) 都分别注册了 Bean。

  • scanner.scan(packageToScan) 找出带有特定注解(如 @DubboService 等 serviceAnnotationTypes 中定义的注解)的类,得到这些具体类型的 BeanDefinition 并注册到 Spring 容器中;
  • processScannedBeanDefinition(beanDefinitionHolder) 方法会对 scanner.scan 扫描得到的 BeanDefinitionHolder 进行进一步处理。在这个方法中,会对这些 BeanDefinition 进行定制和扩展,添加与 Dubbo 服务相关的属性和配置,得到 ServiceBean 的 BeanDefinition 并注册到 Spring 容器中。

上述方法中,findServiceBeanDefinitionHolders 找出刚刚注册的 BeanDefinition,然后再一个循环里处理每个 BeanDefinition,得到 ServiceBean 的 BeanDefinition,并调用 registerServiceBeanDefinition 方法将 BeanDefinition 注册到 Spring 容器中:

    private void processScannedBeanDefinition(BeanDefinitionHolder beanDefinitionHolder) {
		// 拿到 BeanDefinition 的类型
        Class<?> beanClass = resolveClass(beanDefinitionHolder);
		// 拿到 BeanDefinition 的 @Service 注解
        Annotation service = findServiceAnnotation(beanClass);

        // 拿到 BeanDefinition 的 @Service 注解的属性
        Map<String, Object> serviceAnnotationAttributes = AnnotationUtils.getAttributes(service, true);

        String serviceInterface = resolveInterfaceName(serviceAnnotationAttributes, beanClass);
        String annotatedServiceBeanName = beanDefinitionHolder.getBeanName();
        String beanName = generateServiceBeanName(serviceAnnotationAttributes, serviceInterface);

		// 构造 ServiceBean 的 BeanDefinition
        AbstractBeanDefinition serviceBeanDefinition =
                buildServiceBeanDefinition(serviceAnnotationAttributes, serviceInterface, annotatedServiceBeanName);
		// 注册 BeanDefinition
        registerServiceBeanDefinition(beanName, serviceBeanDefinition, serviceInterface);
    }

这些 BeanDefinition 都被注册到 Spring 容器后,Spring 将在 Bean 初始化时将它们注册到注册中心。

总结来说,Dubbo 利用扫描器,把含有 @DubboService 注解的类扫描出来,得到一个 BeanDefinition 的集合。循环扫描出来的 BeanDefinition 集合,在循环体中,从已扫描的 Bean 定义中,提取注解属性和服务接口名,然后创建一个新的 ServiceBean 类型的 Bean 定义。

服务导出

服务导出的操作主要是在 Spring 容器的生命周期中,当 ServiceBean 实例化并初始化完成之后触发。更具体地说,Spring 的 AbstractApplicationContext 在执行 refresh 方法时,会发布 ContextRefreshedEvent, DubboBootstrapApplicationListener 接收到 ContextRefreshedEvent 后,会初始化并导出服务。

/**
 * An ApplicationListener to control Dubbo application.
 */
public class DubboDeployApplicationListener
        implements ApplicationListener<ApplicationContextEvent>, ApplicationContextAware, Ordered {

    @Override
    public void onApplicationEvent(ApplicationContextEvent event) {
        if (nullSafeEquals(applicationContext, event.getSource())) {
            if (event instanceof ContextRefreshedEvent) {
                onContextRefreshedEvent((ContextRefreshedEvent) event);
            } else if (event instanceof ContextClosedEvent) {
                onContextClosedEvent((ContextClosedEvent) event);
            }
        }
    }

    private void onContextRefreshedEvent(ContextRefreshedEvent event) {
        ModuleDeployer deployer = moduleModel.getDeployer();
        Assert.notNull(deployer, "Module deployer is null");
        Object singletonMutex = LockUtils.getSingletonMutex(applicationContext);
        // start module
        Future future = null;
        synchronized (singletonMutex) {
            future = deployer.start();
        }

        // if the module does not start in background, await finish
        if (!deployer.isBackground()) {
            try {
                future.get();
            } catch (InterruptedException e) {
                logger.warn(
                        CONFIG_FAILED_START_MODEL,
                        "",
                        "",
                        "Interrupted while waiting for dubbo module start: " + e.getMessage());
            } catch (Exception e) {
                logger.warn(
                        CONFIG_FAILED_START_MODEL,
                        "",
                        "",
                        "An error occurred while waiting for dubbo module start: " + e.getMessage(),
                        e);
            }
        }
    }
}

默认的 ModuleDeployer 的 start 方法实现如下:

    @Override
    public Future start() throws IllegalStateException {
        // initialize,maybe deadlock applicationDeployer lock & moduleDeployer lock
        applicationDeployer.initialize();

        return startSync();
    }

    private synchronized Future startSync() throws IllegalStateException {
        if (isStopping() || isStopped() || isFailed()) {
            throw new IllegalStateException(getIdentifier() + " is stopping or stopped, can not start again");
        }

        try {
            if (isStarting() || isStarted() || isCompletion()) {
                return startFuture;
            }

            onModuleStarting();

            initialize();

            // 导出服务
            exportServices();

            // prepare application instance
            // exclude internal module to avoid wait itself
            if (moduleModel != moduleModel.getApplicationModel().getInternalModule()) {
                applicationDeployer.prepareInternalModule();
            }

            // refer services
            referServices();

            // if no async export/refer services, just set started
            if (asyncExportingFutures.isEmpty() && asyncReferringFutures.isEmpty()) {
                // publish module started event
                onModuleStarted();

                // 将服务注册到注册中心
                registerServices();

                // check reference config
                checkReferences();

                // publish module completion event
                onModuleCompletion();

                // complete module start future after application state changed
                completeStartFuture(true);
            } else {
                frameworkExecutorRepository.getSharedExecutor().submit(() -> {
                    try {
                        // wait for export finish
                        waitExportFinish();

                        // wait for refer finish
                        waitReferFinish();

                        // publish module started event
                        onModuleStarted();

                        // register services to registry
                        registerServices();

                        // check reference config
                        checkReferences();

                        // publish module completion event
                        onModuleCompletion();
                    } catch (Throwable e) {
                        logger.warn(
                                CONFIG_FAILED_WAIT_EXPORT_REFER,
                                "",
                                "",
                                "wait for export/refer services occurred an exception",
                                e);
                        onModuleFailed(getIdentifier() + " start failed: " + e, e);
                    } finally {
                        // complete module start future after application state changed
                        completeStartFuture(true);
                    }
                });
            }

        } catch (Throwable e) {
            onModuleFailed(getIdentifier() + " start failed: " + e, e);
            throw e;
        }

        return startFuture;
    }

其中,exportServices 完成了服务导出,其调用链如下:

exportServices -> exportServiceInternal -> ServiceConfigBase.export

在 ServiceConfig 中:

export -> doExport -> doExportUrls

其中,doExportUrls 主要做了以下几件事:

  1. 获取服务仓库并注册服务描述符
  2. 创建并注册提供者模型
  3. 获取注册中心的 URL 列表
  4. 遍历协议配置并导出 URL

我们主要关注第四步,其调用链如下:

doExportUrls -> doExportUrlsFor1Protocol -> exportUrl -> exportRemote -> doExportUrl

doExportUrl 从 proxyFactory 拿到 Invoker 对象,利用 Dubbo 的 SPI 机制对服务进行导出。

    private void doExportUrl(URL url, boolean withMetaData, RegisterTypeEnum registerType) {
        if (!url.getParameter(REGISTER_KEY, true)) {
            registerType = RegisterTypeEnum.MANUAL_REGISTER;
        }
        if (registerType == RegisterTypeEnum.NEVER_REGISTER
                || registerType == RegisterTypeEnum.MANUAL_REGISTER
                || registerType == RegisterTypeEnum.AUTO_REGISTER_BY_DEPLOYER) {
            url = url.addParameter(REGISTER_KEY, false);
        }

        Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, url);
        if (withMetaData) {
            invoker = new DelegateProviderMetaDataInvoker(invoker, this);
        }
        // 利用 Dubbo 的 SPI 机制对服务进行导出
        Exporter<?> exporter = protocolSPI.export(invoker);
        exporters
                .computeIfAbsent(registerType, k -> new CopyOnWriteArrayList<>())
                .add(exporter);
    }

如果服务端使用的是 Dubbo 协议,调用的是 DubboProtocol 的 export 方法:

    @Override
    public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
        checkDestroyed();
        URL url = invoker.getUrl();

        // 生成服务键并创建 DubboExporter 对象
        String key = serviceKey(url);
        DubboExporter<T> exporter = new DubboExporter<>(invoker, key, exporterMap);

        // 处理 stub 服务事件支持
        boolean isStubSupportEvent = url.getParameter(STUB_EVENT_KEY, DEFAULT_STUB_EVENT);
        boolean isCallbackService = url.getParameter(IS_CALLBACK_SERVICE, false);
        if (isStubSupportEvent && !isCallbackService) {
            String stubServiceMethods = url.getParameter(STUB_EVENT_METHODS_KEY);
            if (stubServiceMethods == null || stubServiceMethods.length() == 0) {
                if (logger.isWarnEnabled()) {
                    logger.warn(
                            PROTOCOL_UNSUPPORTED,
                            "",
                            "",
                            "consumer [" + url.getParameter(INTERFACE_KEY)
                                    + "], has set stub proxy support event ,but no stub methods founded.");
                }
            }
        }

		// 打开服务器
        openServer(url);
        optimizeSerialization(url);

        return exporter;
    }

上述 openServer 方法根据服务的 URL 打开服务器,监听指定的网络地址和端口,以便接收来自消费者的请求。该方法会根据配置的协议(如 dubbo 协议)启动相应的网络服务。

服务的注册

完成了服务导出后,回到 ModuleDeployer,registerServices 完成了服务的注册,其调用链如下:

registerServices -> registerServiceInternal -> ServiceConfigBase.register -> Exporter.register

实际调用的类视注册中心的不同而不同,以 ZooKeeper 为例,通过模版方法实现服务注册:

public abstract class FailbackRegistry extends AbstractRegistry {
    @Override
    public void register(URL url) {
        if (!shouldRegister(url)) {
            return;
        }
        super.register(url);
        removeFailedRegistered(url);
        removeFailedUnregistered(url);
        try {
            // Sending a registration request to the server side
            doRegister(url);
        } catch (Exception e) {
            Throwable t = e;

            // If the startup detection is opened, the Exception is thrown directly.
            boolean check = getUrl().getParameter(Constants.CHECK_KEY, true)
                    && url.getParameter(Constants.CHECK_KEY, true)
                    && (url.getPort() != 0);
            boolean skipFailback = t instanceof SkipFailbackWrapperException;
            if (check || skipFailback) {
                if (skipFailback) {
                    t = t.getCause();
                }
                throw new IllegalStateException(
                        "Failed to register " + url + " to registry " + getUrl().getAddress() + ", cause: "
                                + t.getMessage(),
                        t);
            } else {
                logger.error(
                        INTERNAL_ERROR,
                        "unknown error in registry module",
                        "",
                        "Failed to register " + url + ", waiting for retry, cause: " + t.getMessage(),
                        t);
            }

            // Record a failed registration request to a failed list, retry regularly
            addFailedRegistered(url);
        }
    }
}

doRegister 仅仅是在 Zookeeper 创建一个 ZNode(Zookeeper 文件系统中的节点):

    @Override
    public void doRegister(URL url) {
        try {
            checkDestroyed();
            zkClient.create(toUrlPath(url), url.getParameter(DYNAMIC_KEY, true), true);
        } catch (Throwable e) {
            throw new RpcException(
                    "Failed to register " + url + " to zookeeper " + getUrl() + ", cause: " + e.getMessage(), e);
        }
    }

至此,DubboService 完成了服务在注册中心的注册。

你可能感兴趣的:(dubbo,java,rpc)