9-DubboBootstrap 服务启动的生命周期

作者: vnjohn / 发表于 2025-05-26 / 专栏: Dubbo 3.3

Dubbo3, 源码

9.1 启动方法简介

在说启动器之前先将关注点拉回到第一章《1-DubboV3.3 从一个服务提供者的 Demo 说起》的 main 方法,贴下 DubboBootstrap 相关的代码:

private static void startWithBootstrap() {
  ServiceConfig<DemoServiceImpl> service = new ServiceConfig<>();
  service.setInterface(DemoService.class);
  service.setRef(new DemoServiceImpl());
  DubboBootstrap bootstrap = DubboBootstrap.getInstance();
  bootstrap
    .application(new ApplicationConfig("dubbo-demo-api-provider"))
    .registry(new RegistryConfig(REGISTRY_URL))
    .protocol(new ProtocolConfig(CommonConstants.DUBBO, -1))
    .service(service)
    // 这一个篇章主要说这里:
    .start()
    .await();
}

前面介绍了 Dubbo 启动器 DubboBootstrap 类型对象的创建,又介绍了 DubboBootstrap 启动器初始化的各种配置信息,这一篇博客就开始分析启动方法 DubboBootstrap#start 处理逻辑。

Dubbo 启动器借助 Deployer 发布器来启动和发布服务,发布器的启动过程包含了启动配置中心、加载配置、启动元数据中心、启动服务等操作,都是比较重要且复杂的过程,下面我们来具体分析启动过程的生命周期为后面的内容做铺垫。

9.2 DubboBoostrap#start 方法调用逻辑

public DubboBootstrap start() {
  // 调用重载方法进行启动,参数代表是否等待启动结束
  this.start(true);
  return this;
}

调用重载方法 start,携带一个参数是否同步等待启动结束

public DubboBootstrap start(boolean wait) {
  // 该发布器是在 ApplicationModel 对象创建之后
  // 进行初始化的具体类型 DefaultApplicationDeployer
  Future future = applicationDeployer.start();
  if (wait) {
    try {
      // 等待启动完成
      future.get();
    } catch (Exception e) {
      // 启动失败抛出异常
      throw new IllegalStateException("await dubbo application start finish failure", e);
    }
  }
  return this;
}

9.3 Deployer 简介

DubboBootstrap 启动涉及到应用及模块发布器的启动和初始化,下面先简单了解它们之间的关系

image-20250528003926289

发布器主要包含:

  • 应用发布器:ApplicationDeployer 用于初始化并启动应用程序实例
  • 模块发布器:ModuleDeployer 用于模块(服务)导出/引用服务

两种发布器都有各自的接口,它们都继承了抽象的发布器 AbstractDeployer,其中封装了一些公共的操作,比如:状态切换、状态查询的逻辑

另外再来看下发布过程涉及到状态枚举类 DeployState 有那些状态,如下:

public enum DeployState {
    /**
     * Unknown state
     */
    UNKNOWN,

    /**
     * Pending, wait for start
     */
    PENDING,

    /**
     * Starting
     */
    STARTING,

    /**
     * Started
     */
    STARTED,

    /**
     * Completion
     */
    COMPLETION,

    /**
     * Stopping
     */
    STOPPING,

    /**
     * Stopped
     */
    STOPPED,

    /**
     * Failed
     */
    FAILED
}

9.4 DefaultApplicationDeployer#start 方法

发布器是帮助我们发布服务、引用服务的,在 Dubbo3 中无论是服务提供者还是服务消费者,若想要启动服务都需要走这个启动方法的逻辑,所以比较重要。

接着直接来看 DefaultApplicationDeployer#start 方法的源码实现:

public Future start() {
  // 启动锁,防止重复启动
  synchronized (startLock) {
    // 发布器,状态已设置停止或失败就直接抛出异常
    if (isStopping() || isStopped() || isFailed()) {
      throw new IllegalStateException(getIdentifier() + " is stopping or stopped, can not start again");
    }
    try {
      // maybe call start again after add new module, check if any new module
      // 可能在添加新模块后再次调用 start,检查是否有任何新模块
      // 遍历当前应用程序下的所有模块,若某个模块是 PENDING 状态则 hasPendingModule 值为 true
      boolean hasPendingModule = hasPendingModule();
      // 发布器状态正在启动中
      if (isStarting()) {
        // currently, is starting, maybe both start by module and application
        // if it has new modules, start them
        // 存在挂起的模块
        if (hasPendingModule) {
          // 启动模型
          startModules();
        }
        // if it is starting, reuse previous startFuture
        // 模块异步启动中
        return startFuture;
      }
      // if is started and no new module, just return
      // 若已启动且没有新模块,则直接返回
      if ((isStarted() || isCompletion()) && !hasPendingModule) {
        return CompletableFuture.completedFuture(false);
      }
      // pending -> starting : first start app
      // started -> starting : re-start app
      // 启动状态切换,启动状态切换为启动中 (pending、started 状态无需切换)
      onStarting();
      // 核心初始化逻辑,主要做应用级别的启动,比如:配置中心、元数据中心
      initialize();
      // 启动模块(服务提供、服务引用是在这个模块级别下)
      doStart();
    } catch (Throwable e) {
      onFailed(getIdentifier() + " start failure", e);
      throw e;
    }
    return startFuture;
  }
}

