超详细Dubbo服务导出源码解读

1. Dubbo服务导出概述

前面已经解读过Dubbo SPI相关的源码见:一篇短文就能搞定Dubbo SPI 源码及示例。本文主要研究一下 Dubbo 导出服务的过程。Dubbo 服务导出过程始于 Spring 容器发布刷新事件,Dubbo 在接收到事件后,会立即执行服务导出逻辑。整个逻辑大致可分为三个部分,第一部分是前置工作,主要用于检查参数,组装 URL。第二部分是导出服务,包含导出服务到本地 (JVM),和导出服务到远程两个过程。第三部分是向注册中心注册服务,用于服务发现。本文将基于dubbo-2.7.7源码对这三个部分代码进行详细的分析。

2. Dubbo服务导出源码解读
2.1 服务导出入口

在2.7.5之前服务导出的入口方法是 ServiceBean 的 onApplicationEvent。onApplicationEvent 是一个事件响应方法,该方法会在收到 Spring 上下文刷新事件后执行服务导出操作。但是在2.7.5开始新增了DubboBootstrapApplicationListener,该类继承了OneTimeExecutionApplicationContextEventListener抽象类,OneTimeExecutionApplicationContextEventListener又实现了ApplicationListener接口onApplicationEvent方法,同之前版本一样,该方法会在收到 Spring 上下文刷新事件后执行。两个类的关键源码如下:

abstract class OneTimeExecutionApplicationContextEventListener implements ApplicationListener, ApplicationContextAware {
   

    private ApplicationContext applicationContext;

    public final void onApplicationEvent(ApplicationEvent event) {
   
    	//判断事件源是持有的ApplicationContext并且是应用上下文事件
        if (isOriginalEventSource(event) && event instanceof ApplicationContextEvent) {
   
            onApplicationContextEvent((ApplicationContextEvent) event);
        }
    }

    /**
     * The subclass overrides this method to handle {@link ApplicationContextEvent}
     *
     * @param event {@link ApplicationContextEvent}
     */
    protected abstract void onApplicationContextEvent(ApplicationContextEvent event);
 //省略部分源码
}

public class DubboBootstrapApplicationListener extends OneTimeExecutionApplicationContextEventListener
        implements Ordered {
   

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

    private final DubboBootstrap dubboBootstrap;

    public DubboBootstrapApplicationListener() {
   
        this.dubboBootstrap = DubboBootstrap.getInstance();
    }
    
    @Override
    public void onApplicationContextEvent(ApplicationContextEvent event) {
   
        if (event instanceof ContextRefreshedEvent) {
   //是上下文刷新事件则调用DubboBootstrap的start方法
            onContextRefreshedEvent((ContextRefreshedEvent) event);
        } else if (event instanceof ContextClosedEvent) {
   //如果是上下文关闭事件则调用DubboBootstrap的stop方法
            onContextClosedEvent((ContextClosedEvent) event);
        }
    }

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

    private void onContextClosedEvent(ContextClosedEvent event) {
   
        dubboBootstrap.stop();
    }

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

2.2 DubboBootstrap#start

根据上面的源码可知,在DubboBootstrapApplicationListener 的构造函数中会先去获取DubboBootstrap 的实例,在监听到ContextRefreshedEvent事件时触发DubboBootstrap 的start方法,因此接着我们看看DubboBootstrap 的实例化及开始方法的源码。

DubboBootstrap 实例化

public class DubboBootstrap extends GenericEventListener {
   


    private static DubboBootstrap instance;

    private final ConfigManager configManager;

    private final Environment environment;

    /**
     * 加锁构造单例
     * See {@link ApplicationModel} and {@link ExtensionLoader} for why DubboBootstrap is designed to be singleton.
     */
    public static synchronized DubboBootstrap getInstance() {
   
        if (instance == null) {
   
            instance = new DubboBootstrap();
        }
        return instance;
    }
    //私有化构造函数,保证只能被自己实例化
    private DubboBootstrap() {
   
    	//通过SPI方式获取环境配置和环境管理实例,继承关系如下图
        configManager = ApplicationModel.getConfigManager();
        environment = ApplicationModel.getEnvironment();
        //注册shutdown事件,回调DubboBootstrap的destroy方法,销毁所有导出及引用的服务等
        DubboShutdownHook.getDubboShutdownHook().register();
        ShutdownHookCallbacks.INSTANCE.addCallback(new ShutdownHookCallback() {
   
            @Override
            public void callback() throws Throwable {
   
                DubboBootstrap.this.destroy();
            }
        });
    }

在这里插入图片描述

org.apache.dubbo.config.bootstrap.DubboBootstrap.start()

  /**
     * Start the bootstrap
     */
    public DubboBootstrap start() {
   
    	//原子操作,保证只启动一次
        if (started.compareAndSet(false, true)) {
   
        	//初始化操作
            initialize();
            if (logger.isInfoEnabled()) {
   
                logger.info(NAME + " is starting...");
            }
            // 导出dubbo服务(最终调用ServiceConfig的export方法)
            exportServices();

            // 不仅仅是注册服务提供者或者已经导出元数据
            if (!isOnlyRegisterProvider() || hasExportedServices()) {
   
                // 导出元数据服务(最终构建了ServiceConfig实例,然后调用export方法)
                exportMetadataService();
                // 如果有则需要注册本地服务实例,通过SPI方式获取服务发现注册中心,然后调用他们的注册方法(默认自适应拓展实现是zookeeper)
                registerServiceInstance();
            }
            //执行服务引入
            referServices();

            if (logger.isInfoEnabled()) {
   
                logger.info(NAME + " has started.");
            }
        }
        return this;
    }
2.3 DubboBootstrap#start内部

根据上面start方法源码可以看到,里面一次执行了服务初始化以服务导出和引入的方法,接下来我们追踪下个方法的具体实现。

org.apache.dubbo.config.bootstrap.DubboBootstrap.initialize()

/**
     * Initialize
     */
    private void initialize() {
   
    	// 原子操作确保只初始化一次
        if (!initialized.compareAndSet(false, true)) {
   
            return;
        }
        //初始化Dubbo组件的生命周期,这里主要是对环境配置初始化
        ApplicationModel.iniFrameworkExts();
        //开始构建配置中心
        startConfigCenter();
        //如果是zookeeper作为注册中心且没有指定配置中心时,使用注册中心做配置中心
        useRegistryAsConfigCenterIfNecessary();
        //加载协议ID到协议配置中并加载注册id到注册中心配置
        loadRemoteConfigs();
        //全局配置校验(应用、元数据、提供者、消费者、监控等)
        checkGlobalConfigs();
        //初始化元数据服务
        initMetadataService();
        //初始化事件监听器(将当前实例添加到事件监听器中)
        initEventListener();

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

org.apache.dubbo.config.bootstrap.DubboBootstrap.exportServices()

 private void exportServices() {
   
        configManager.getServices().forEach(sc -> {
   
            //设置ServiceConfig的启动器为当前实例
            ServiceConfig serviceConfig = (ServiceConfig) sc;
            serviceConfig.setBootstrap(this);
            
            if (exportAsync) {
   
            	//异步导出,将导出任务提交到线程池异步完成
                ExecutorService executor = executorRepository.getServiceExporterExecutor();
                Future<?> future = executor.submit(() -> {
   
                    sc.export();
                });
                asyncExportingFutures.add(future);
            } else {
   
            	//同步导出 则直接调用ServiceConfig的导出方法
                sc.export();
  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值