阿里背书的Dubbo 3.0正式版在2021年3月份正式发布,Dubbo3.0核心功能包括:新一代RPC协议、应用级服务发现、新版路由规则等。随着云原生时代的到来,Dubbo 3.0 为了能够更好地适配云原生,3.0将原来的接口级服务发现演进为应用级服务发现。
Dubbo 3.0应用级服务发现主要有以下优势:
目前关于Dubbo服务端暴露流程的技术文章是基于Dubbo接口级服务发现机制来解读的。在Dubbo3.0的应用级服务发现机制下,服务端暴露流程与之前有很大的变化,而且我们团队也在使用Dubbo3+Nacos2做微服务改造,已积累了一定的实战经验和对Dubbo3.0应用级服务发现有了更深入的了解,本文希望可以通过对Dubbo 3.0源码理解来解析服务端暴露全流程。
Dubbo3.0主要解决的问题:
假设应用app-user部署了3个实例(user1, user2, user3),并且对外提供了 3 个接口(UserApi, RoleApi, MenuApi)。在接口级和应用级服务发现机制下,注册到注册中心的数据是截然不同的。如下图所示:
"UserApi":
[
{"instanceId":"instance1","ip":"×××.×××.×××.×××","port":"30002","weight":1,"ephemeral":true,"metadata":{"side":"provider","service.name":"ServiceBean:/×××.×××.×××.×××.UserApi:1.0.0","methods":"getById","release":"2.7.13","deprecated":false,"dubbo":"2.0.2","pid":1,"interface":"×××.×××.×××.×××.SysUserApi","version":"1.0.0","generic":false,"revision":"1.0.0","path":"×××.×××.×××.×××.SysUserApi","protocol":"dubbo","application":"annotation-user","dynamic":true,"category":"providers","anyhost":true,"timestamp":1662031063698}},
{"instanceId":"instance2","ip":"×××.×××.×××.×××","port":"30002","weight":1,"ephemeral":true,"metadata":{"side":"provider","service.name":"ServiceBean:/×××.×××.×××.×××.UserApi:1.0.0","methods":"getById","release":"2.7.13","deprecated":false,"dubbo":"2.0.2","pid":1,"interface":"×××.×××.×××.×××.SysUserApi","version":"1.0.0","generic":false,"revision":"1.0.0","path":"×××.×××.×××.×××.SysUserApi","protocol":"dubbo","application":"annotation-user","dynamic":true,"category":"providers","anyhost":true,"timestamp":1662031063698}},
{"instanceId":"instance2","ip":"×××.×××.×××.×××","port":"30002","weight":1,"ephemeral":true,"metadata":{"side":"provider","service.name":"ServiceBean:/×××.×××.×××.×××.UserApi:1.0.0","methods":"getById","release":"2.7.13","deprecated":false,"dubbo":"2.0.2","pid":1,"interface":"×××.×××.×××.×××.SysUserApi","version":"1.0.0","generic":false,"revision":"1.0.0","path":"×××.×××.×××.×××.SysUserApi","protocol":"dubbo","application":"annotation-user","dynamic":true,"category":"providers","anyhost":true,"timestamp":1662031063698}}
],
"RoleApi":
[
{"instanceId":"instance1","ip":"×××.×××.×××.×××","port":"30002","weight":1,"ephemeral":true,"metadata":{"side":"provider","service.name":"ServiceBean:/×××.×××.×××.×××.RoleApi:1.0.0","methods":"selectById","release":"2.7.13","deprecated":false,"dubbo":"2.0.2","pid":1,"interface":"×××.×××.×××.×××.SysUserApi","version":"1.0.0","generic":false,"revision":"1.0.0","path":"×××.×××.×××.×××.SysUserApi","protocol":"dubbo","application":"annotation-user","dynamic":true,"category":"providers","anyhost":true,"timestamp":1662031063698}},
{"instanceId":"instance2","ip":"×××.×××.×××.×××","port":"30002","weight":1,"ephemeral":true,"metadata":{"side":"provider","service.name":"ServiceBean:/×××.×××.×××.×××.RoleApi:1.0.0","methods":"selectById","release":"2.7.13","deprecated":false,"dubbo":"2.0.2","pid":1,"interface":"×××.×××.×××.×××.SysUserApi","version":"1.0.0","generic":false,"revision":"1.0.0","path":"×××.×××.×××.×××.SysUserApi","protocol":"dubbo","application":"annotation-user","dynamic":true,"category":"providers","anyhost":true,"timestamp":1662031063698}},
{"instanceId":"instance2","ip":"×××.×××.×××.×××","port":"30002","weight":1,"ephemeral":true,"metadata":{"side":"provider","service.name":"ServiceBean:/×××.×××.×××.×××.RoleApi:1.0.0","methods":"selectById","release":"2.7.13","deprecated":false,"dubbo":"2.0.2","pid":1,"interface":"×××.×××.×××.×××.SysUserApi","version":"1.0.0","generic":false,"revision":"1.0.0","path":"×××.×××.×××.×××.SysUserApi","protocol":"dubbo","application":"annotation-user","dynamic":true,"category":"providers","anyhost":true,"timestamp":1662031063698}}
],
"MenuApi":
[
{"instanceId":"instance1","ip":"×××.×××.×××.×××","port":"30002","weight":1,"ephemeral":true,"metadata":{"side":"provider","service.name":"ServiceBean:/×××.×××.×××.×××.MenuApi:1.0.0","methods":"selectByProjectId","release":"2.7.13","deprecated":false,"dubbo":"2.0.2","pid":1,"interface":"×××.×××.×××.×××.SysUserApi","version":"1.0.0","generic":false,"revision":"1.0.0","path":"×××.×××.×××.×××.SysUserApi","protocol":"dubbo","application":"annotation-user","dynamic":true,"category":"providers","anyhost":true,"timestamp":1662031063698}},
{"instanceId":"instance2","ip":"×××.×××.×××.×××","port":"30002","weight":1,"ephemeral":true,"metadata":{"side":"provider","service.name":"ServiceBean:/×××.×××.×××.×××.MenuApi:1.0.0","methods":"selectByProjectId","release":"2.7.13","deprecated":false,"dubbo":"2.0.2","pid":1,"interface":"×××.×××.×××.×××.SysUserApi","version":"1.0.0","generic":false,"revision":"1.0.0","path":"×××.×××.×××.×××.SysUserApi","protocol":"dubbo","application":"annotation-user","dynamic":true,"category":"providers","anyhost":true,"timestamp":1662031063698}},
{"instanceId":"instance2","ip":"×××.×××.×××.×××","port":"30002","weight":1,"ephemeral":true,"metadata":{"side":"provider","service.name":"ServiceBean:/×××.×××.×××.×××.MenuApi:1.0.0","methods":"selectByProjectId","release":"2.7.13","deprecated":false,"dubbo":"2.0.2","pid":1,"interface":"×××.×××.×××.×××.SysUserApi","version":"1.0.0","generic":false,"revision":"1.0.0","path":"×××.×××.×××.×××.SysUserApi","protocol":"dubbo","application":"annotation-user","dynamic":true,"category":"providers","anyhost":true,"timestamp":1662031063698}}
]
"annotation-user":{
[
{"instanceId":"instance1","ip":"×××.×××.×××.×××","port":"30002","weight":1,"ephemeral":true,"metadata":{"dubbo.metadata-service.url-params":{"connections":"1","version":"1.0.0","dubbo":"2.0.2","release":"3.0.10","side":"provider","port":"20880","protocol":"dubbo"},"dubbo.endpoints":[{"port":30002,"protocol":"tri"}],"dubbo.metadata.revision":"31e476788487b90a0ca94b53bccb656c","dubbo.metadata.storage-type":"local","timestamp":1662343919223}},
{"instanceId":"instance2","ip":"×××.×××.×××.×××","port":"30002","weight":1,"ephemeral":true,"metadata":{"dubbo.metadata-service.url-params":{"connections":"1","version":"1.0.0","dubbo":"2.0.2","release":"3.0.10","side":"provider","port":"20880","protocol":"dubbo"},"dubbo.endpoints":[{"port":30002,"protocol":"tri"}],"dubbo.metadata.revision":"31e476788487b90a0ca94b53bccb656c","dubbo.metadata.storage-type":"local","timestamp":1662343919223}},
{"instanceId":"instance2","ip":"×××.×××.×××.×××","port":"30002","weight":1,"ephemeral":true,"metadata":{"dubbo.metadata-service.url-params":{"connections":"1","version":"1.0.0","dubbo":"2.0.2","release":"3.0.10","side":"provider","port":"20880","protocol":"dubbo"},"dubbo.endpoints":[{"port":30002,"protocol":"tri"}],"dubbo.metadata.revision":"31e476788487b90a0ca94b53bccb656c","dubbo.metadata.storage-type":"local","timestamp":1662343919223}}
]
}
通过对比我们可以发现,采用应用级服务发现机制确实使注册中心中的数据量减少了很多,那些原有的接口级的数据存储在元数据中心中。
注:接下来的代码为dubbo3.0.10版本为例(注册中心、配置中心选用Nacos2.1.0版本)
引入应用级服务发现机制以后,Dubbo 3.0 服务端暴露全流程和之前有很大的区别。
暴露服务端全流程从DubboDeployApplicationListener#onApplicationEvent中开始,整体链路如下:
具体如下:
// org.apache.dubbo.config.spring.context.DubboDeployApplicationListener
@Override
public void onApplicationEvent(ApplicationContextEvent event) {
if (nullSafeEquals(applicationContext, event.getSource())) {
if (event instanceof ContextRefreshedEvent) { // 上下文刷新事件
onContextRefreshedEvent((ContextRefreshedEvent) event);
} else if (event instanceof ContextClosedEvent) {
onContextClosedEvent((ContextClosedEvent) event);
}
}
}
//org/apache/dubbo/config/ServiceConfig
protected synchronized void doExport() {
......
doExportUrls(); // 协议的服务初始化,
exported(); // 应用级服务注册、元数据发布
}
我们可以看到,整个的注册流程还是挺复杂的,一共可以分为四个部分:
下面会分别从这四个部分对服务暴露全流程进行详细讲解。
injvm协议的服务是暴露在本地的,主要原因是在一个应用上往往既有 Service(暴露服务)又有 Reference(服务引用)的情况存在,并且 Reference 引用的服务就是在该应用上暴露的 Service。为了支持这种使用场景,Dubbo 提供了injvm协议,将Service暴露在本地,Reference就可以不需要走网络直接在本地调用Service,其本质主要逻辑就是封装injvm的URL,缓存在本地
其核心代码:ServiceConfig#exportLocal开始初始化injvm协议的本地服务
//org/apache/dubbo/config/ServiceConfig
private void exportUrl(URL url, List registryURLs) {
String scope = url.getParameter(SCOPE_KEY);
if (!SCOPE_NONE.equalsIgnoreCase(scope)) {
if (!SCOPE_REMOTE.equalsIgnoreCase(scope)) {
//injvm协议的服务初始化
exportLocal(url);
}
if (!SCOPE_LOCAL.equalsIgnoreCase(scope)) {
// service-discover-registry协议的服务初始化
url = exportRemote(url, registryURLs);
if (!isGeneric(generic) && !getScopeModel().isInternal()) {
// 接口元数据信息发布
MetadataUtils.publishServiceDefinition(url, providerModel.getServiceModel(), getApplicationModel());
}
}
}
this.urls.add(url);
}
//org/apache/dubbo/config/ServiceConfig
private void exportLocal(URL url) {
URL local = URLBuilder.from(url)
.setProtocol(LOCAL_PROTOCOL) // 设置协议为:injvm
.setHost(LOCALHOST_VALUE) // host:127.0.0.1
.setPort(0)
.build();
local = local.setScopeModel(getScopeModel())
.setServiceModel(providerModel);
doExportUrl(local, false); //暴露本地服务
......
}
初始化到本地的核心作用:
注册 service-discovery-registry 协议的核心目的是为了注册与服务相关的元数据,默认情况下元数据通过 InMemoryWritableMetadataService 将数据存储在本地内存和本地文件。
核心代码在ServiceConfig#exportRemote 中,具体如下:
//org/apache/dubbo/config/ServiceConfig
private URL exportRemote(URL url, List registryURLs) {
if (CollectionUtils.isNotEmpty(registryURLs)) {
// 如果是多个注册中心,通过循环对每个注册中心进行注册
for (URL registryURL : registryURLs) {
// 一系列封装url逻辑,如:
// 将service-name-mapping=true加入到url中
//if protocol is only injvm ,not register
// 创建monitorURL,将其加入url中(如果没有配置monitor,则为null)
// For providers, this is used to enable custom proxy to generate invoker
......
//核心逻辑:注册service-discovery-registry协议复用服务暴露流程
doExportUrl(registryURL.putAttribute(EXPORT_KEY, url), true);
}
......
} else {
......
}
return url;
}
//org/apache/dubbo/config/ServiceConfig
private void doExportUrl(URL url, boolean withMetaData) {
//为实现类创建Invoker,类型:JavassistProxyFactory
Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, url);
if (withMetaData) {
// 将invoker包装成DelegateProviderMetaDataInvoker
invoker = new DelegateProviderMetaDataInvoker(invoker, this);
}
//protocolSPI:协议自定义适配器,根据协议动态加载实现类
//此时exporter为ProtocolSerializationWrapper对象
Exporter<?> exporter = protocolSPI.export(invoker);
// 将导出服务添加到exporters中
exporters.add(exporter);
}
核心代码在 ProtocolListenerWrapper#export 中,具体如下:
// org.apache.dubbo.rpc.protocol.ProtocolListenerWrapper
@Override
public Exporter export(Invoker invoker) throws RpcException {
// 此时protocol为RegistryProtocol类型
if (UrlUtils.isRegistry(invoker.getUrl())) {
return protocol.export(invoker);
}
......
}
核心代码在 RegistryProtocol#export 中,具体如下:
// org.apache.dubbo.registry.integration.RegistryProtocol
@Override
public Exporter export(final Invoker originInvoker) throws RpcException {
URL registryUrl = getRegistryUrl(originInvoker);
URL providerUrl = getProviderUrl(originInvoker);
......
//创建ProviderConfigurationListener对象,缓存到本地registeredBeanInfos list中
Map overrideListeners = getProviderConfigurationListener(providerUrl).getOverrideListeners();
// 将overrideSubscribeListener(默认为空)放入到overrideListeners map中,
overrideListeners.put(registryUrl, overrideSubscribeListener);
// 从配置中心拉取数据,覆盖providerUrl属性
providerUrl = overrideUrlWithConfig(providerUrl, overrideSubscribeListener);
//暴露Triple协议的服务
final ExporterChangeableWrapper exporter = doLocalExport(originInvoker, providerUrl);
// registryUrl中包含service-discovery-registry协议
// 通过该协议创建ServiceDiscoveryRegistry对象
// 最后包装成ListenerRegistryWrapper对象
final Registry registry = getRegistry(registryUrl);
final URL registeredProviderUrl = getUrlToRegistry(providerUrl, registryUrl);
boolean register = providerUrl.getParameter(REGISTER_KEY, true) && registryUrl.getParameter(REGISTER_KEY, true);
if (register) {
// 注册service-discovery-registry协议
register(registry, registeredProviderUrl);
}
.......
// 触发RegistryServiceListener的onRegister事件
notifyExport(exporter);
.......
}
步骤:
核心代码在 RegistryProtocol#doLocalExport 中,具体如下:
// org.apache.dubbo.registry.integration.RegistryProtocol
private ExporterChangeableWrapper doLocalExport(final Invoker originInvoker, URL providerUrl) {
String key = getCacheKey(originInvoker);
return (ExporterChangeableWrapper) bounds.computeIfAbsent(key, s -> {
Invoker<?> invokerDelegate = new InvokerDelegate<>(originInvoker, providerUrl);
// 最终会调用TripleProtocol#export(Invoker invoker)方法
return new ExporterChangeableWrapper<>((Exporter) protocol.export(invokerDelegate), originInvoker);
});
}
// org.apache.dubbo.rpc.protocol.tri.TripleProtocol
public Exporter export(Invoker invoker) throws RpcException {
......
// 将tri协议缓存到exporterMap中(如:key=ai.advance.guardian.annotation.api.SysUserApi:1.0.0:30002)
// exporter类型为TripleProtocol
exporterMap.put(key, exporter);
// 将invoker放入到invokers Set中,invoker类型为:FilterChainBuilder.CallbackRegistrationInvoker
invokers.add(invoker);
// 将invoker放入到pathResolver中(如:key=ai.advance.guardian.annotation.api.SysUserApi:1.0.0)
// pathResolver类型为TriplePathResolver,初始化TripleProtocol时通过SPI创建pathResolver
pathResolver.add(url.getServiceKey(), invoker);
// key=ai.advance.guardian.annotation.api.SysUserApi
pathResolver.add(url.getServiceModel().getServiceModel().getInterfaceName(), invoker);
......
url.getOrDefaultApplicationModel().getExtensionLoader(ExecutorRepository.class)
.getDefaultExtension()// 通过SPI获取ExecutorRepository
.createExecutorIfAbsent(url); // 给接口创建执行线程池,然后放入到executors map中
PortUnificationExchanger.bind(url);
optimizeSerialization(url);
return exporter;
}
注:
核心代码在 ServiceDiscoveryRegistry#register和MetadataInfo#addService 中,具体如下:
// org.apache.dubbo.registry.client.ServiceDiscoveryRegistry
public final void register(URL url) {
if (!shouldRegister(url)) { // Should Not Register
return;
}
// 注册service-discovery-registry协议
doRegister(url);
}
// org.apache.dubbo.metadata.MetadataInfo
public synchronized void addService(URL url) {
// fixme, pass in application mode context during initialization of MetadataInfo.
if (this.loader == null) {
this.loader = url.getOrDefaultApplicationModel().getExtensionLoader(MetadataParamsFilter.class);
}
List filters = loader.getActivateExtension(url, "params-filter");
// generate service level metadata
ServiceInfo serviceInfo = new ServiceInfo(url, filters);
// 将serviceInfo放入到services map中
this.services.put(serviceInfo.getMatchKey(), serviceInfo);
// extract common instance level params
extractInstanceParams(url, filters);
if (exportedServiceURLs == null) {
exportedServiceURLs = new ConcurrentSkipListMap<>();
}
// 将url放入到exportedServiceURLs map中
addURL(exportedServiceURLs, url);
updated = true;
}
注:
ServiceDiscoveryRegistry#doRegister最终会调用到MetadataInfo#addService
缓存service-discovery-registry协议URL到exportedServiceURLs map中
缓存serviceInfo到services map中
核心代码在 RegistryProtocol#notifyExport 中,具体如下:
// org.apache.dubbo.registry.integration.RegistryProtocol
private void notifyExport(ExporterChangeableWrapper exporter) {
ScopeModel scopeModel = exporter.getRegisterUrl().getScopeModel();
List listeners = ScopeModelUtil.getExtensionLoader(RegistryProtocolListener.class, scopeModel)
.getActivateExtension(exporter.getOriginInvoker().getUrl(), REGISTRY_PROTOCOL_LISTENER_KEY);
if (CollectionUtils.isNotEmpty(listeners)) {
for (RegistryProtocolListener listener : listeners) {
// 发布RegistryProtocolListener的onExport事件
listener.onExport(this, exporter);
}
}
}
注:
由于暴露 Triple 协议服务的流程和暴露 Injvm 协议服务的流程是一致的,所以不再赘述。
服务注册/发布包含3部分:
元数据分两种类型:
此处将接口元数据发布到配置中心
因为在前面完成了injvm、service-discovery-registry、tri等等协议服务初始化工作之后基本完成了MetaData、ServiceBean的初始化工作,所以接下来的口元数据发布到配置中心比较简单
核心代码从MetadataUtils#publishServiceDefinition开始,具体如下:
// org.apache.dubbo.registry.client.metadata.MetadataUtils
public static void publishServiceDefinition(URL url, ServiceDescriptor serviceDescriptor, ApplicationModel applicationModel) {
......
String side = url.getSide();
if (PROVIDER_SIDE.equalsIgnoreCase(side)) {
String serviceKey = url.getServiceKey();
FullServiceDefinition serviceDefinition = serviceDescriptor.getFullServiceDefinition(serviceKey);
if (StringUtils.isNotEmpty(serviceKey) && serviceDefinition != null) {
serviceDefinition.setParameters(url.getParameters());
for (Map.Entry entry : getMetadataReports(applicationModel).entrySet()) {
MetadataReport metadataReport = entry.getValue();
if (!metadataReport.shouldReportDefinition()) {
logger.info("Report of service definition is disabled for " + entry.getKey());
continue;
}
metadataReport.storeProviderMetadata(
new MetadataIdentifier(
url.getServiceInterface(),
url.getVersion() == null ? "" : url.getVersion(),
url.getGroup() == null ? "" : url.getGroup(),
PROVIDER_SIDE,
applicationModel.getApplicationName())
, serviceDefinition);
}
}
}
......
}
//org/apache/dubbo/metadata/report/support/AbstractMetadataReport
private void storeProviderMetadataTask(MetadataIdentifier providerMetadataIdentifier, ServiceDefinition serviceDefinition) {
try {
......
allMetadataReports.put(providerMetadataIdentifier, serviceDefinition);
failedReports.remove(providerMetadataIdentifier);
String data = JsonUtils.getJson().toJson(serviceDefinition);
//
doStoreProviderMetadata(providerMetadataIdentifier, data);
saveProperties(providerMetadataIdentifier, data, true, !syncReport);
} catch (Exception e) {
......
}
}
步骤:
因为在前面已经完成了MetaData、ServiceBean的初始化工作的初始化工作,所以到这里,应用映射接口数据发布过程其实比较简单
数据格式如下:
{
"dataId":"×××.×××.×××.UserApi",
"group":"mapping",
"content":"application-name"
}
从ServiceConfig#exported开启"应用映射接口数据"发布,以下是一些核心逻辑
// 调用来源为ServiceConfig.doExport()
// org.apache.dubbo.config.spring.ServiceBean
protected void exported() {
// 调用ServiceConfig.exported方法
super.exported();
// Publish ServiceBeanExportedEvent
publishExportEvent();
}
// org/apache/dubbo/config/ServiceConfig
protected void exported() {
exported = true;
List exportedURLs = this.getExportedUrls();
exportedURLs.forEach(url -> {
if (url.getParameters().containsKey(SERVICE_NAME_MAPPING_KEY)) {
// 通过SPI方式动态获取MetadataServiceNameMapping对象
ServiceNameMapping serviceNameMapping = ServiceNameMapping.getDefaultExtension(getScopeModel());
try {
// 封装应用映射接口数据
boolean succeeded = serviceNameMapping.map(url);
......
} catch (Exception e) {
logger.error("Failed register interface application mapping for service " + url.getServiceKey(), e);
}
}
});
......
}
// org.apache.dubbo.metadata.store.nacos.NacosMetadataReport
@Override
public boolean registerServiceAppMapping(String key, String group, String content, Object ticket) {
try {
if (!(ticket instanceof String)) {
throw new IllegalArgumentException("nacos publishConfigCas requires string type ticket");
}
//往nacos配置中心注册应用映射数据
return configService.publishConfigCas(key, group, content, (String) ticket);
} catch (NacosException e) {
logger.warn("nacos publishConfigCas failed.", e);
return false;
}
}
// org.apache.dubbo.metadata.store.nacos.NacosConfigServiceWrapper
public boolean publishConfigCas(String dataId, String group, String content, String casMd5) throws NacosException {
//调用nacos接口往配置中心注册应用映射数据
return configService.publishConfigCas(handleInnerSymbol(dataId), handleInnerSymbol(group), content, casMd5);
}
注册应用级服务是Dubbo3.0服务暴露的核心流程,也就是之前提到的应用级服务发现机制。
相比接口元数据的发布和应用映射接口数据的发布,应用级服务注册放在了最后执行
应用级服务的注册从DefaultApplicationDeployer#prepareApplicationInstance开始,以下是一些核心逻辑:
// org.apache.dubbo.config.deploy.DefaultApplicationDeployer
public void prepareApplicationInstance() {
......
if (isRegisterConsumerInstance()) {
exportMetadataService();
if (hasPreparedApplicationInstance.compareAndSet(false, true)) {
// 最终会调用到AbstractServiceDiscovery#register()
registerServiceInstance();
}
}
}
// org.apache.dubbo.registry.clien.AbstractServiceDiscovery
public synchronized void register() throws RuntimeException {
// 创建服务实例对象
this.serviceInstance = createServiceInstance(this.metadataInfo);
if (!isValidInstance(this.serviceInstance)) {
return;
}
// 创建元数据版本号
boolean revisionUpdated = calOrUpdateInstanceRevision(this.serviceInstance);
if (revisionUpdated) {
......
doRegister(this.serviceInstance); // 应用注册
}
}
protected ServiceInstance createServiceInstance(MetadataInfo metadataInfo) {
DefaultServiceInstance instance = new DefaultServiceInstance(serviceName, applicationModel);
instance.setServiceMetadata(metadataInfo);
// 设置dubbo.metadata.storage-type:local/remote,local:存储在应用中,remote:存储在配置中心
setMetadataStorageType(instance, metadataType);
// 封装元数据,例如:timestamp=1662343919223
ServiceInstanceMetadataUtils.customizeInstance(instance, applicationModel);
return instance;
}
注:
从DefaultModuleDeployer#start()的onModuleStarted()调用开始应用服务注册
步骤:
// org.apache.dubbo.registry.nacos.NacosServiceDiscovery
@Override
public void doRegister(ServiceInstance serviceInstance) {
execute(namingService, service -> {
Instance instance = toInstance(serviceInstance);
service.registerInstance(instance.getServiceName(), Constants.DEFAULT_GROUP, instance);
});
}
public void registerInstance(String serviceName, String group, Instance instance) throws NacosException {
namingService.registerInstance(handleInnerSymbol(serviceName), group, instance);
}
步骤:
最终注册的dubbo应用信息
Dubbo应用注册和Spring Boot应用注册区别
通过对Dubbo 3.0服务端暴露全流程的解析发现,应用级服务发现机制的实现相对复杂,本文时序图以及代码梳理仅仅梳理核心又关键的逻辑点,想要深入理解Dubbo3.0应用级服务注册,还需要自己动手深入了解
最后由于本人能力有限,如果有错误的地方,请指出!
页面更新:2024-03-05
本站资料均由网友自行发布提供,仅用于学习交流。如有版权问题,请与我联系,QQ:4156828
© CopyRight 2020-2024 All Rights Reserved. Powered By 71396.com 闽ICP备11008920号-4
闽公网安备35020302034903号