该启动方法逻辑并不多,主要有三个方法来重点查看:

  1. onStarting:启动方法之前的状态切换
  2. initialize:应用的初始化逻辑,比如配置中心、元数据中心的初始化
  3. doStart:启动模块,比如启动服务的提供者及引用者

9.5 DefaultApplicationDeployer#initialize 方法

应用程序发布器的初始化方法,源码如下:

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();
    // 启动配置中心,感觉Dubbo3耦合了这个玩意
    startConfigCenter();
    // 加载配置,一般配置信息当前机器的来源:环境变量、JVM 启动参数、配置文字
    loadApplicationConfigs();
    // 初始化模块发布器 (发布服务提供和服务引用使用)
    initModuleDeployers();
    // 初始化指标报告器,负责数据上报,为 initMetricsService() 提供数据采集能力
    initMetricsReporter();
    // 管理监控服务,暴露指标接口,依赖 initMetricsReporter() 的数据,依赖 initObservationRegistry() 的观测数据
    initMetricsService();
    // @since 3.2.3
    // 初始化观测注册表,管理 Tracing 上下文,为 initMetricsService() 提供观测数据支持
    initObservationRegistry();
    // @since 2.7.8
    // 启动元数据中心
    startMetadataCenter();
    // 初始化完成
    initialized = true;
    if (logger.isInfoEnabled()) {
      logger.info(getIdentifier() + " has been initialized!");
    }
  }
}

以上是整个生命周期概览的方法,将具体的逻辑拆分到各个子方法中,是代码重构的一种策略,上面的每个方法会在后续有单独的博客进行分析

9.6 DefaultApplicationDeployer#start 方法

应用程序下的模块启动方法,针对于服务的发布及引用

private void doStart() {
  // 启动模块
  startModules();
}

private void startModules() {
  // ensure init and start internal module first
  // 确保初始化并首先启动内部模块,Dubbo3 中将模块分为内部和外部
  // 内部是核心代码已经提供的一些服务,比如元数据服务,外部是我们自己写的服务
  prepareInternalModule();
  // filter and start pending modules, ignore new module during starting, throw exception of module start
  // 启动所有的模块(启动所有的服务)
  for (ModuleModel moduleModel : applicationModel.getModuleModels()) {
    // 这个状态默认就是 PENDING
    if (moduleModel.getDeployer().isPending()) {
      // 模块启动器,发布服务
      moduleModel.getDeployer().start();
    }
  }
}

模块的启动其实就是用来启动吧服务的,先启动内部服务,再启动外部服务

内部服务有个元数据服务,在 Dubbo3 中每个服务都可以对外提供服务的元数据信息,来简化服务配置,无论是内部服务还是外部服务都是调用模块发布器 ModuleDeployer#start 方法,接下来来看下模块发布器的生命周期相关函数

9.7 ModuleDeployer#start 方法

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 {
    // 启动中或者已经启动了则直接返回一个 Future 对象
    if (isStarting() || isStarted() || isCompletion()) {
      return startFuture;
    }
    // 切换模块启动状态为 STARTING
    onModuleStarting();
    // 模块发布器进行初始化
    initialize();

    // export services 暴露服务
    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
    // 非异步启动则直接切换状态为 STARTED
    if (asyncExportingFutures.isEmpty() && asyncReferringFutures.isEmpty()) {
      // publish module started event
      onModuleStarted();

      // register services to registry
      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;
}

以上源码就是服务启动的整个生命周期

9.8 DubboBootstrap 生命周期流程图

在这里插入图片描述

参考文献

https://www.ktyhub.com/zh/chapter_dubbo/12-start-life-cycle/

vnjohn

作者

vnjohn

后端研发工程师。喜欢探索新技术,空闲时也折腾 AIGC 等效率工具。 可以在 GitHub 关注我了解更多,也可以加我微信(vnjohn) 与我交流。