当前位置: 首页 > news >正文

网站建设维护报价网站加入地图导航

网站建设维护报价,网站加入地图导航,做网站为什么先交定金,网页设计与制作课程思政教案基于Dubbo 3.1#xff0c;详细介绍了Dubbo服务的发布与引用的源码。 此前我们学习了Dubbo 3.x源码(12)—Dubbo服务发布导出源码(1)#xff0c;也就是Dubbo服务发布导出的入口源码#xff0c;现在我们继续学习#xff0c;服务导出的核心方法doExportUrls的源码。 Dubbo 3.x… 基于Dubbo 3.1详细介绍了Dubbo服务的发布与引用的源码。 此前我们学习了Dubbo 3.x源码(12)—Dubbo服务发布导出源码(1)也就是Dubbo服务发布导出的入口源码现在我们继续学习服务导出的核心方法doExportUrls的源码。 Dubbo 3.x源码(11)—Dubbo服务的发布与引用的入口Dubbo 3.x源码(12)—Dubbo服务发布导出源码(1)Dubbo 3.x源码(13)—Dubbo服务发布导出源码(2) 文章目录 1 doExportUrls导出全部服务url2 registerService注册服务2.1 ReflectionServiceDescriptor反射的服务描述符 3 loadRegistries加载注册中心地址3.1 genCompatibleRegistries获取兼容的双注册地址 4 doExportUrlsFor1Protocol单协议多注册中心导出5 exportUrl导出服务url5.1 exportLocal本地导出5.2 exportRemote远程导出5.3 publishServiceDefinition发布服务元数据 6 总结 1 doExportUrls导出全部服务url 该方法根据配置不同的协议导出服务地址到多个注册中心Dubbo支持dubbo、rmi、hessian、http、webservice、thrift、redis等多种协议。 大概步骤为 调用registerService方法注册serviceDescriptor服务描述符到本地内存的服务仓库ModuleServiceRepository的services缓存中即注册service。构建此服务对应的服务提供者模型providerModel并且调用registerProvider方法注册到本地内存的服务仓库ModuleServiceRepository的providers缓存中即注册provider。通过loadRegistries方法加载全部的注册中心url地址信息方便后续对于所有的注册中心进行服务导出。遍历当前service支持的所有协议对每一个协议调用doExportUrlsFor1Protocol方法尝试向多个注册中心url集合进行服务导出。 /*** ServiceConfig的方法* p* 导出服务url*/ SuppressWarnings({unchecked, rawtypes}) private void doExportUrls() {//获取Module级别的服务存储仓库其内部保存着服务提供者的缓存ModuleServiceRepository repository getScopeModel().getServiceRepository();//服务描述符通过它可以获取服务描述信息例如服务提供的方法服务接口名服务接口Class等ServiceDescriptor serviceDescriptor;//服务实现类型是否属于ServerService类型一般都不属于final boolean serverService ref instanceof ServerService;/** 1 注册serviceDescriptor服务描述符到本地内存的服务仓库ModuleServiceRepository的services缓存中即注册service*///如果属于ServerServiceif (serverService) {//通过服务实现获取服务描述符serviceDescriptor ((ServerService) ref).getServiceDescriptor();//注册服务描述符到服务仓库内部的services集合中repository.registerService(serviceDescriptor);} else {//大部分情况的逻辑//注册服务描述符到服务仓库内部的services集合中serviceDescriptor repository.registerService(getInterfaceClass());}/** 2 构建此服务对应的服务提供者模型providerModel并且注册到本地内存的服务仓库ModuleServiceRepository的providers缓存中即注册provider*/providerModel new ProviderModel(serviceMetadata.getServiceKey(),//服务实现ref,//服务描述符serviceDescriptor,//域模型getScopeModel(),//服务元数据服务接口类加载器serviceMetadata, interfaceClassLoader);// Compatible with dependencies on ServiceModel#getServiceConfig(), and will be removed in a future version//与ServiceModel#getServiceConfig()上的依赖项兼容并将在未来的版本中删除providerModel.setConfig(this);providerModel.setDestroyRunner(getDestroyRunner());//注册服务提供者模型到服务仓库内部的providers集合中repository.registerProvider(providerModel);/** 3 加载全部的注册中心url地址信息方便后续对于所有的注册中心进行服务导出*/ListURL registryURLs ConfigValidationUtils.loadRegistries(this, true);/** 4 遍历当前service支持的协议对每一个协议尝试向多个注册中心url集合进行服务导出*/for (ProtocolConfig protocolConfig : protocols) {//获取另一个路径keyString pathKey URL.buildKey(getContextPath(protocolConfig).map(p - p / path).orElse(path), group, version);// stub service will use generated service name//如果服务实现类型不属于ServerService类型一般都不属于if (!serverService) {// In case user specified path, register service one more time to map it to path.//如果用户指定了路径则再注册一次服务以将其映射到路径。repository.registerService(pathKey, interfaceClass);}/** 根据协议向多个注册中心url集合进行服务导出*/doExportUrlsFor1Protocol(protocolConfig, registryURLs);}//设置引用服务的urlproviderModel.setServiceUrls(urls); }2 registerService注册服务 该方法注册服务描述符到本地内存的服务仓库ModuleServiceRepository内部的services集合中。ServiceDescriptor服务描述符通过它的相关方法可以直接获取服务描述信息例如服务提供的方法服务接口名服务接口Class等。 实际上在ApplicationModel#initialize方法中就会创建一个服务ServiceRepository其构造器内部会调用ModuleServiceRepository#registerService方法在那时就会进行服务的注册了。 registerService方法的大概步骤为 基于服务接口class构建一个ReflectionServiceDescriptor对象。调用另一个两个参数的registerService方法完成注册key为服务接口名value就是对应的方法描述符。 /*** ModuleServiceRepository的方法* p* 注册服务描述符到服务仓库内部的services集合中** param interfaceClazz 服务接口* return 服务描述符*/ public ServiceDescriptor registerService(Class? interfaceClazz) {//构建一个ReflectionServiceDescriptor对象ServiceDescriptor serviceDescriptor new ReflectionServiceDescriptor(interfaceClazz);//调用另一个两个参数的registerService方法完成注册return registerService(interfaceClazz, serviceDescriptor); }/*** 注册服务** param interfaceClazz 服务接口* param serviceDescriptor 服务描述符* return 服务描述符*/ public ServiceDescriptor registerService(Class? interfaceClazz, ServiceDescriptor serviceDescriptor) {//如果该服务不存在则存入一个空的CopyOnWriteArrayList到services并返回否则返回此前存在的serviceDescriptors集合ListServiceDescriptor serviceDescriptors services.computeIfAbsent(interfaceClazz.getName(),k - new CopyOnWriteArrayList());//加锁synchronized (serviceDescriptors) {//遍历此前的serviceDescriptors集合获取第一个interfaceClazz为当前参数的数据OptionalServiceDescriptor previous serviceDescriptors.stream().filter(s - s.getServiceInterfaceClass().equals(interfaceClazz)).findFirst();//如果存在服务if (previous.isPresent()) {//获取此前的服务描述符return previous.get();}//如果不存在服务else {//将当前服务描述符加入到集合中serviceDescriptors.add(serviceDescriptor);//返回当前服务描述符return serviceDescriptor;}} }2.1 ReflectionServiceDescriptor反射的服务描述符 下面看看ReflectionServiceDescriptor的构造器的源码。 serviceInterfaceClass就是服务接口classinterfaceName就是接口全路径名随后调用initMethods方法获取接口全部的公共方法封装为ReflectionMethodDescriptor方法描述符对象随后存入methods缓存map集合key为方法名value为方法描述符集合因为可能有同名方法随后还会构建descToMethods缓存map集合key为方法名value为一个方法参数到方法描述符的映射。 public ReflectionServiceDescriptor(Class? interfaceClass) { //服务接口Classthis.serviceInterfaceClass interfaceClass;//接口全路径名this.interfaceName interfaceClass.getName();//初始化方法描述符initMethods(); }private void initMethods() {//获取接口的所有public方法包括自身的和从父类、接口继承的。Method[] methodsToExport this.serviceInterfaceClass.getMethods();for (Method method : methodsToExport) {//设置访问标记method.setAccessible(true);//构建方法描述符对象MethodDescriptor methodDescriptor new ReflectionMethodDescriptor(method);//存入methods缓存map集合key为方法名value为方法描述符集合因为可能有同名方法ListMethodDescriptor methodModels methods.computeIfAbsent(method.getName(), (k) - new ArrayList(1));methodModels.add(methodDescriptor);}//存入descToMethods缓存map集合key为方法名value为一个方法参数到方法描述符的映射methods.forEach((methodName, methodList) - {MapString, MethodDescriptor descMap descToMethods.computeIfAbsent(methodName, k - new HashMap());methodList.forEach(methodModel - descMap.put(methodModel.getParamDesc(), methodModel));}); }3 loadRegistries加载注册中心地址 一个服务可以支持多个注册中心例如zookeeper、nacos。该方法加载全部的注册中心url地址信息方便后续对于所有的注册中心进行服务导出。大概逻辑为 在该方法中首先获取全部的注册中心配置集合然后对每个注册中心的配置拼接好注册中心url。随后会对url根据协议类型进行一次统一的协议替换如果协议参数中有registry-typeservice的参数那么协议改为“service-discovery-registry”即服务级别发现注册中心地址否则协议取url的registry-protocol-type参数的值如果也没有该参数那么协议改为“registry”即接口级别发现注册中心地址。最后调用genCompatibleRegistries方法获取兼容的注册中心地址。 /*** ConfigValidationUtils的方法* 加载全部的注册中心url地址信息** param interfaceConfig 当前服务配置/引用配置* param provider 是否是服务提供者* return*/ public static ListURL loadRegistries(AbstractInterfaceConfig interfaceConfig, boolean provider) {// 必要时检查覆盖ListURL registryList new ArrayList();ApplicationConfig application interfaceConfig.getApplication();//获取全部的注册中心配置信息ListRegistryConfig registries interfaceConfig.getRegistries();if (CollectionUtils.isNotEmpty(registries)) {//遍历全部注册中心配置for (RegistryConfig config : registries) {// try to refresh registry in case it is set directly by user using config.setRegistries()//尝试刷新注册表以防它是由用户使用config.setRegistries()直接设置的。if (!config.isRefreshed()) {config.refresh();}//拼接注册中心url地址String address config.getAddress();if (StringUtils.isEmpty(address)) {address ANYHOST_VALUE;}if (!RegistryConfig.NO_AVAILABLE.equalsIgnoreCase(address)) {//url参数mapMapString, String map new HashMapString, String();AbstractConfig.appendParameters(map, application);AbstractConfig.appendParameters(map, config);map.put(PATH_KEY, RegistryService.class.getName());AbstractInterfaceConfig.appendRuntimeParameters(map);if (!map.containsKey(PROTOCOL_KEY)) {map.put(PROTOCOL_KEY, DUBBO_PROTOCOL);}//根据模式 \s*[|;]\s* 拆分urlListURL urls UrlUtils.parseURLs(address, map);for (URL url : urls) {//重写urlurl URLBuilder.from(url).addParameter(REGISTRY_KEY, url.getProtocol())//重设协议如果协议参数中有registry-typeservice的参数那么协议改为“service-discovery-registry”即服务级别发现注册中心地址//否则协议取url的registry-protocol-type参数的值如果也没有该参数那么协议改为“registry”即接口级别发现注册中心地址.setProtocol(extractRegistryType(url)).setScopeModel(interfaceConfig.getScopeModel()).build();//provider延迟注册状态将在RegistryProtocol#export中检查 provider delay register state will be checked in RegistryProtocol#exportif (provider || url.getParameter(SUBSCRIBE_KEY, true)) {registryList.add(url);}}}}}//获取兼容的注册中心地址return genCompatibleRegistries(interfaceConfig.getScopeModel(), registryList, provider); }3.1 genCompatibleRegistries获取兼容的双注册地址 在该方法调用之前会根据url参数中的registry-type参数更改协议如果协议参数中有registry-typeservice的参数那么协议改为“service-discovery-registry”即服务级别发现注册中心地址否则协议取url的registry-protocol-type参数的值如果也没有该参数那么协议改为“registry”即接口级别发现注册中心地址。 大部分情况下我们是不会主动在注册中心url中添加registry-type参数的因此一般协议都会被更改为registry。而在genCompatibleRegistries方法中会进行一系列判断并仍然可能多添加一个service-discovery-registry协议地址即最终返回两个协议地址一个是接口级别的另一个是服务级别的。这就是所谓的Dubbo3.0的服务端自动开启接口级 应用级双注册功能https://dubbo.apache.org/zh/docs3-v2/java-sdk/upgrades-and-compatibility/service-discovery/service-discovery-samples/ 该方法的大概逻辑为 如果当前为服务提供者那么需要对参数协议地址进行兼容性改造 如果参数url协议已经是服务级别发现注册中心地址协议即service-discovery-registry一般都是registry因为这需要手动配置registry-typeservice参数。 获取服务注册模式registerMode获取url参数中的register-mode属性如果没有则默认值为dubbo.application.register-mode属性的值该属性默认值为instance。如果值为null或者不是interface、instance、all三者中的一个那么registerMode默认instance即应用级模式。将参数中的应用级url加入结果集。如果服务注册模式为all并且没有接口级地址那么新增一个接口级地址即registry开头随后将接口级别注册地址加入结果集这样配置的一个注册中心就有两个url地址了。 协议如果不是服务级别发现注册中心地址协议即不是service-discovery-registry一般都是走这个逻辑。 获取服务注册模式registerMode获取url参数中的register-mode属性如果没有则默认值为dubbo.application.register-mode属性的值该属性默认值为all。如果值为null或者不是interface、instance、all三者中的一个那么registerMode默认interface即接口级模式。如果服务注册模式为all或者instance并且没有应用级地址那么新增一个应用级地址即service-discovery-registry开头随后将接口级别注册地址加入结果集。如果服务注册模式为all或者interface那么再将参数中的接口级url加入结果集。 如果当前不是服务提供者那么直接返回参数协议地址即可。 通过上面的步骤可知默认情况下在Dubbo3.1中将会对于一个注册中心返回两个注册协议地址一个接口级别的协议地址以registry开头兼容Dubbo2另一个是应用级别的协议地址以service-discovery-registry开头满足Dubbo3。 下面举一个这两种url地址的例子 服务级别发现注册中心地址service-discovery-registry://127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?REGISTRY_CLUSTERregistry1applicationdemo-providerapplication.version1dubbo2.0.2pid28659registryzookeeperregistry-typeserviceregistry.typeservicetimeout20001timestamp1666161869858接口级别发现注册中心地址registry://127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?REGISTRY_CLUSTERregistry1applicationdemo-providerapplication.version1dubbo2.0.2pid30594registryzookeepertimeout20001timestamp1666162612790 /*** ConfigValidationUtils的方法* 获取兼容的注册中心地址** param scopeModel 域模型* param registryList 注册地址* param provider 是否是服务提供者* return 兼容的注册中心地址*/ private static ListURL genCompatibleRegistries(ScopeModel scopeModel, ListURL registryList, boolean provider) {ListURL result new ArrayList(registryList.size());registryList.forEach(registryURL - {//如果是服务提供者if (provider) {// for registries enabled service discovery, automatically register interface compatible addresses.//对于启用服务发现的注册中心自动注册接口兼容地址。String registerMode;//协议如果是服务级别发现注册中心地址协议即service-discovery-registry一般都是registryif (SERVICE_REGISTRY_PROTOCOL.equals(registryURL.getProtocol())) {//获取服务注册模式获取url参数中的register-mode属性默认值为dubbo.application.register-mode属性的值该属性默认值为instanceregisterMode registryURL.getParameter(REGISTER_MODE_KEY, ConfigurationUtils.getCachedDynamicProperty(scopeModel, DUBBO_REGISTER_MODE_DEFAULT_KEY, DEFAULT_REGISTER_MODE_INSTANCE));//如果值为null或者不是interface、instance、all三者中的一个那么registerMode默认instance即应用级模式if (!isValidRegisterMode(registerMode)) {registerMode DEFAULT_REGISTER_MODE_INSTANCE;}//加入结果result.add(registryURL);//如果服务注册模式为all并且没有接口级地址那么新增一个接口级地址即registry开头if (DEFAULT_REGISTER_MODE_ALL.equalsIgnoreCase(registerMode) registryNotExists(registryURL, registryList, REGISTRY_PROTOCOL)) {URL interfaceCompatibleRegistryURL URLBuilder.from(registryURL).setProtocol(REGISTRY_PROTOCOL).removeParameter(REGISTRY_TYPE_KEY).build();//将接口级别注册地址加入结果集result.add(interfaceCompatibleRegistryURL);}}//协议如果不是服务级别发现注册中心地址协议即不是service-discovery-registry一般都是registryelse {//获取服务注册模式获取url参数中的register-mode属性默认值为dubbo.application.register-mode属性的值该属性默认值为allregisterMode registryURL.getParameter(REGISTER_MODE_KEY, ConfigurationUtils.getCachedDynamicProperty(scopeModel, DUBBO_REGISTER_MODE_DEFAULT_KEY, DEFAULT_REGISTER_MODE_ALL));//如果值为null或者不是interface、instance、all三者中的一个那么registerMode默认interface即接口级模式if (!isValidRegisterMode(registerMode)) {registerMode DEFAULT_REGISTER_MODE_INTERFACE;}//如果服务注册模式为all或者instance并且没有应用级地址那么新增一个应用级地址即service-discovery-registry开头if ((DEFAULT_REGISTER_MODE_INSTANCE.equalsIgnoreCase(registerMode) || DEFAULT_REGISTER_MODE_ALL.equalsIgnoreCase(registerMode)) registryNotExists(registryURL, registryList, SERVICE_REGISTRY_PROTOCOL)) {URL serviceDiscoveryRegistryURL URLBuilder.from(registryURL).setProtocol(SERVICE_REGISTRY_PROTOCOL).removeParameter(REGISTRY_TYPE_KEY).build();//将应用级别注册地址加入结果集result.add(serviceDiscoveryRegistryURL);}//如果服务注册模式为all或者interfaceif (DEFAULT_REGISTER_MODE_INTERFACE.equalsIgnoreCase(registerMode) || DEFAULT_REGISTER_MODE_ALL.equalsIgnoreCase(registerMode)) {//将接口级别注册地址加入结果集result.add(registryURL);}}//注册状态报告服务FrameworkStatusReportService reportService ScopeModelUtil.getApplicationModel(scopeModel).getBeanFactory().getBean(FrameworkStatusReportService.class);//注册报告服务reportService.reportRegistrationStatus(reportService.createRegistrationReport(registerMode));}//如果不是服务提供者那么直接将url加入结果集else {result.add(registryURL);}});return result; }4 doExportUrlsFor1Protocol单协议多注册中心导出 该方法根据单个协议向多个注册中心url集合进行服务导出。 首先会构建给定协议的服务导出url在dubbo中各种交互都是通过url的形式进行数据传递的所以url在dubbo源码中无处不在。 这里构建的服务导出url中包含ip、端口、服务接口、服务方法等参数样式例如dubbo://127.0.0.1:20880/org.apache.dubbo.demo.DemoService?anyhosttrueapplicationdemo-providerapplication.version1backgroundfalsebind.ip127.0.0.1bind.port20880delay5000deprecatedfalsedubbo2.0.2dynamictruegenericfalseinterfaceorg.apache.dubbo.demo.DemoServicemethodssayHello,sayHelloAsyncpid1688sideprovidertimeout3000timestamp1666274253014。 然后调用exportUrl方法对所有的注册中心进行服务url导出。 /*** ServiceConfig的方法* p* 根据协议向多个注册中心url集合进行服务导出** param protocolConfig 协议* param registryURLs 多个注册中心url集合*/ private void doExportUrlsFor1Protocol(ProtocolConfig protocolConfig, ListURL registryURLs) {//构建协议导出服务的各种参数用于组装服务导出的urlMapString, String map buildAttributes(protocolConfig);// remove null key and null value//移除空key和空值的属性map.keySet().removeIf(key - StringUtils.isEmpty(key) || StringUtils.isEmpty(map.get(key)));// init serviceMetadata attachments//将map存入服务元数据的附加数据中serviceMetadata.getAttachments().putAll(map);//通过协议 参数 构建服务导出url包含ip、端口、服务接口、服务方法等参数样式例如//dubbo://127.0.0.1:20880/org.apache.dubbo.demo.DemoService?anyhosttrueapplicationdemo-providerapplication.version1backgroundfalsebind.ip127.0.0.1bind.port20880delay5000deprecatedfalsedubbo2.0.2dynamictruegenericfalseinterfaceorg.apache.dubbo.demo.DemoServicemethodssayHello,sayHelloAsyncpid1688sideprovidertimeout3000timestamp1666274253014//在dubbo中各种交互都是通过url的形式进行数据传递的URL url buildUrl(protocolConfig, map);/** 导出协议url*/exportUrl(url, registryURLs); }5 exportUrl导出服务url 该方法是导出服务url的入口方法Dubbo在服务导出前会根据scope属性判断服务导出的范围none代表不导出、local仅导出到本地JVM、remote会导出到远程默认情况下会同时导出到本地JVM和远程。 通过exportLocal进行本地导出通过exportRemote方法进行远程导出在远程导出完毕之后还会调用MetadataUtils#publishServiceDefinition方法发布服务元数据信息。 /*** 导出服务url** param url 要导出的服务url* param registryURLs 注册中心url地址*/ private void exportUrl(URL url, ListURL registryURLs) {String scope url.getParameter(SCOPE_KEY);// dont export when none is configured//如果没有配置scope则不要导出if (!SCOPE_NONE.equalsIgnoreCase(scope)) {// export to local if the config is not remote (export to remote only when config is remote)//如果配置不是远程的则导出到本地(只有当配置是远程的时才导出到远程)if (!SCOPE_REMOTE.equalsIgnoreCase(scope)) {/** 1 本地导出*/exportLocal(url);}// export to remote if the config is not local (export to local only when config is local)//如果配置不是本地的则导出到远程(仅当配置为本地时导出到本地)if (!SCOPE_LOCAL.equalsIgnoreCase(scope)) {/** 2 远程导出*/url exportRemote(url, registryURLs);/** 3 发布服务元数据信息*/if (!isGeneric(generic) !getScopeModel().isInternal()) {MetadataUtils.publishServiceDefinition(url, providerModel.getServiceModel(), getApplicationModel());}}}//服务urls集合加入该导出的urlthis.urls.add(url); }5.1 exportLocal本地导出 构建injvm协议的urlip地址固定为127.0.0.1端口为0。可以看到所谓的本地导出没有监听端口没有远程调用。说白了就是本jvm的消费者调用本jvm上的服务提供者但是仍然会走dubbo的Filter和Listener。随后调用doExportUrl方法导出服务url。 /*** ServiceConfig的方法* p* 本地导出服务采用injvm协议* always export injvm*/ private void exportLocal(URL url) {//构建injvm协议的urlip地址固定为127.0.0.1端口为0。可以看到所谓的本地导出没有监听端口没有远程调用//说白了就是本jvm的消费者调用本jvm上的服务提供者但是仍然会走dubbo的Filter和ListenerURL local URLBuilder.from(url).setProtocol(LOCAL_PROTOCOL).setHost(LOCALHOST_VALUE).setPort(0).build();local local.setScopeModel(getScopeModel()).setServiceModel(providerModel);/** 导出url*/doExportUrl(local, false);logger.info(Export dubbo service interfaceClass.getName() to local registry url : local); }5.2 exportRemote远程导出 该方法会对服务url依次调用注册中心registryURL并通过doExportUrl方法进行远程服务导出在导出前注册中心url的attributes中添加key为exportvalue为服务导出url的属性。 注意如果注册中心url集合为空那么表示直连服务消费者如果注册中心配置成dubbo:registry address“N/A”/ 表示服务消费者直连服务提供者消费者可通过在service标签中配置url属性配置直连url。 /*** ServiceConfig的方法* p* 远程导出url** param url 导出的服务url* param registryURLs 注册中心url* return*/ private URL exportRemote(URL url, ListURL registryURLs) {//如果注册中心url集合不为空那么进行远程导出if (CollectionUtils.isNotEmpty(registryURLs)) {//遍历注册中心url依次注册for (URL registryURL : registryURLs) {//如果是应用级注册中心那么为url添加service-name-mappingtrue参数if (SERVICE_REGISTRY_PROTOCOL.equals(registryURL.getProtocol())) {url url.addParameterIfAbsent(SERVICE_NAME_MAPPING_KEY, true);}//if protocol is only injvm ,not register//如果导出的服务url为injvm那么不会进行导出if (LOCAL_PROTOCOL.equalsIgnoreCase(url.getProtocol())) {continue;}//动态添加dynamic属性url url.addParameterIfAbsent(DYNAMIC_KEY, registryURL.getParameter(DYNAMIC_KEY));//加载监控配置urlURL monitorUrl ConfigValidationUtils.loadMonitor(this, registryURL);if (monitorUrl ! null) {url url.putAttribute(MONITOR_KEY, monitorUrl);}// For providers, this is used to enable custom proxy to generate invoker//对于服务提供者这用于支持自定义代理生成调用程序String proxy url.getParameter(PROXY_KEY);if (StringUtils.isNotEmpty(proxy)) {registryURL registryURL.addParameter(PROXY_KEY, proxy);}if (logger.isInfoEnabled()) {if (url.getParameter(REGISTER_KEY, true)) {logger.info(Register dubbo service interfaceClass.getName() url url to registry registryURL.getAddress());} else {logger.info(Export dubbo service interfaceClass.getName() to url url);}}//注册中心url的attributes中添加key为exportvalue为服务导出url的属性进行服务导出doExportUrl(registryURL.putAttribute(EXPORT_KEY, url), true);}}//否则进行直接导出者表示直连服务消费者如果注册中心配置成dubbo:registry addressN/A/ 表示服务消费者直连服务提供者//消费者可通过在service标签中配置url属性配置直连urlelse {if (logger.isInfoEnabled()) {logger.info(Export dubbo service interfaceClass.getName() to url url);}doExportUrl(url, true);}return url; }5.3 publishServiceDefinition发布服务元数据 在进行了远程服务导出之后还需要通过MetadataUtils#publishServiceDefinition方法将服务的元数据信息发布到全部的元数据中心。 该方法最终是调用MetadataReport的storeProviderMetadata方法同步ProviderMetadata调用MetadataReport的storeConsumerMetadata方法同步ConsumerMetadata这两个方法我们在此前Dubbo启动元数据中心的时候就讲过了。 /*** 将生产者或者消费者的元数据信息发布到元数据中心** param url 服务url* param serviceDescriptor 服务描述符* param applicationModel 应用程序模型*/ public static void publishServiceDefinition(URL url, ServiceDescriptor serviceDescriptor, ApplicationModel applicationModel) {if (getMetadataReports(applicationModel).size() 0) {String msg Remote Metadata Report Server is not provided or unavailable, will stop registering service definition to remote center!;logger.warn(msg);return;}try {//服务类型String side url.getSide();//如果是provider即服务提供者if (PROVIDER_SIDE.equalsIgnoreCase(side)) {//获取服务key {group}/{servicePath}:{version}String serviceKey url.getServiceKey();//获取全部服务元数据宝库哦服务接口名、源代码位置、服务方法、注解等等服务描述信息FullServiceDefinition serviceDefinition serviceDescriptor.getFullServiceDefinition(serviceKey);if (StringUtils.isNotEmpty(serviceKey) serviceDefinition ! null) {serviceDefinition.setParameters(url.getParameters()); //url的参数设置到serviceDefinition中//遍历全部元数据中心for (Map.EntryString, MetadataReport entry : getMetadataReports(applicationModel).entrySet()) {MetadataReport metadataReport entry.getValue();if (!metadataReport.shouldReportDefinition()) {logger.info(Report of service definition is disabled for entry.getKey());continue;}//调用storeProviderMetadata方法同步ProviderMetadata该方法我们在此前Dubbo启动元数据中心的时候就讲过了metadataReport.storeProviderMetadata(new MetadataIdentifier(url.getServiceInterface(),url.getVersion() null ? : url.getVersion(),url.getGroup() null ? : url.getGroup(),PROVIDER_SIDE,applicationModel.getApplicationName()), serviceDefinition);}}}//如果是服务消费者else {//遍历全部元数据中心for (Map.EntryString, MetadataReport entry : getMetadataReports(applicationModel).entrySet()) {MetadataReport metadataReport entry.getValue();if (!metadataReport.shouldReportDefinition()) {logger.info(Report of service definition is disabled for entry.getKey());continue;}//调用storeConsumerMetadata方法同步ConsumerMetadata该方法我们在此前Dubbo启动元数据中心的时候就讲过了metadataReport.storeConsumerMetadata(new MetadataIdentifier(url.getServiceInterface(),url.getVersion() null ? : url.getVersion(),url.getGroup() null ? : url.getGroup(),CONSUMER_SIDE,applicationModel.getApplicationName()),url.getParameters());}}} catch (Exception e) {//ignore errorlogger.error(publish service definition metadata error., e);} }这两个方法会将服务元数据信息同步到元数据中心zookeeper作为元数据中心时节点路径/dubbo/{pathTag}/{servicePath}/{version}/{group}/{side}例如 /dubbo/metadata/org.apache.dubbo.demo.GreetingService/1.0.0/greeting/provider/demo-provider。 zookeeper中的provider的元数据格式如下 zookeeper中的consumer的元数据格式如下 6 总结 本次我们学习了服务导出的核心方法doExportUrls的部分源码。通过源码我们可以知道 默认情况下在Dubbo3.1中将会对于一个注册中心返回两个注册协议地址一个接口级别的协议地址以registry开头兼容Dubbo2另一个是应用级别的协议地址以service-discovery-registry开头满足Dubbo3。即服务端自动开启接口级 应用级双注册功能https://dubbo.apache.org/zh/docs3-v2/java-sdk/upgrades-and-compatibility/service-discovery/service-discovery-samples/。 在导出服务的时候Dubbo一个服务可以支持多个不同的协议例如dubbo、rmi、hessian、http、webservice、thrift、redis以及支持多个不同的注册中心例如zookeeper、nacos。服务将会被同时根据不同的协议想不同的注册中心进行服务导出。 doExportUrls方法中首先构建并注册对应服务的serviceDescriptor、providerModel然后通过loadRegistries加载全部的注册中心url地址信息最后遍历当前服务接口支持的协议依次调用doExportUrlsFor1Protocol方法尝试将每个协议向多个注册中心url进行服务导出。 loadRegistries方法用于加载注册中心url地址获取全部注册中心配置集合然后对每个注册中心的配置拼接好注册中心url协议将被改写为注册中心协议。 在Dubbo3.1版本中如果url没有特殊参数那么默认情况下每个注册中心都会生成两个注册中心协议url一个service-discovery-registry开头的协议表示服务级别发现注册中心一个registry开头的协议表示接口级别发现注册中心。也就是说服务将会同时进行接口级别和服务级别的注册。doExportUrlsFor1Protocol方法中会根据协议参数和当前服务元数据构建出一个服务导出协议url然后调用exportUrl方法继续导出。 exportUrl方法中获取scope属性判断服务导出的范围none代表不导出、local仅导出到本地JVM、remote会导出到远程默认为null表示会同时导出到本地JVM和远程分别调用exportLocal和exportRemote方法。在远程导出完毕之后还会调用MetadataUtils#publishServiceDefinition方法发布服务元数据信息。 exportLocal 方法用于本地导出用不上注册中心协议url主要用于本jvm的消费者调用本jvm上的服务提供者不涉及远程网络调用。 将服务导出协议url改写为injvm协议的urlip地址固定为127.0.0.1端口为0。可以看到所谓的本地导出没有监听端口没有远程调用但是仍然会走dubbo的Filter和Listener。随后调用doExportUrl方法执行协议导出。 exportRemote方法用于远程导出涉及到服务注册、启动服务端nettyserver等逻辑用于远程网络调用。 遍历全部注册中心协议url添加参数例如如果是应用级注册中心那么为url添加service-name-mappingtrue参数。在注册中心协议url内部的attributes中添加属性key为exportvalue为服务导出协议url随后调用doExportUrl方法执行协议导出。 exportLocal 和exportRemote方法最终都会调用doExportUrl方法该方法是服务导出的核心方法。下次我们将继续深入学习导出对某一个服务忌语某一个协议导出到某一个注册中心的doExportUrl方法的具体源码。
http://www.zqtcl.cn/news/783491/

