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服务的内部运作机制。

相关推荐
凌波粒几秒前
SpringMVC基础教程(3)--SSM框架整合
java·sql·spring·intellij-idea·mybatis
舒一笑5 分钟前
PandaCoder:我的个人开发者工具进化之路
后端·程序员·intellij idea
2021_fc16 分钟前
分布式应用可观测全链路追踪技术
java
数据的世界0117 分钟前
JAVA和C#的语法对比
java·windows·c#
渡我白衣22 分钟前
深入理解 OverlayFS:用分层的方式重新组织 Linux 文件系统
android·java·linux·运维·服务器·开发语言·人工智能
IT_陈寒23 分钟前
Vue 3.4 正式发布:5个不可错过的性能优化与Composition API新特性
前端·人工智能·后端
百***926538 分钟前
java进阶1——JVM
java·开发语言·jvm
虫师c1 小时前
字节码(Bytecode)深度解析:跨平台运行的魔法基石
java·jvm·java虚拟机·跨平台·字节码
q***72191 小时前
Spring Boot环境配置
java·spring boot·后端
洛_尘1 小时前
数据结构--7:排序(Sort)
java·数据结构