目录
- 18-Dubbo3元数据服务MetadataService的导出
- 18.1 简介
- 18.2 MetadataService的导出过程
- 18.3 模块启动成功时候的逻辑 onModuleStarted();
- 18.4 准备发布元数据信息和应用实例信息
- 18.4.1 导出元数据服务方法exportMetadataService
- 18.4.2 可配置元数据服务的导出ConfigurableMetadataServiceExporter的export
- 18.4.3 元数据服务配置对象的创建
18-Dubbo3元数据服务MetadataService的导出
18.1 简介
MetadataService
此服务用于公开Dubbo进程内的元数据信息。典型用途包括:
- 使用者查询提供者的元数据信息,以列出接口和每个接口的配置
- 控制台(dubbo admin)查询特定进程的元数据,或聚合所有进程的数据。在Dubbo2.x的时候,所有的服务数据都是以接口的形式注册在注册中心.
Dubbo3将部分数据抽象为元数据的形式来将数据存放在元数据中心,然后元数据由服务提供者提供给消费者而不是再由注册中心进行推送,如下图所示:
引入 MetadataService 元数据服务服务的好处
• 由中心化推送转向点对点拉取(Consumer - Proroder)
• 易于扩展更多的参数
• 更多的数据量
• 对外暴露更多的治理数据
18.2 MetadataService的导出过程
了解元数据的到处过程,这个就要继续前面博客往后的代码了前面博客说了一个服务发布之后的服务信息的双注册数据,这里继续看下导出服务之后的代码:
先来简单回顾下模块发布的启动生命周期方法:
DefaultModuleDeployer类型的start方法:
@Override
public synchronized Future start() throws IllegalStateException {
...
try {
...
onModuleStarting();
// initialize
applicationDeployer.initialize();
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
if (asyncExportingFutures.isEmpty() && asyncReferringFutures.isEmpty()) {
onModuleStarted();
} else {
....
return startFuture;
}
前面的博客我们已经说了服务提供者导出服务的方法如下:
// export services
exportServices();
在导出服务之后如果代码中配置了引用服务的代码将会执行引用服务的功能,调用代码如下:
referServices();
不过我们样例代码并没有介绍引用服务的功能,这里先不说,等服务提供者完全启动成功之后我们再来看消费者的逻辑。
接下来我们要看的是模块启动成功之后的方法 onModuleStarted();,在这个方法中会去发布服务元数据信息。
18.3 模块启动成功时候的逻辑 onModuleStarted();
这里我们直接先看代码再来分析下逻辑:
DefaultModuleDeployer类型的onModuleStarted方法如下所示:
private void onModuleStarted() {
try {
//状态判断是否为启动中如果是则将状态设置为STARTED
if (isStarting()) {
//先修改状态
setStarted();
logger.info(getIdentifier() + " has started.");
//状态修改成功之后开始通知应用程序发布器模块发布器启动成功了
applicationDeployer.notifyModuleChanged(moduleModel, DeployState.STARTED);
}
} finally {
// complete module start future after application state changed
completeStartFuture(true);
}
}
应用程序发布器处理启动成功的逻辑:
DefaultApplicationDeployer类型的notifyModuleChanged方法:
@Override
public void notifyModuleChanged(ModuleModel moduleModel, DeployState state) {
//根据所有模块的状态来判断应用发布器的状态
checkState(moduleModel, state);
// notify module state changed or module changed
//通知所有模块状态更新
synchronized (stateLock) {
stateLock.notifyAll();
}
}
应用发布器模型DefaultApplicationDeployer检查状态方法checkState代码如下:
@Override
public void checkState(ModuleModel moduleModel, DeployState moduleState) {
//存在写操作 先加个锁
synchronized (stateLock) {
//非内部模块,并且模块的状态是发布成功了
if (!moduleModel.isInternal() && moduleState == DeployState.STARTED) {
prepareApplicationInstance();
}
//应用下所有模块状态进行汇总计算
DeployState newState = calculateState();
switch (newState) {
case STARTED:
onStarted();
break;
case STARTING:
onStarting();
break;
case STOPPING:
onStopping();
break;
case STOPPED:
onStopped();
break;
case FAILED:
Throwable error = null;
ModuleModel errorModule = null;
for (ModuleModel module : applicationModel.getModuleModels()) {
ModuleDeployer deployer = module.getDeployer();
if (deployer.isFailed() && deployer.getError() != null) {
error = deployer.getError();
errorModule = module;
break;
}
}
onFailed(getIdentifier() + " found failed module: " + errorModule.getDesc(), error);
break;
case PENDING:
// cannot change to pending from other state
// setPending();
break;
}
}
}
18.4 准备发布元数据信息和应用实例信息
前面有个代码调用比较重要:
prepareApplicationInstance()
DefaultApplicationDeployer类型的prepareApplicationInstance方法如下所示
@Override
public void prepareApplicationInstance() {
//已经注册过应用实例数据了 直接返回 (下面CAS逻辑判断了)
if (hasPreparedApplicationInstance.get()) {
return;
}
//注册开关控制默认为true
//通过将registerConsumer默认设置为“false”来关闭纯使用者进程实例的注册。
if (isRegisterConsumerInstance()) {
exportMetadataService();
if (hasPreparedApplicationInstance.compareAndSet(false, true)) {
// register the local ServiceInstance if required
registerServiceInstance();
}
}
}
18.4.1 导出元数据服务方法exportMetadataService
这里我们就先直接来贴一下代码:
DefaultApplicationDeployer类型的exportMetadataService方法如下所示:
private void exportMetadataService() {
if (!isStarting()) {
return;
}
//这里监听器我们主要关注的类型是ExporterDeployListener类型
for (DeployListener<ApplicationModel> listener : listeners) {
try {
if (listener instanceof ApplicationDeployListener) {
// 回调监听器的模块启动成功方法
((ApplicationDeployListener) listener).onModuleStarted(applicationModel);
}
} catch (Throwable e) {
logger.error(getIdentifier() + " an exception occurred when handle starting event", e);
}
}
}
前面我们主要关注ExporterDeployListener类型的监听器的回调方法,这里我贴一下代码:
ExporterDeployListener类型的onModuleStarted方法如下:
@Override
public synchronized void onModuleStarted(ApplicationModel applicationModel) {
// start metadata service exporter
//MetadataServiceDelegation类型为实现提供远程RPC服务以方便元数据信息的查询功能的类型。
MetadataServiceDelegation metadataService = applicationModel.getBeanFactory().getOrRegisterBean(MetadataServiceDelegation.class);
if (metadataServiceExporter == null) {
metadataServiceExporter = new ConfigurableMetadataServiceExporter(applicationModel, metadataService);
// fixme, let's disable local metadata service export at this moment
//默认我们是没有配置这个元数据类型的这里元数据类型默认为local 条件是不是remote则开始导出,在前面的博客<<Dubbo启动器DubboBootstrap添加应用程序的配置信息ApplicationConfig>> 中有提到这个配置下面我再说下
if (!REMOTE_METADATA_STORAGE_TYPE.equals(getMetadataType(applicationModel))) {
metadataServiceExporter.export();
}
}
}
在前面的博客<<Dubbo启动器DubboBootstrap添加应用程序的配置信息ApplicationConfig>> 中有提到这个配置下面我再说下
metadata-type
metadata 传递方式,是以 Provider 视角而言的,Consumer 侧配置无效,可选值有:
- remote - Provider 把 metadata 放到远端注册中心,Consumer 从注册中心获取。
- local - Provider把 metadata 放在本地,Consumer 从 Provider 处直接获取。
可以看到默认的local配置元数据信息的获取是由消费者从提供者拉的,那提供者怎么拉取对应服务的元数据信息那就要要用到这个博客说到的MetadataService服务,传递方式为remote的方式其实就要依赖注册中心了相对来说增加了注册中心的压力。
18.4.2 可配置元数据服务的导出ConfigurableMetadataServiceExporter的export
前面了解了导出服务的调用链路,这里详细看下ConfigurableMetadataServiceExporter的export过程源码如下所示:
public synchronized ConfigurableMetadataServiceExporter export() {
//元数据服务配置已经存在或者已经导出或者不可导出情况下是无需导出的
if (serviceConfig == null || !isExported()) {
//创建服务配置
this.serviceConfig = buildServiceConfig();
// export
//导出服务 ,导出服务的具体过程这里就不再说了可以看上一个博客,这个导出服务的过程会绑定端口
serviceConfig.export();
metadataService.setMetadataURL(serviceConfig.getExportedUrls().get(0));
if (logger.isInfoEnabled()) {
logger.info("The MetadataService exports urls : " + serviceConfig.getExportedUrls());
}
} else {
if (logger.isWarnEnabled()) {
logger.warn("The MetadataService has been exported : " + serviceConfig.getExportedUrls());
}
}
return this;
}
18.4.3 元数据服务配置对象的创建
前面我们看到了构建元数据服务对象的代码调用ServiceConfig,接下来我们详细看下构建源码如下所示:
ConfigurableMetadataServiceExporter类型的buildServiceConfig构建元数据服务配置对象方法如下:
private ServiceConfig<MetadataService> buildServiceConfig() {
//1 获取当前的应用配置 然后初始化应用配置
ApplicationConfig applicationConfig = getApplicationConfig();
//创建服务配置对象
ServiceConfig<MetadataService> serviceConfig = new ServiceConfig<>();
//设置域模型
serviceConfig.setScopeModel(applicationModel.getInternalModule());
serviceConfig.setApplication(applicationConfig);
//2 创建注册中心配置对象 然后并初始化
RegistryConfig registryConfig = new RegistryConfig("N/A");
registryConfig.setId("internal-metadata-registry");
//3 创建服务配置对象,并初始化
serviceConfig.setRegistry(registryConfig);
serviceConfig.setRegister(false);
//4 生成协议配置 ,这里会配置一下元数据使用的服务端口号默认使用其他服务的端口20880
serviceConfig.setProtocol(generateMetadataProtocol());
serviceConfig.setInterface(MetadataService.class);
serviceConfig.setDelay(0);
//这里也是需要注意的地方服务引用的类型为MetadataServiceDelegation
serviceConfig.setRef(metadataService);
serviceConfig.setGroup(applicationConfig.getName());
serviceConfig.setVersion(MetadataService.VERSION);
//5 生成方法配置 这里目前提供的服务方法为getAndListenInstanceMetadata方法 后续可以看下这个方法的视线
serviceConfig.setMethods(generateMethodConfig());
serviceConfig.setConnections(1); // separate connection
serviceConfig.setExecutes(100); // max tasks running at the same time
return serviceConfig;
}
- 注册中心的配置register设置为了false 则为不向注册中心注册具体的服务配置信息
- 对每个提供者的最大连接数connections为1
- 服务提供者每服务每方法最大可并行执行请求数executes为100