Dubbo 3.2版本分析Provider启动时操作

Dubbo 3.2版本分析Provider启动时操作

  • 前言
  • 例子
  • 分析
    • onStarting 模块
    • doStart 模块
  • 小结

前言

上一篇文章,我们分析了 Dubbo 3.2 版本在 Provider 启动前的操作流程,这次我们具体分析具体它的启动过程,揭开它的神秘面纱。

例子

这里我们还是以provider启动的Demo为入口,进行分析。如下,为Dubbo服务暴漏出来的接口:

public interface DemoService {
	String sayHello(String name);
}

DemoServiceImpl为Dubbo provider

public class DemoServiceImpl implements DemoService {
    private static final Logger logger = LoggerFactory.getLogger(DemoServiceImpl.class);
    
    @Override
    public String sayHello(String name) {
        logger.info("Hello " + name + ", request from consumer: "
                + RpcContext.getServiceContext().getRemoteAddress());
        return "Hello " + name + ", response from provider: "
                + RpcContext.getServiceContext().getLocalAddress();
    }

}

Application为服务的启动类

public class Application {
    private static final String REGISTRY_URL = "zookeeper://sr-1-zk-cluster-1.gz.cvte.cn:2181";

    public static void main(String[] args) {
        startWithBootstrap();
    }
    
    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-api-provider"))
                .registry(new RegistryConfig(REGISTRY_URL))
                .protocol(new ProtocolConfig(CommonConstants.DUBBO, -1))
                .service(service)
                .start()
                .await();
    }
}

分析

上文分析过设置 application -> 设置 registry -> 设置 protocol -> 设置 serviceConfig 的流程了,这次我们进入 start 方法。

  1. 进入 start 方法后,最终来到 applicationDeployer.start() 这里。关于 applicationDeployer,它申明是ApplicationDeployer 类型,默认情况下,具体实现为 DefaultApplicationDeployer,关于它们的类结构如图1所示。
  2. Dubbo 3.0 把服务的发布流程进行了抽象,引入了 **Deployer **的概念,负责应用的的初始化和启动。除了 start 方法,其他方法如图2所示。
public DubboBootstrap start() {
        this.start(true);
        return this;
    }


    /**
     * Start dubbo application
     *
     * @param wait If true, wait for startup to complete, or else no waiting.
     * @return
     */
    public DubboBootstrap start(boolean wait) {
        Future future = applicationDeployer.start();
        if (wait) {
            try {
                future.get();
            } catch (Exception e) {
                throw new IllegalStateException("await dubbo application start finish failure", e);
            }
        }
        return this;
    }

图1
图2

  1. 接着,我们具体看看 DefaultApplicationDeployer对 start 方法的具体实现。
  2. 这里先进行服务发布状态的判断,通过 AbstractDeployer类里定义的一个共享的成员变量进行判断,默认值是 PENDING

private volatile DeployState state = PENDING;

  1. Dubbo 3.0 引入了领域模型的概念,那么在这里会先判断是否由新加入的模型而重新调用启动过程。判断方式见 hasPendingModule 方法。其中 applicationModel.getModuleModels() 会返回一个线程安全的读写队列。

private final List moduleModels = new CopyOnWriteArrayList<>();

  1. 这里我们是刚启动的状态,所以会 ModuleModel 默认状态即 **PENDING,hasPendingModule **的结果为ture。
  2. 进入 isStarting 方法,同样判断是判断 state,此时还是为 PENDING,所以结果为false。关于 startModules 方法的分析,我们放在后边讲。
  3. 再完后,我们可以看到三个被封装好的流程: onStarting()、 initialize()、doStart(),分别进行服务启动状态的设置、服务启动的初始化,启动各个领域模型。针对这个三个模快,我们在后文继续分析。
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
                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() && !hasPendingModule) {
                    return CompletableFuture.completedFuture(false);
                }

                // pending -> starting : first start app
                // started -> starting : re-start app
                onStarting();

                initialize();

                doStart();
            } catch (Throwable e) {
                onFailed(getIdentifier() + " start failure", e);
                throw e;
            }

            return startFuture;
        }
    }

