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

相关推荐
煸橙干儿~~4 分钟前
分析JS Crash(进程崩溃)
java·前端·javascript
2401_854391085 分钟前
Spring Boot大学生就业招聘系统的开发与部署
java·spring boot·后端
Amor风信子6 分钟前
华为OD机试真题---跳房子II
java·数据结构·算法
虽千万人 吾往矣26 分钟前
golang gorm
开发语言·数据库·后端·tcp/ip·golang
杨荧32 分钟前
【JAVA开源】基于Vue和SpringBoot的洗衣店订单管理系统
java·开发语言·vue.js·spring boot·spring cloud·开源
陈逸轩*^_^*1 小时前
Java 网络编程基础
java·网络·计算机网络
这孩子叫逆1 小时前
Spring Boot项目的创建与使用
java·spring boot·后端
星星法术嗲人1 小时前
【Java】—— 集合框架:Collections工具类的使用
java·开发语言
一丝晨光1 小时前
C++、Ruby和JavaScript
java·开发语言·javascript·c++·python·c·ruby
天上掉下来个程小白1 小时前
Stream流的中间方法
java·开发语言·windows