相关文章:

  • 做得比较好的公司网站自己可以学做网站吗
  • 陕西省两学一做网站产品推广方案
  • 做网站ps文字有锯齿网站建设项目管理基本要求
  • 大连网站制作的网络科技公司取名创意
  • 哈尔滨企业网站建站推荐专业微网站营销
  • 阿里云模板建站怎么样上海免费建站模板
  • 中企动力网站建设合同织梦商业网站内容管理系统
  • 厦门石材网站建设个人网页模板制作
  • 网站建设责任分工手机兼职群
  • 做网站维护的收入怎么确认网校网站毕业设计的方案
  • 商丘网站建设想象力网络普洱做网站的报价
  • wordpress前端是什么网站建设备案优化
  • 琼海市建设局网站网络宣传网站建设制作
  • 怎样做返利网站apple私人免费网站怎么下载
  • 靖宇东兴自助建站深圳网站建设 排行榜
  • 怎样编辑网站梅州免费建站
  • 桂林北站怎么去阳朔简易网页
  • 百度123123网址大全无忧网站优化
  • 做个人网站用什么程序怎么建设一个人自己网站
  • 怎么样建设网站网站通州建设局网站
  • 网站备案有期限吗洛阳宣传片制作公司
  • 给wordpress添加引导页seo营销的策略有哪些
  • 聚美联盟网站怎么做金空间网站
  • 域名注册网站的域名哪里来的更改网站模板内容
  • 南京网站设计网站wordpress选择模板没
  • 河南省网站集约化建设国内房地产设计网站建设
  • 长治招聘网站建设电话销售精准客户资源
  • 灵璧有做公司网站的吗自定义wordpress
  • 创个网站怎么弄做国内第一游戏数据门户网站
  • 沈阳网站制作全过程小程序商城的好处