private boolean hasPendingModule() {
    boolean found = false;
    for (ModuleModel moduleModel : applicationModel.getModuleModels()) {
        if (moduleModel.getDeployer().isPending()) {
            found = true;
            break;
        }
    }
    return found;
}

onStarting 模块

  1. 这里进来还是先进行状态的判断,如果既不是处于准备中状态或者启动过的状态,则return。
  2. 接着,通过 setStarting 方法修改我们的stateSTARTING 状态。
  3. 这里还会去启动监听者,listeners 为 AbstractDeployer类中的成员变量

protected List<DeployListener> listeners = new CopyOnWriteArrayList<>();

  1. 关于它的赋值有两个时机,这里可以回顾我们的例子中获取 DubboBootstrap 对象时的代码,这里在创建的过程中,会去加载两个监听器。

DubboBootstrap bootstrap = DubboBootstrap.getInstance();

  1. 如下面代码所示,一个是通过我们 SPI 机制引入的 ExporterDeployListener,另一个是用于监听服务启动过程的 DeployListenerAdapter,不过可能是考虑便于后边拓展,这里 onStarting 方法目前暂未做实现。
  2. 最后,构造一个异步线程池赋值给 startFuture 变量,该线程池可用于服务启动、配置加载等事件状态的执行。

private volatile CompletableFuture startFuture;

private void onStarting() {
        // pending -> starting
        // started -> starting
        if (!(isPending() || isStarted())) {
            return;
        }
        setStarting();
        startFuture = new CompletableFuture();
        if (logger.isInfoEnabled()) {
            logger.info(getIdentifier() + " is starting.");
        }
    }

protected void setStarting() {
        this.state = STARTING;
        for (DeployListener<E> listener : listeners) {
            try {
                listener.onStarting(scopeModel);
            } catch (Throwable e) {
                logger.error(
                        COMMON_MONITOR_EXCEPTION,
                        "",
                        "",
                        getIdentifier() + " an exception occurred when handle starting event",
                        e);
            }
        }
    }

// DeployListenerAdapter
applicationDeployer.addDeployListener(new DeployListenerAdapter<ApplicationModel>() {
            @Override
            public void onStarted(ApplicationModel scopeModel) {
                notifyStarted(applicationModel);
            }

            @Override
            public void onStopped(ApplicationModel scopeModel) {
                notifyStopped(applicationModel);
            }

            @Override
            public void onFailure(ApplicationModel scopeModel, Throwable cause) {
                notifyStopped(applicationModel);
            }
        }); 

// ExporterDeployListener
public class ExporterDeployListener implements ApplicationDeployListener, Prioritized {
    protected volatile ConfigurableMetadataServiceExporter metadataServiceExporter;

    @Override
    public void onInitialize(ApplicationModel scopeModel) {}

    @Override
    public void onStarting(ApplicationModel scopeModel) {

    }

// ....

}

doStart 模块

  1. 在执行 initialize 模块初始化后,最终是 doStart 模块,这里先启动 内部的模块模型 即 DefaultModuleDeployer 实例,这里同样会把 **DefaultModuleDeployer **的状态先设置为 STARTING。
  2. 注意,前面设置的是DefaultApplicationDeployer 的状态,跟这里的模块的状态还不一样。
  3. 然后会遍历所有的模块列表,可能有些业务场景自己拓展了模块吧,这里会判断它们是否出于准备状态,然后一起启动。
private void doStart() {
        startModules();
}

private void startModules() {
        // ensure init and start internal module first
        prepareInternalModule();

        // filter and start pending modules, ignore new module during starting, throw exception of module start
        for (ModuleModel moduleModel : applicationModel.getModuleModels()) {
            if (moduleModel.getDeployer().isPending()) {
                moduleModel.getDeployer().start();
            }
        }
    }

小结

本文分析了 Dubbo 3.2 版本分析Provider启动时的流程,会先启动 ApplicationDeployer 类型实例,在执行 state( onStarting 方法) 更新流程后,进行初始化**( initialize 方法),最后然后是执行启动流程( do start 方法)**。在 do start 方法里才又先启动我们的内部模块,再遍历所有的模块列表,把所有处于准备状态的模块进行启动。最后,关于 initialize 方法由于涉及流程很长,为了保证文章可读性,这里笔者暂时先不展开,也算留个悬念,下一篇我们继续分析它。