Dubbo服务暴露源码解析

前言

前面那篇文章简单介绍了Dubbo是什么,而今天的这篇文章将正式展开对其源码的解析,为我们进入更深层次的探索铺平了道路。

环境配置

使用的Dubbo版本为3.2.0,注册中心使用zookeeper,demo为官方springboot演示代码。

Provide启动

我们看到springboot启动类上多了一个@EnableDubbo注解,我们点进这个注解发现它其实@EnableDubboConfig、@DubboComponentScan两个注解。

java 复制代码
@EnableDubboConfig
@DubboComponentScan
public @interface EnableDubbo {
java 复制代码
@Import(DubboConfigConfigurationRegistrar.class)
public @interface EnableDubboConfig {
java 复制代码
@Import(DubboComponentScanRegistrar.class)
public @interface DubboComponentScan {

我们点进DubboConfigConfigurationRegistrar这个类

java 复制代码
public class DubboConfigConfigurationRegistrar implements ImportBeanDefinitionRegistrar {
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        // initialize dubbo beans
        DubboSpringInitializer.initialize(registry);
    }
}
java 复制代码
public class DubboSpringInitializer {
    public static void initialize(BeanDefinitionRegistry registry) {
        //.......省略非核心代码
        // init dubbo context
        initContext(context, registry, beanFactory);
    }
    private static void initContext(DubboSpringInitContext context, BeanDefinitionRegistry registry,
                                ConfigurableListableBeanFactory beanFactory) {
        // register common beans
        DubboBeanUtils.registerCommonBeans(registry);
    }
    static void registerCommonBeans(BeanDefinitionRegistry registry) {
        ......
        //这里主要是处理@DubboReference和@Reference注解
        registerInfrastructureBean(registry, ReferenceAnnotationBeanPostProcessor.BEAN_NAME,
            ReferenceAnnotationBeanPostProcessor.class);
        // register ApplicationListeners 注册监听器,这里很重要
        registerInfrastructureBean(registry, DubboDeployApplicationListener.class.getName(), DubboDeployApplicationListener.class);
        registerInfrastructureBean(registry, DubboConfigApplicationListener.class.getName(), DubboConfigApplicationListener.class);
        // Dubbo config initializer 加载配置
        registerInfrastructureBean(registry, DubboConfigBeanInitializer.BEAN_NAME, DubboConfigBeanInitializer.class);
        ......
      }

这里我们可以看到@EnableDubboConfig注解的作用是加载dubbo的配置,注册两个监听器(很重要,先记下)DubboDeployApplicationListener、DubboConfigApplicationListener,进行服务提供方的初始化工作。我们接着看下一个注解

java 复制代码
public class DubboComponentScanRegistrar implements ImportBeanDefinitionRegistrar {
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        // initialize dubbo beans
        DubboSpringInitializer.initialize(registry);
        Set<String> packagesToScan = getPackagesToScan(importingClassMetadata);
        registerServiceAnnotationPostProcessor(packagesToScan, registry);
    }
    private void registerServiceAnnotationPostProcessor(Set<String> packagesToScan, BeanDefinitionRegistry registry) {

        BeanDefinitionBuilder builder = rootBeanDefinition(ServiceAnnotationPostProcessor.class);
        builder.addConstructorArgValue(packagesToScan);
        builder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
        AbstractBeanDefinition beanDefinition = builder.getBeanDefinition();
        BeanDefinitionReaderUtils.registerWithGeneratedName(beanDefinition, registry);
}

这里我们可以看到创建了一个ServiceAnnotationPostProcessor的BeanDefinition,它是spring中的后置处理器,这里为了直观,我们直接开启debug断点 可以看到服务提供者的实现类被扫描到后组装成了BeanDefinition,其实@DubboComponentScan注解的核心职责就是将服务提供者的实现类扫描后交给spring的容器进行管理,这里就牵扯到spring的启动流程及bean的生命周期相关知识了,还记得前面注册的监听器吗,这里我们直接找到这个DubboConfigApplicationListener监听器代码 可以看到,在spring刷新过程中的registerListeners()方法中,前面注册的监听器会接受到事件进行处理

java 复制代码
@Override
public void prepare() {
    applicationDeployer.initialize();
    this.initialize();
}
@Override
public void initialize() {
    if (initialized) {
        return;
    }
    // Ensure that the initialization is completed when concurrent calls
    synchronized (startLock) {
        if (initialized) {
            return;
        }
        onInitialize();
        // register shutdown hook
        registerShutdownHook();
        startConfigCenter();
        loadApplicationConfigs();
        initModuleDeployers();
        initMetricsReporter();
        initMetricsService();
        // @since 2.7.8
        startMetadataCenter();
        initialized = true;
    }
}
@Override
public void initialize() throws IllegalStateException {
    if (initialized) {
        return;
    }
    // Ensure that the initialization is completed when concurrent calls
    synchronized (this) {
        if (initialized) {
            return;
        }
        onInitialize();
        loadConfigs();
        // read ModuleConfig
        ModuleConfig moduleConfig = moduleModel.getConfigManager().getModule().orElseThrow(() -> new IllegalStateException("Default module config is not initialized"));
        exportAsync = Boolean.TRUE.equals(moduleConfig.getExportAsync());
        referAsync = Boolean.TRUE.equals(moduleConfig.getReferAsync());
        // start in background
        background = moduleConfig.getBackground();
        if (background == null) {
            // compatible with old usages
            background = isExportBackground() || isReferBackground();
        }
        initialized = true;
    }
}

这两步initialize所做的时间方法比较简单,看方法就能知道各个子方法的职责,就不过多赘述了。 前面说到有两个很重要的监听器,我们还有DubboDeployApplicationListener监听器没讲到,我们进入这个监听器。

服务暴露

java 复制代码
@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");
    // start module 服务暴露
    Future future = deployer.start();
    ......
}
java 复制代码
@Override
public Future start() throws IllegalStateException {
    // initialize,maybe deadlock applicationDeployer lock & moduleDeployer lock
    applicationDeployer.initialize();
    return startSync();
}

private synchronized Future startSync() throws IllegalStateException {
        ......
        // export services 服务暴露
        exportServices();
private void exportServices() {
    for (ServiceConfigBase sc : configManager.getServices()) {
        exportServiceInternal(sc);
    }
}
private void exportServiceInternal(ServiceConfigBase sc) {
    ......
    } else {
        if (!sc.isExported()) {
            //服务暴露
            sc.export();
            exportedServices.add(sc);
        }
    }
}
private void doExportUrls() {
   ......
   //获取注册的URL
    List<URL> registryURLs = ConfigValidationUtils.loadRegistries(this, true);
    MetricsEventBus.post(RegistryEvent.toRsEvent(module.getApplicationModel(), getUniqueServiceName(), protocols.size() * registryURLs.size()),
        () -> {
            for (ProtocolConfig protocolConfig : protocols) {
                String pathKey = URL.buildKey(getContextPath(protocolConfig)
                    .map(p -> p + "/" + path)
                    .orElse(path), group, version);
                // stub service will use generated service name
                if (!serverService) {
                    // In case user specified path, register service one more time to map it to path.
                    repository.registerService(pathKey, interfaceClass);
                }
                //进行服务暴露
                doExportUrlsFor1Protocol(protocolConfig, registryURLs);
            }
            return null;
        }
    );
    providerModel.setServiceUrls(urls);

这里有本地暴露和远程暴露,我们直接看远程暴露的代码 中间的wapper调用链比较长,我们知道dubbo底层通讯框架使用的netty,我们直接断点到NettyServer的代码处 最后的最后的,就是暴露给我们的注册中心zk了。

总结

最后用一段话总结一下,服务暴露的流程始于Spring IOC容器完成刷新后,获取需要导出的接口信息,组装成本地暴露和远程暴露的URL,通过javassist 动态封装引用(实现类),将其统一暴露为Invoker,隐藏底层实现细节,封装成exporter,以方便调用方使用,接着启动Netty服务器,最后将URL注册的配置中心。

这篇dubbo服务暴露的源码解析文章,深入挖掘了服务启动暴露的过程,虽然非核心环节已经略过,但因为涉及的内容丰富,导致了整篇文章的篇幅较长。对dubbo源码感兴趣的朋友,我强烈建议您自己对核心流程进行debug一遍,深入了解dubbo服务的内部运作机制。

相关推荐
松☆11 分钟前
Dart 核心语法精讲:从空安全到流程控制(3)
android·java·开发语言
编码者卢布25 分钟前
【Azure Storage Account】Azure Table Storage 跨区批量迁移方案
后端·python·flask
编码者卢布32 分钟前
【App Service】Java应用上传文件功能部署在App Service Windows上报错 413 Payload Too Large
java·开发语言·windows
q行1 小时前
Spring概述(含单例设计模式和工厂设计模式)
java·spring
好好研究2 小时前
SpringBoot扩展SpringMVC
java·spring boot·spring·servlet·filter·listener
毕设源码-郭学长2 小时前
【开题答辩全过程】以 高校项目团队管理网站为例,包含答辩的问题和答案
java
玄〤2 小时前
Java 大数据量输入输出优化方案详解:从 Scanner 到手写快读(含漫画解析)
java·开发语言·笔记·算法
tb_first2 小时前
SSM速通3
java·jvm·spring boot·mybatis
独自破碎E2 小时前
总持续时间可被 60 整除的歌曲
java·开发语言