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 启动涉及到应用及模块发布器的启动和初始化,下面先简单了解它们之间的关系
发布器主要包含:
- 应用发布器: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;
}
}
该启动方法逻辑并不多,主要有三个方法来重点查看:
- onStarting:启动方法之前的状态切换
- initialize:应用的初始化逻辑,比如配置中心、元数据中心的初始化
- 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/