12 全局视野来看Dubbo3的服务启动生命周期

    前面我们介绍了Dubbo启动器DubboBootstrap类型对象的创建,又介绍了为DubboBootstrap启动器初始化各种配置信息,这一个博客就开始到了分析启动方法的位置了,Dubbo启动器借助Deployer发布器来启动和发布服务,发布器的启动过程包含了启动配置中心,加载配置,启动元数据中心,启动服务等操作都是比较重要又比较复杂的过程,这里我们先来看下启动过程的生命周期来为后面的内容做好铺垫。

    12.2 启动器启动方法的调用逻辑start()

    这里我们就直接来看DubboBootstrap的start()方法:

    1. //调用重载的方法进行启动参数代表是否等待启动结束
    2. this.start(true);
    3. return this;
    4. }

    我们再来看重载的start方法:

    1. public DubboBootstrap start(boolean wait) {
    2. //这个发布器是在ApplicationModel对象创建之后初始化的时候进行初始化的具体类型为DefaultApplicationDeployer
    3. Future future = applicationDeployer.start();
    4. if (wait) {
    5. try {
    6. //等待异步启动的结果
    7. future.get();
    8. } catch (Exception e) {
    9. //启动失败则抛出一个异常
    10. throw new IllegalStateException("await dubbo application start finish failure", e);
    11. }
    12. }
    13. return this;
    14. }

    发布器是帮助我们发布服务和引用服务的,在Dubbo3中不论是服务提供者还是服务消费者如果想要启动服务都需要走这个启动方法的逻辑,所以务必重视

    我们直接来看DefaultApplicationDeployer的start()代码:

    这个启动方法逻辑不多 主要三个方法我们重点来看:

    • onStarting() 这个是启动之前的状态切换
    • initialize() 应用的初始化逻辑 比如配置中心,元数据中心的初始化
    • doStart() 启动模块比如启动我们的服务提供和服务引用的)

    12.4 应用程序发布器对应用级别的初始化逻辑

    这个我们先来看DefaultApplicationDeployer的初始化方法initialize():

    1. @Override
    2. public void initialize() {
    3. //状态判断 如果已经初始化过了就直接返回
    4. if (initialized) {
    5. return;
    6. }
    7. // Ensure that the initialization is completed when concurrent calls
    8. //启动锁,确保在并发调用时完成初始化
    9. synchronized (startLock) {
    10. //双重校验锁 如果已经初始化过了就直接返回
    11. if (initialized) {
    12. return;
    13. }
    14. // register shutdown hook
    15. //注册关闭钩子,这个逻辑基本每个中间件应用都必须要要做的事情了,正常关闭应用回收资源,一般没这个逻辑情况下容易出现一些异常,让我们开发人员很疑惑,而这个逻辑往往并不好处理的干净。
    16. registerShutdownHook();
    17. //启动配置中心,感觉Dubbo3耦合了这个玩意
    18. startConfigCenter();
    19. //加载配置,一般配置信息当前机器的来源:环境变量,JVM启动参数,配置文字
    20. loadApplicationConfigs();
    21. //初始化模块发布器 (发布服务提供和服务引用使用)
    22. initModuleDeployers();
    23. //启动元数据中心
    24. startMetadataCenter();
    25. //初始化完成
    26. initialized = true;
    27. if (logger.isInfoEnabled()) {
    28. logger.info(getIdentifier() + " has been initialized!");
    29. }
    30. }
    31. }

    这个是个生命周期整体概览的方法,将具体逻辑拆分到各个子方法中,是代码重构的一种策略,上面注释也很清楚了就不细说了,上面每个方法在后面会有单独的博客来分析。

    我们回过头来详细看DefaultApplicationDeployer的doStart()代码:

    1. private void doStart() {
    2. // 启动模块
    3. startModules();

    DefaultApplicationDeployer的 startModules()方法

    这个模块的启动其实就是用来启动服务的 先启动内部服务,再启动外部服务
    内部服务有个元数据服务Dubbo3中每个服务都可以对外提供服务的元数据信息,来简化服务配置,不论是内部服务还是外部服务调用的代码逻辑都是模块发布器ModuleDeployer的start()方法,接下来我们详细看下模块发布器的生命周期函数。

    12.6 模块发布器发布服务的过程

    前面我们说到了所有的服务都是经过模块发布器,ModuleDeployer的start()方法来启动的,那我们接下来就来看看这个模块发布器的启动方法。

    1. @Override
    2. public synchronized Future start() throws IllegalStateException {
    3. //模块发布器已经停止或者启动失败则直接抛出异常返回
    4. if (isStopping() || isStopped() || isFailed()) {
    5. throw new IllegalStateException(getIdentifier() + " is stopping or stopped, can not start again");
    6. }
    7. try {
    8. //启动重或者已经启动了则直接返回一个Future对象
    9. if (isStarting() || isStarted()) {
    10. return startFuture;
    11. }
    12. //切换模块启动状态为STARTING
    13. onModuleStarting();
    14. // initialize
    15. //如果应用未初始化则初始化(非正常逻辑)
    16. applicationDeployer.initialize();
    17. //模块发布器进行初始化
    18. initialize();
    19. // export services
    20. //暴露服务
    21. exportServices();
    22. // prepare application instance
    23. // exclude internal module to avoid wait itself
    24. if (moduleModel != moduleModel.getApplicationModel().getInternalModule()) {
    25. applicationDeployer.prepareInternalModule();
    26. }
    27. // refer services
    28. //引用服务
    29. referServices();
    30. // if no async export/refer services, just set started
    31. //非异步启动则直接切换状态为STARTED
    32. if (asyncExportingFutures.isEmpty() && asyncReferringFutures.isEmpty()) {
    33. onModuleStarted();
    34. } else {
    35. //如果是异步的则等待服务发布和服务引用异步回调
    36. frameworkExecutorRepository.getSharedExecutor().submit(() -> {
    37. waitExportFinish();
    38. // wait for refer finish
    39. waitReferFinish();
    40. } catch (Throwable e) {
    41. logger.warn("wait for export/refer services occurred an exception", e);
    42. } finally {
    43. //异步回调完成 所有服务都启动了,再切换状态
    44. onModuleStarted();
    45. }
    46. });
    47. }
    48. } catch (Throwable e) {
    49. onModuleFailed(getIdentifier() + " start failed: " + e, e);
    50. throw e;
    51. }
    52. return startFuture;
    53. }

    好了整体的服务启动生命周期就如上代码,后续我们再详细来看每个细节。

    前面主要说了应用和模块的发布器的启动和初始化,下面简单了解下它们的关系,如下所示

    可以发布器主要包含

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

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

    另外我们再来看下发布过程的状态枚举DeployState如下:

    1. public enum DeployState {
    2. /**
    3. * Unknown state
    4. */
    5. UNKNOWN,
    6. /**
    7. * Pending, wait for start
    8. */
    9. PENDING,
    10. /**
    11. * Starting
    12. */
    13. STARTING,
    14. /**
    15. * Started
    16. */
    17. STARTED,
    18. /**
    19. * Stopping
    20. */
    21. STOPPING,
    22. /**
    23. * Stopped
    24. */
    25. STOPPED,
    26. /**
    27. * Failed
    28. */
    29. }

    Dubbo这一块后续可以优化以下,这里的状态切换全部耦合在一起了,可以考虑使用状态机将状态与行为解耦。

    技术咨询支持,可以扫描微信公众号进行回复咨询
    在这里插入图片描述