专栏电商日志财经减肥爱情
投稿投诉
爱情常识
搭配分娩
减肥两性
孕期塑形
财经教案
论文美文
日志体育
养生学堂
电商科学
头戴业界
专栏星座
用品音乐

Dubbo3。0应用级服务发现源码分析

  背景
  阿里背书的Dubbo3。0正式版在2021年3月份正式发布,Dubbo3。0核心功能包括:新一代RPC协议、应用级服务发现、新版路由规则等。随着云原生时代的到来,Dubbo3。0为了能够更好地适配云原生,3。0将原来的接口级服务发现演进为应用级服务发现。
  Dubbo3。0应用级服务发现主要有以下优势:适配云原生微服务变革。云原生时代的基础设施能力不断向上释放,像Kubernetes等平台都集成了微服务概念抽象,Dubbo3。0的应用级服务发现是适配各种微服务体系的通用模型(来自Dubbo官网数据)提升性能与可伸缩性。支持超大规模集群的服务治理一直以来都是Dubbo的优势,通过引入应用级服务发现模型,从本质上解决了注册中心地址数据的存储与推送压力,相应的Consumer侧的地址计算压力也成数量级下降;集群规模也开始变得可预测、可评估(与RPC接口数量无关,只与实例部署规模相关)。能支持的集群实例规模以百万计的集群注册中心总体数据量下降超60,极限场景下下降超90(来自Dubbo官网数据)
  目前关于Dubbo服务端暴露流程的技术文章是基于Dubbo接口级服务发现机制来解读的。在Dubbo3。0的应用级服务发现机制下,服务端暴露流程与之前有很大的变化,而且我们团队也在使用Dubbo3Nacos2做微服务改造,已积累了一定的实战经验和对Dubbo3。0应用级服务发现有了更深入的了解,本文希望可以通过对Dubbo3。0源码理解来解析服务端暴露全流程。
  概念什么是应用级服务发现?
  Dubbo3。0主要解决的问题:对齐主流微服务模型,如:SpringCloud减少注册中心数据存储能力,降低了地址变更推送的压力应用级服务与接口级服务的区别
  假设应用appuser部署了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:annotationuser,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:annotationuser,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:annotationuser,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:annotationuser,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:annotationuser,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:annotationuser,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:annotationuser,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:annotationuser,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:annotationuser,dynamic:true,category:providers,anyhost:true,timestamp:1662031063698}}〕应用级服务发现机制下注册中心中的数据annotationuser:{〔{instanceId:instance1,ip:。。。,port:30002,weight:1,ephemeral:true,metadata:{dubbo。metadataservice。urlparams:{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。storagetype:local,timestamp:1662343919223}},{instanceId:instance2,ip:。。。,port:30002,weight:1,ephemeral:true,metadata:{dubbo。metadataservice。urlparams:{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。storagetype:local,timestamp:1662343919223}},{instanceId:instance2,ip:。。。,port:30002,weight:1,ephemeral:true,metadata:{dubbo。metadataservice。urlparams:{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。storagetype:local,timestamp:1662343919223}}〕}
  通过对比我们可以发现,采用应用级服务发现机制确实使注册中心中的数据量减少了很多,那些原有的接口级的数据存储在元数据中心中。协议的服务初始化
  注:接下来的代码为dubbo3。0。10版本为例(注册中心、配置中心选用Nacos2。1。0版本)
  引入应用级服务发现机制以后,Dubbo3。0服务端暴露全流程和之前有很大的区别。
  暴露服务端全流程从DubboDeployApplicationListeneronApplicationEvent中开始,整体链路如下:
  具体如下:org。apache。dubbo。config。spring。context。DubboDeployApplicationListenerOverridepublicvoidonApplicationEvent(ApplicationContextEventevent){if(nullSafeEquals(applicationContext,event。getSource())){if(eventinstanceofContextRefreshedEvent){上下文刷新事件onContextRefreshedEvent((ContextRefreshedEvent)event);}elseif(eventinstanceofContextClosedEvent){onContextClosedEvent((ContextClosedEvent)event);}}}orgapachedubboconfigServiceConfigprotectedsynchronizedvoiddoExport(){。。。。。。doExportUrls();协议的服务初始化,exported();应用级服务注册、元数据发布}
  协议服务初始化、元数据封装、服务注册发布的整体入口从监听Spring上下文刷新事件开始注:onApplicationEvent是一个Spring上下文刷新事件的通知类,DubboDeployApplicationListener类实现了ApplicationListener,通过一个事件监听器开始dubbo服务注册onContextRefreshedEvent最终会调用到ServiceConfigdoExportdoExportUrls:协议的服务初始化exported:应用级服务注册元数据发布
  我们可以看到,整个的注册流程还是挺复杂的,一共可以分为四个部分:injvm协议的服务初始化servicediscoveryregistry协议的服务初始化triple协议的服务初始化
  下面会分别从这四个部分对服务暴露全流程进行详细讲解。
  injvm协议的服务初始化
  injvm协议的服务是暴露在本地的,主要原因是在一个应用上往往既有Service(暴露服务)又有Reference(服务引用)的情况存在,并且Reference引用的服务就是在该应用上暴露的Service。为了支持这种使用场景,Dubbo提供了injvm协议,将Service暴露在本地,Reference就可以不需要走网络直接在本地调用Service,其本质主要逻辑就是封装injvm的URL,缓存在本地
  其核心代码:ServiceConfigexportLocal开始初始化injvm协议的本地服务orgapachedubboconfigServiceConfigprivatevoidexportUrl(URLurl,ListURLregistryURLs){Stringscopeurl。getParameter(SCOPEKEY);if(!SCOPENONE。equalsIgnoreCase(scope)){if(!SCOPEREMOTE。equalsIgnoreCase(scope)){injvm协议的服务初始化exportLocal(url);}if(!SCOPELOCAL。equalsIgnoreCase(scope)){servicediscoverregistry协议的服务初始化urlexportRemote(url,registryURLs);if(!isGeneric(generic)!getScopeModel()。isInternal()){接口元数据信息发布MetadataUtils。publishServiceDefinition(url,providerModel。getServiceModel(),getApplicationModel());}}}this。urls。add(url);}orgapachedubboconfigServiceConfigprivatevoidexportLocal(URLurl){URLlocalURLBuilder。from(url)。setProtocol(LOCALPROTOCOL)设置协议为:injvm。setHost(LOCALHOSTVALUE)host:127。0。0。1。setPort(0)。build();locallocal。setScopeModel(getScopeModel())。setServiceModel(providerModel);doExportUrl(local,false);暴露本地服务。。。。。。}
  初始化到本地的核心作用:创建InjvmProtocol对象将其放入到exporterMap中创建ListenerExporterWrapper对象,将InjvmProtocol对象将封装进去,然后ListenerExporterWrapper对象存入到exportersList中
  servicediscoveryregistry协议服务初始化
  注册servicediscoveryregistry协议的核心目的是为了注册与服务相关的元数据,默认情况下元数据通过InMemoryWritableMetadataService将数据存储在本地内存和本地文件。
  核心代码在ServiceConfigexportRemote中,具体如下:注册servicediscoveryregistry协议的入口orgapachedubboconfigServiceConfigprivateURLexportRemote(URLurl,ListURLregistryURLs){if(CollectionUtils。isNotEmpty(registryURLs)){如果是多个注册中心,通过循环对每个注册中心进行注册for(URLregistryURL:registryURLs){一系列封装url逻辑,如:将servicenamemappingtrue加入到url中ifprotocolisonlyinjvm,notregister创建monitorURL,将其加入url中(如果没有配置monitor,则为null)Forproviders,thisisusedtoenablecustomproxytogenerateinvoker。。。。。。核心逻辑:注册servicediscoveryregistry协议复用服务暴露流程doExportUrl(registryURL。putAttribute(EXPORTKEY,url),true);}。。。。。。}else{。。。。。。}returnurl;}
  将invoker包装成exporter,然后放入到exportersorgapachedubboconfigServiceConfigprivatevoiddoExportUrl(URLurl,booleanwithMetaData){为实现类创建Invoker,类型:JavassistProxyFactoryInvokerlt;?invokerproxyFactory。getInvoker(ref,(Class)interfaceClass,url);if(withMetaData){将invoker包装成DelegateProviderMetaDataInvokerinvokernewDelegateProviderMetaDataInvoker(invoker,this);}protocolSPI:协议自定义适配器,根据协议动态加载实现类此时exporter为ProtocolSerializationWrapper对象Exporterlt;?exporterprotocolSPI。export(invoker);将导出服务添加到exporters中exporters。add(exporter);}
  通过RegistryProtocol将Invoker转化成Exporter
  核心代码在ProtocolListenerWrapperexport中,具体如下:org。apache。dubbo。rpc。protocol。ProtocolListenerWrapperOverridepublicTExporterTexport(InvokerTinvoker)throwsRpcException{此时protocol为RegistryProtocol类型if(UrlUtils。isRegistry(invoker。getUrl())){returnprotocol。export(invoker);}。。。。。。}
  RegistryProtocol将Invoker转化成Exporter的核心流程
  核心代码在RegistryProtocolexport中,具体如下:org。apache。dubbo。registry。integration。RegistryProtocolOverridepublicTExporterTexport(finalInvokerToriginInvoker)throwsRpcException{URLregistryUrlgetRegistryUrl(originInvoker);URLproviderUrlgetProviderUrl(originInvoker);。。。。。。创建ProviderConfigurationListener对象,缓存到本地registeredBeanInfoslist中MapURL,NotifyListeneroverrideListenersgetProviderConfigurationListener(providerUrl)。getOverrideListeners();将overrideSubscribeListener(默认为空)放入到overrideListenersmap中,overrideListeners。put(registryUrl,overrideSubscribeListener);从配置中心拉取数据,覆盖providerUrl属性providerUrloverrideUrlWithConfig(providerUrl,overrideSubscribeListener);暴露Triple协议的服务finalExporterChangeableWrapperTexporterdoLocalExport(originInvoker,providerUrl);registryUrl中包含servicediscoveryregistry协议通过该协议创建ServiceDiscoveryRegistry对象最后包装成ListenerRegistryWrapper对象finalRegistryregistrygetRegistry(registryUrl);finalURLregisteredProviderUrlgetUrlToRegistry(providerUrl,registryUrl);booleanregisterproviderUrl。getParameter(REGISTERKEY,true)registryUrl。getParameter(REGISTERKEY,true);if(register){注册servicediscoveryregistry协议register(registry,registeredProviderUrl);}。。。。。。。触发RegistryServiceListener的onRegister事件notifyExport(exporter);。。。。。。。}
  步骤:getRegistryUrl:创建servicediscoveryregister协议URLgetProviderUrl:创建triple协议URLgetProviderConfigurationListener:1。初始化ProviderConfigurationListener类型监听器,将ProviderConfigurationListener对象缓存到registeredBeanInfoslist中。2。ProviderConfigurationListener从Nacos配置中心拉取ProviderConfiguration相关的配置,供Provider使用。(注:如果有需要,可以在dataId:annotationuser。configurators,group:dubbo配置文件中自定义一些provider相关的配置)overrideUrlWithConfig:1。创建serviceConfigurationListener,将serviceConfigurationListener缓存到serviceConfigurationListenersmap中。2。serviceConfigurationListener从nacos拉取服务配置信息(入参:dataId:。UserApi:version:。configurators,group:dubbo)。根据Nacos配置覆盖providerUrl配置doLocalExport:暴露Triple协议的服务。1。创建ExporterChangeableWrapper对象,将其放入到boundsmap中。2。创建ListenerExporterWrapper对象将其封装到ExporterChangeableWrapper对象中,将TripleProtocol对象封装到ListenerExporterWrapper对象中getRegistry:registryUrl是servicediscoveryregistry协议URL。1。创建协议对应的ServiceDiscoveryRegistry对象,将其放入registriesmap中。2。创建RegistryFactoryWrapper对象最终会创建ListenerRegistryWrapper对象,同时从Nacos配置中心拉取配置信息,进行数据封装
  注册Triple协议的服务
  核心代码在RegistryProtocoldoLocalExport中,具体如下:org。apache。dubbo。registry。integration。RegistryProtocolprivateTExporterChangeableWrapperTdoLocalExport(finalInvokerToriginInvoker,URLproviderUrl){StringkeygetCacheKey(originInvoker);return(ExporterChangeableWrapperT)bounds。computeIfAbsent(key,s{Invokerlt;?invokerDelegatenewInvokerDelegate(originInvoker,providerUrl);最终会调用TripleProtocolexport(InvokerTinvoker)方法returnnewExporterChangeableWrapper((ExporterT)protocol。export(invokerDelegate),originInvoker);});}org。apache。dubbo。rpc。protocol。tri。TripleProtocolpublicTExporterTexport(InvokerTinvoker)throwsRpcException{。。。。。。将tri协议缓存到exporterMap中(如:keyai。advance。guardian。annotation。api。SysUserApi:1。0。0:30002)exporter类型为TripleProtocolexporterMap。put(key,exporter);将invoker放入到invokersSet中,invoker类型为:FilterChainBuilder。CallbackRegistrationInvokerinvokers。add(invoker);将invoker放入到pathResolver中(如:keyai。advance。guardian。annotation。api。SysUserApi:1。0。0)pathResolver类型为TriplePathResolver,初始化TripleProtocol时通过SPI创建pathResolverpathResolver。add(url。getServiceKey(),invoker);keyai。advance。guardian。annotation。api。SysUserApipathResolver。add(url。getServiceModel()。getServiceModel()。getInterfaceName(),invoker);。。。。。。url。getOrDefaultApplicationModel()。getExtensionLoader(ExecutorRepository。class)。getDefaultExtension()通过SPI获取ExecutorRepository。createExecutorIfAbsent(url);给接口创建执行线程池,然后放入到executorsmap中PortUnificationExchanger。bind(url);optimizeSerialization(url);returnexporter;}
  注:protocol为ProtocolSerializationWrapper类型对象,逻辑跟injvm协议的服务注册基本一致,不过暴露Triple协议的服务最终会创建TripleProtocol对象TripleProtocol中将export、invoker等放入map中
  注册servicediscoveryregistry协议
  核心代码在ServiceDiscoveryRegistryregister和MetadataInfoaddService中,具体如下:org。apache。dubbo。registry。client。ServiceDiscoveryRegistrypublicfinalvoidregister(URLurl){if(!shouldRegister(url)){ShouldNotRegisterreturn;}注册servicediscoveryregistry协议doRegister(url);}org。apache。dubbo。metadata。MetadataInfopublicsynchronizedvoidaddService(URLurl){fixme,passinapplicationmodecontextduringinitializationofMetadataInfo。if(this。loadernull){this。loaderurl。getOrDefaultApplicationModel()。getExtensionLoader(MetadataParamsFilter。class);}ListMetadataParamsFilterfiltersloader。getActivateExtension(url,paramsfilter);generateservicelevelmetadataServiceInfoserviceInfonewServiceInfo(url,filters);将serviceInfo放入到servicesmap中this。services。put(serviceInfo。getMatchKey(),serviceInfo);extractcommoninstancelevelparamsextractInstanceParams(url,filters);if(exportedServiceURLsnull){exportedServiceURLsnewConcurrentSkipListMap();}将url放入到exportedServiceURLsmap中addURL(exportedServiceURLs,url);updatedtrue;}
  注:
  ServiceDiscoveryRegistrydoRegister最终会调用到MetadataInfoaddService
  缓存servicediscoveryregistry协议URL到exportedServiceURLsmap中
  缓存serviceInfo到servicesmap中
  发布服务注册事件
  核心代码在RegistryProtocolnotifyExport中,具体如下:org。apache。dubbo。registry。integration。RegistryProtocolprivateTvoidnotifyExport(ExporterChangeableWrapperTexporter){ScopeModelscopeModelexporter。getRegisterUrl()。getScopeModel();ListRegistryProtocolListenerlistenersScopeModelUtil。getExtensionLoader(RegistryProtocolListener。class,scopeModel)。getActivateExtension(exporter。getOriginInvoker()。getUrl(),REGISTRYPROTOCOLLISTENERKEY);if(CollectionUtils。isNotEmpty(listeners)){for(RegistryProtocolListenerlistener:listeners){发布RegistryProtocolListener的onExport事件listener。onExport(this,exporter);}}}
  注:我们可以看出注册servicediscoveryregistry协议的核心目的是为了将服务的接口相关的信息存储在内存中。从兼容性和平滑迁移两方面来考虑,在实现的时候采取复用ServiceConfig的暴露流程的方式。通过SPI方式动态创建RegistryProtocolListener的实现类是MigrationRuleListener,构造函数中会调用Nacos配置中心配置信息(订阅规则信息),如果应用中有订阅者则可以规则改变,进而概念调用者的行为。
  triple协议服务初始化
  由于暴露Triple协议服务的流程和暴露Injvm协议服务的流程是一致的,所以不再赘述。
  协议的服务初始化总结:
  代码中大量运用了DubboSPI方式动态加载实现类,相比JavaSPI,DubboSPI加载实现类更加灵活,不过代码没有运行起来的情况下可读性稍差协议的服务初始化主要目的是初始化注册、服务相关的元数据,以供元数据发布、服务注册时使用,不过也可以从Nacos配置中心读取数据,进行初始化
  应用级服务注册发布
  服务注册发布包含3部分:接口元数据发布应用映射接口数据发布应用服务注册
  接口元数据发布
  元数据分两种类型:注册中心的应用实例中的元数据信息(protocol、version相关信息)配置中心的接口相关的元数据信息(接口、方法、host、port、version等等相关信息)
  此处将接口元数据发布到配置中心
  因为在前面完成了injvm、servicediscoveryregistry、tri等等协议服务初始化工作之后基本完成了MetaData、ServiceBean的初始化工作,所以接下来的口元数据发布到配置中心比较简单
  核心代码从MetadataUtilspublishServiceDefinition开始,具体如下:org。apache。dubbo。registry。client。metadata。MetadataUtilspublicstaticvoidpublishServiceDefinition(URLurl,ServiceDescriptorserviceDescriptor,ApplicationModelapplicationModel){。。。。。。Stringsideurl。getSide();if(PROVIDERSIDE。equalsIgnoreCase(side)){StringserviceKeyurl。getServiceKey();FullServiceDefinitionserviceDefinitionserviceDescriptor。getFullServiceDefinition(serviceKey);if(StringUtils。isNotEmpty(serviceKey)serviceDefinition!null){serviceDefinition。setParameters(url。getParameters());for(Map。EntryString,MetadataReportentry:getMetadataReports(applicationModel)。entrySet()){MetadataReportmetadataReportentry。getValue();if(!metadataReport。shouldReportDefinition()){logger。info(Reportofservicedefinitionisdisabledforentry。getKey());continue;}metadataReport。storeProviderMetadata(newMetadataIdentifier(url。getServiceInterface(),url。getVersion()null?:url。getVersion(),url。getGroup()null?:url。getGroup(),PROVIDERSIDE,applicationModel。getApplicationName()),serviceDefinition);}}}。。。。。。}orgapachedubbometadatareportsupportAbstractMetadataReportprivatevoidstoreProviderMetadataTask(MetadataIdentifierproviderMetadataIdentifier,ServiceDefinitionserviceDefinition){try{。。。。。。allMetadataReports。put(providerMetadataIdentifier,serviceDefinition);failedReports。remove(providerMetadataIdentifier);StringdataJsonUtils。getJson()。toJson(serviceDefinition);doStoreProviderMetadata(providerMetadataIdentifier,data);saveProperties(providerMetadataIdentifier,data,true,!syncReport);}catch(Exceptione){。。。。。。}}
  步骤:publishServiceDefinition:getMetadataReports(applicationModel)获取接口元数据信息(该接口元数据通过DefaultApplicationDeployerstartMetadataCenter方法加载,其实在DeubboConfigBean初始化阶段已完成MetadataCenter数据初始化)。storeProviderMetadata:去发布接口的元数据。1。storeProviderMetadataTask:将serviceDefinition放入到mapallMetadataReports中。(以providerMetadataIdentifier作为key)生成元数据json字符串data)。2。storeMetadata:开始发布元数据信息(拼接生成dataId,拼接方法:{serviceInterface}:{version}:{group}:{side}:{applicationName},如:。。。UserApi:1。0。0::provider:applicationname。注:初始化MetadataIdentifier时,group为空)。publishConfigInner:调用nacosclient接口发布元数据信息,在nacos配置列表,指定namespace、指定group下创建配置数据saveProperties:将元数据信息放入到properties中,既本地缓存doSaveProperties:将元数据存储到本地文件中,文件为:Usersadvance。dubbodubbometadataapplicationname。。。8848。cache
  应用映射接口数据发布
  因为在前面已经完成了MetaData、ServiceBean的初始化工作的初始化工作,所以到这里,应用映射接口数据发布过程其实比较简单
  数据格式如下:{dataId:。。。UserApi,group:mapping,content:applicationname}
  从ServiceConfigexported开启应用映射接口数据发布,以下是一些核心逻辑调用来源为ServiceConfig。doExport()org。apache。dubbo。config。spring。ServiceBeanprotectedvoidexported(){调用ServiceConfig。exported方法super。exported();PublishServiceBeanExportedEventpublishExportEvent();}orgapachedubboconfigServiceConfigprotectedvoidexported(){exportedtrue;ListURLexportedURLsthis。getExportedUrls();exportedURLs。forEach(url{if(url。getParameters()。containsKey(SERVICENAMEMAPPINGKEY)){通过SPI方式动态获取MetadataServiceNameMapping对象ServiceNameMappingserviceNameMappingServiceNameMapping。getDefaultExtension(getScopeModel());try{封装应用映射接口数据booleansucceededserviceNameMapping。map(url);。。。。。。}catch(Exceptione){logger。error(Failedregisterinterfaceapplicationmappingforserviceurl。getServiceKey(),e);}}});。。。。。。}org。apache。dubbo。metadata。store。nacos。NacosMetadataReportOverridepublicbooleanregisterServiceAppMapping(Stringkey,Stringgroup,Stringcontent,Objectticket){try{if(!(ticketinstanceofString)){thrownewIllegalArgumentException(nacospublishConfigCasrequiresstringtypeticket);}往nacos配置中心注册应用映射数据returnconfigService。publishConfigCas(key,group,content,(String)ticket);}catch(NacosExceptione){logger。warn(nacospublishConfigCasfailed。,e);returnfalse;}}org。apache。dubbo。metadata。store。nacos。NacosConfigServiceWrapperpublicbooleanpublishConfigCas(StringdataId,Stringgroup,Stringcontent,StringcasMd5)throwsNacosException{调用nacos接口往配置中心注册应用映射数据returnconfigService。publishConfigCas(handleInnerSymbol(dataId),handleInnerSymbol(group),content,casMd5);}
  exported:应用映射接口数据。1。获取到已初始化的exportedURLs。2。serviceNameMapping。map最终会调用到NacosMetadataReportregisterServiceAppMapping方法(往配置中心发布应用映射接口数据)publishExportEvent:发布导出应用事件
  应用级服务注册
  注册应用级服务是Dubbo3。0服务暴露的核心流程,也就是之前提到的应用级服务发现机制。
  相比接口元数据的发布和应用映射接口数据的发布,应用级服务注册放在了最后执行
  ServiceInstance封装
  应用级服务的注册从DefaultApplicationDeployerprepareApplicationInstance开始,以下是一些核心逻辑:org。apache。dubbo。config。deploy。DefaultApplicationDeployerpublicvoidprepareApplicationInstance(){。。。。。。if(isRegisterConsumerInstance()){exportMetadataService();if(hasPreparedApplicationInstance。compareAndSet(false,true)){最终会调用到AbstractServiceDiscoveryregister()registerServiceInstance();}}}org。apache。dubbo。registry。clien。AbstractServiceDiscoverypublicsynchronizedvoidregister()throwsRuntimeException{创建服务实例对象this。serviceInstancecreateServiceInstance(this。metadataInfo);if(!isValidInstance(this。serviceInstance)){return;}创建元数据版本号booleanrevisionUpdatedcalOrUpdateInstanceRevision(this。serviceInstance);if(revisionUpdated){。。。。。。doRegister(this。serviceInstance);应用注册}}protectedServiceInstancecreateServiceInstance(MetadataInfometadataInfo){DefaultServiceInstanceinstancenewDefaultServiceInstance(serviceName,applicationModel);instance。setServiceMetadata(metadataInfo);设置dubbo。metadata。storagetype:localremote,local:存储在应用中,remote:存储在配置中心setMetadataStorageType(instance,metadataType);封装元数据,例如:timestamp1662343919223ServiceInstanceMetadataUtils。customizeInstance(instance,applicationModel);returninstance;}
  注:
  从DefaultModuleDeployerstart()的onModuleStarted()调用开始应用服务注册
  步骤:prepareApplicationInstance:开始应用注册registerMetadataAndInstance:1。从BeanFactory获取registryManager,获取ServiceDiscoveries。2。调用serviceDiscovery。register方法,开始注册createServiceInstance:1。创建DefaultServiceInstance对象。2。设置元数据(如:dubbo。metadata。storagetype:localremote,local:存储在应用中,remote:存储在配置中心)calOrUpdateInstanceRevision:创建元数据版本号reportMetadata:判断是否需要将元数据发布到配置中心
  向Nacos注册dubbo应用org。apache。dubbo。registry。nacos。NacosServiceDiscoveryOverridepublicvoiddoRegister(ServiceInstanceserviceInstance){execute(namingService,service{InstanceinstancetoInstance(serviceInstance);service。registerInstance(instance。getServiceName(),Constants。DEFAULTGROUP,instance);});}publicvoidregisterInstance(StringserviceName,Stringgroup,Instanceinstance)throwsNacosException{namingService。registerInstance(handleInnerSymbol(serviceName),group,instance);}
  步骤:doRegister:异步向注册dubbo应用将DefaultServiceInstanceserviceInstance封装成Instanceinstance对象,形成最终的应用注册信息,如下所示。registerInstance:dubbo应用group默认为:DEFAULTGROUP
  最终注册的dubbo应用信息
  Dubbo应用注册和SpringBoot应用注册区别Instance中metadata(Dubbo有5项数据、SpringBoot只有一项数据)SpringBoot注册group可以自定义,Dubbo不能自定义,默认为DEFAULTGROUP应用注册触发机制不同
  总结
  通过对Dubbo3。0服务端暴露全流程的解析发现,应用级服务发现机制的实现相对复杂,本文时序图以及代码梳理仅仅梳理核心又关键的逻辑点,想要深入理解Dubbo3。0应用级服务注册,还需要自己动手深入了解
  最后由于本人能力有限,如果有错误的地方,请指出!

孩子咳嗽老不好,吃梨来润肺止咳?提醒分不清类型可能加重咳嗽气温变化,一些孩子可能就会出现感冒咳嗽的症状,孩子咳嗽老不好,看着孩子难受,是让家长们最揪心的事情,除了吃药,有一些家长们会找各种小偏方来给孩子治疗咳嗽。日常生活中最常听说的就是梨春节也要做防护之羊康宝宝出院,还要注意什么?羊康宝宝出院还要注意什么目前,不少感染过新型冠状病毒的羊宝宝住院治疗康复了,在这里提醒家长出院并非万事大吉。那么羊宝宝回家该注意什么,如何护理呢?PART。01按时服药定期复查遵医双相障碍父母与其自责,不如努力改变与孩子相处的方式阿媛和妈妈一起来到中心的时候,她正读初二,神情显得有些拘谨,用细小的声音缓慢的语速开始诉说她内心的困惑。刘老师,你有被最好的朋友背叛过吗?问出这句话,阿媛眼眶发红,声音哽咽。原来,千年3手游最早期的玩法,还原真实的武侠世界只有随着在线时间增长的年龄系统,所以新手一样可以穿顶级装备。当传统限制被解除后,游戏中用来行走江湖的武功,修成了核心玩法,然而核心玩法又重回韩系PG。一如既往的还是要靠不断的刷,实科学饮茶十不要(一)饮茶不但是传统饮食文化,同时,由于茶中含有多种抗氧化物质与抗氧化营养素,对于消除自由基有一定的效果。因此喝茶也有助防老,具养生保健功能。茶叶中含有多种维生素和氨基酸,喝茶对于清油解民营经济大市福建泉州开门红签约总额1782亿元1月27日,中国民营经济大市福建泉州召开2023年民营经济发展大会。陈英杰摄中新网泉州1月27日电(记者孙虹)27日,农历正月初六,中国著名侨乡民营经济大市福建泉州已经动起来了。1你知道李渊造反的真实原因吗?大多数人不知道李渊开战的真正历史原因。都说是因为隋炀帝杨光荒淫无度,导致百姓生活贫困,天下大乱,才诞生了李渊起义。事实上,唐高祖起义的起因是李世民为唐高祖策划的一夜情。曾几何时,一卢晓新势力争抢刘慈欣和他的科幻IP,是一种互相成就(采访观察者网周远方编辑马媛媛)流浪地球2在今年春节档热映,据猫眼专业版数据,上映7天来总票房已破22亿。有一个现象值得关注,该片不但收获不错的关注和票房,而且还赢得不少商业合作,天水的春天天水,古称秦州,是秦人秦早期文化的发祥地。其中现辖的秦州区的名字就是这么来的。天水的春天来得比张掖和兰州都早。街头上随处可见盛开的紫叶李和榆叶梅。镜头正前方古老建筑后面就是著名的胡最新通报立案处罚!游客爆料北海用餐4个菜1500元的新闻引发关注。今天(28日),广西北海市场监管部门最新通报,涉事店家被责令停业整顿限期整改并立案处罚。多位游客反映北海一饭店价格偏高并质疑店家联合信阳鸡公山,青分楚豫鸡公山位于河南省信阳市南38公里的豫鄂两省交界处,雄踞于三关(武胜关平靖关九里关)之间。山脚下的武胜关是中国历史上九大名关之一,它犹如一把石锁,将大别山和桐柏山锁扣成一体,形成江淮
党代表有话说丨党代表心声大河网讯(记者赵檬)党的二十大报告回顾总结了过去五年工作和新时代十年的伟大变革,阐述了开辟马克思主义中国化时代化新境界中国式现代化的中国特色和本质要求等重大问题,擘画了全面建成社会轮到你敬酒时,别傻呆呆地坐着,牢记这3句敬酒话,惊艳众人在每次和朋友聚餐,只要是与人打交道的场合总是少不了白酒,如今白酒在日常生活中是比较常见的,可以说已经渗透进了我们的日常,不仅如此,在一些比较重要的场合,白酒还有着别的饮品所代替不了新兴人,这里有份重要提醒需要您马上看据悉,新兴疾控倡导中高风险地区人员严格遵守当地防控要求,非必要不来(返)新兴。同时,近七天内有高风险区旅居史来(返)新兴人员,实施七天集中隔离医学观察,第12357天各开展一次核酸我在这里想你了一天两天还是三天,白天了又黑夜,月圆了又缺,灰色的沙发上坐着的还是一个人,偶尔掉落的发丝也激不起任何涟漪。早饭是自己做的手抓饼配着生菜煎鸡蛋或者煎火腿,浇上点番茄酱和甜辣酱,勤快的人民日报每日金句摘抄1一个饱经沧桑而初心不改的党,才能基业常青一个铸就辉煌仍勇于自我革命的党,才能无坚不摧。2科学理论就像一面旗帜,旗帜立起来了,团结奋斗才有目标和方向否则,就如同一艘航船没有导航仪,让追逐永不停歇有一种东西,它只在黑夜里闪烁光芒,给摸索前行的人指引方向。有它导航,才不会迷惘。它的名字,叫做信仰。有一种东西,它只在逆境中生根滋养,给跋涉者以力量。有它存在,才不会张惶。它的名字拎得起每一份情义,放得下每一份失去有一句话说你走,我不送你,你来,无论多大的风多大的雨,我都要去接你。何为放下???不删不聊不打扰有时感情便是如此,别离的时候不必太过伤感,来时的道路也不必反复张望,人生本来就有聚有这是躺平?也不知从什么时候开始的,争吵的欲望是一点没有了。真的好生奇怪,之前那么强势的人,那么强势的性格,就这样绵软了下来。大小也是个领导,下属犯点错,之前会气的脸红脖子粗。现在觉得好可笑,1个多月暴涨超80!公募基金重仓ampampquot独门股ampampquot曝光,有什么特点?43只票获持有市值均超亿元公募基金独门重仓股有哪些呢?三季报落幕,公募基金在震荡市下积极调仓,持仓结构普遍有调整。除了抱团热门股外,公募资金开始转向更为分散的筹码。一些独树一帜的冷门股进入公募基金视野,被单成人高考顺利开考!广州统筹安排转运疏散工作11月5日,广州市人民政府新闻办公室召开广州市疫情防控新闻发布会。广州卫生健康委市教育局市商务局海珠区市疾控中心的相关负责同志通报广州市新冠肺炎疫情防控相关情况。昨日,海珠区实行临打压世锦赛女单冠军?国乒深陷舆论漩涡,全锦赛将揭晓答案北京时间11月3日,2022年全国乒乓球锦标赛将正式拉开序幕。继WTT澳门冠军赛河南新乡世界杯决赛之后,中国乒乓球队多位主力队员将再次捉对厮杀,争夺最后的总冠军。据悉,本次比赛设置
友情链接:快好找快生活快百科快传网中准网文好找聚热点快软网