- 17-Dubbo服务提供者的双注册原理
- 17.1 简介
- 17.2 双注册配置的读取
- 17.2.1 注册中心地址作为元数据中心
- 17.2.2 双注册模式配置
- 17.3 双注册服务数据的注册
- 17.3.1 双注册代码逻辑调用简介
- 17.3.2 注册中心领域对象的初始化
- 17.4 双注册元数据信息的注册
上个博客《15-Dubbo的三大中心之元数据中心源码解析》导出服务端的时候多次提到了元数据中心,注册信息的注册。
Dubbo3出来时间不太长,对于现在的用户来说大部分使用的仍旧是Dubbo2.x,
Dubbo3 比较有特色也是会直接使用到的功能就是应用级服务发现:
- 应用级服务发现
从服务/接口粒度到应用粒度的升级,使得 Dubbo 在集群可伸缩性、连接异构微服务体系上更具优势。应用粒度能以更低的资源消耗支持超百万实例规模集群程; 实现与 Spring Cloud、Kubernetes Service 等异构微服务体系的互联互通。
对于直接使用Dubbo3的用户还好,可以仅仅开启应用级注册,但是对于Dubbo2.x的用户升级到Dubbo3的用户来说前期都是要开启双注册来慢慢迁移的,既注册传统的接口信息到注册中心,又注册应用信息到注册中心,同时注册应用与接口关系的元数据信息。
关于双注册与服务迁移的过程的使用可以参考官网:
应用级地址发现迁移指南
关于官网提供者双注册的图我这里贴一下,方便了解:
这个配置的解析过程在前面的博客介绍元数据中心的时候很详细的说了相关链接:15-Dubbo的三大中心之元数据中心源码解析
对应代码位于:DefaultApplicationDeployer类型的startMetadataCenter()方法
private void startMetadataCenter() {
//如果未配置元数据中心的地址等配置则使用注册中心的地址等配置做为元数据中心的配置
useRegistryAsMetadataCenterIfNecessary();
//...省略掉其他代码防止受到干扰
}
具体逻辑是这个方法:
useRegistryAsMetadataCenterIfNecessary
private void useRegistryAsMetadataCenterIfNecessary() {
//配置缓存中查询元数据配置
Collection metadataConfigs = configManager.getMetadataConfigs();
//...省略掉空判断
//查询是否有注册中心设置了默认配置isDefault 设置为true的注册中心则为默认注册中心列表,如果没有注册中心设置为默认注册中心,则获取所有未设置默认配置的注册中心列表
List defaultRegistries = configManager.getDefaultRegistries();
if (defaultRegistries.size() > 0) {
//多注册中心遍历
defaultRegistries
.stream()
//筛选符合条件的注册中心 (筛选逻辑就是查看是否有对应协议的扩展支持)
.filter(this::isUsedRegistryAsMetadataCenter)
//注册中心配置映射为元数据中心 映射就是获取需要的配置
.map(this::registryAsMetadataCenter)
//将元数据中心配置存储在配置缓存中方便后续使用
.forEach(metadataReportConfig -> {
//...省略掉具体的逻辑
});
}
}
关于元数据中心地址的获取,主要经过如下逻辑:
- 查询: 所有可用的默认注册中心列表
- 遍历: 多注册中心遍历
- 筛选: 选符合条件的注册中心 (筛选逻辑就是查看是否有对应协议的扩展支持)
- 转化: 注册中心配置RegistryConfig映射转换为元数据中心配置类型MetadataReportConfig
MetadataReportConfig 映射就是获取需要的配置。
最后会把查询到的元数据中心配置存储在配置缓存中方便后续使用。
17.2.2 双注册模式配置双注册配置类型是这个
dubbo.application.register-mode=all
默认值为all代表应用级注册和接口级注册,当前在完全迁移到应用级注册之后可以将服务直接迁移到应用级配置上去。
配置值解释:
- all 双注册
- instance 应用级注册
- interface 接口级注册
后面的代码如果想要看更详细的代码可以看博客《16-模块发布器发布服务全过程》
关于这个配置的使用我们详细来看下,在Dubbo服务注册时候会先通过此配置查询需要注册服务地址,具体代码位于ServiceConfig的doExportUrls()方法中:
private void doExportUrls() {
//省略掉前面的代码...
List registryURLs = ConfigValidationUtils.loadRegistries(this, true);
//省略掉后面的代码...
}
然后就是具体注册中心地址的获取过程我们看下:
ConfigValidationUtils的加载注册中心地址方法loadRegistries
public static ListloadRegistries(AbstractInterfaceConfig interfaceConfig, boolean provider) { // check && override if necessary //省略掉前面的代码... //这里会获取到一个接口配置注册地址例如:registry://127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=dubbo-demo-api-provider&dubbo=2.0.2&pid=9008®istry=zookeeper&release=3.0.8×tamp=1653703292768 List registries = interfaceConfig.getRegistries(); //省略掉中间的的代码... return genCompatibleRegistries(interfaceConfig.getScopeModel(), registryList, provider); }
ConfigValidationUtils的双注册地址的获取genCompatibleRegistries方法.
前面代码获取到了一个注册中心地址列表例如:
registry://127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=dubbo-demo-api-provider&dubbo=2.0.2&pid=9008®istry=zookeeper&release=3.0.8×tamp=1653703292768
下面可以看下如果根据配置来转换为应用级注册地址+接口级注册地址
private static ListgenCompatibleRegistries(ScopeModel scopeModel, List registryList, boolean provider) { List result = new ArrayList<>(registryList.size()); registryList.forEach(registryURL -> { if (provider) { // for registries enabled service discovery, automatically register interface compatible addresses. String registerMode; if (SERVICE_REGISTRY_PROTOCOL.equals(registryURL.getProtocol())) { //为了更好理解这里简化掉服务发现注册地址配置的逻辑判断过程仅仅看当前例子提供的值走的逻辑 } else { //双注册模式配置查询 对应参数为dubbo.application.register-mode 默认值为all registerMode = registryURL.getParameter(REGISTER_MODE_KEY, ConfigurationUtils.getCachedDynamicProperty(scopeModel, DUBBO_REGISTER_MODE_DEFAULT_KEY, DEFAULT_REGISTER_MODE_ALL)); //如果用户配置了一个错误的注册模式配置则只走接口级配置 这里默认值为interface if (!isValidRegisterMode(registerMode)) { registerMode = DEFAULT_REGISTER_MODE_INTERFACE; } //这个逻辑是满足应用级注册就添加一个应用级注册的地址 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); } //这个逻辑是满足接口级注册配置就添加一个接口级注册地址 if (DEFAULT_REGISTER_MODE_INTERFACE.equalsIgnoreCase(registerMode) || DEFAULT_REGISTER_MODE_ALL.equalsIgnoreCase(registerMode)) { result.add(registryURL); } } //省略掉若干代码和括号 return result; }
可以看到这里简化的配置比较容易理解了
- 双注册模式配置查询 对应参数为dubbo.application.register-mode ,默认值为all
- 如果用户配置了一个错误的注册模式配置则只走接口级配置 这里默认值为interface
- 满足应用级注册就添加一个应用级注册的地址
- 满足接口级注册配置就添加一个接口级注册地址
这个方法是根据服务注册模式来判断使用接口级注册地址还是应用级注册地址分别如下所示:
配置信息:
dubbo.application.register-mode
配置值:
- interface
- 接口级注册
- instance
- 应用级注册
- all
- 接口级别和应用级都注册
最终的注册地址配置如下:
接口级注册地址:
registry://127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=dubbo-demo-api-provider&dubbo=2.0.2&pid=9008®istry=zookeeper&release=3.0.8×tamp=1653703292768
应用级注册地址:
service-discovery-registry://8.131.79.126:2181/org.apache.dubbo.registry.RegistryService?application=dubbo-demo-api-provider&dubbo=2.0.2&pid=10275®istry=zookeeper&release=3.0.8×tamp=165370442592017.3 双注册服务数据的注册 17.3.1 双注册代码逻辑调用简介
前面说了这个注册服务的配置地址会由Dubbo内部进行判断如果判断是all的话会自动将一个配置的注册地址转变为两个一个是传统的接口级注册,一个是应用级注册使用的配置地址
然后我们先看注册中心,注册服务数据的源码
如果想要查看源码细节可以在RegistryProtocol类型的export(final Invoker originInvoker) 方法的如下代码位置打断点:
RegistryProtocol的export方法的注册中心注册数据代码如下:
// url to registry 注册服务对外的接口
final Registry registry = getRegistry(registryUrl);
final URL registeredProviderUrl = getUrlToRegistry(providerUrl, registryUrl);
// decide if we need to delay publish (provider itself and registry should both need to register)
boolean register = providerUrl.getParameter(REGISTER_KEY, true) && registryUrl.getParameter(REGISTER_KEY, true);
if (register) {
register(registry, registeredProviderUrl);
}
// register stated url on provider model
registerStatedUrl(registryUrl, registeredProviderUrl, register);
exporter.setRegisterUrl(registeredProviderUrl);
exporter.setSubscribeUrl(overrideSubscribeUrl);
if (!registry.isServiceDiscovery()) {
// Deprecated! Subscribe to override rules in 2.6.x or before.
registry.subscribe(overrideSubscribeUrl, overrideSubscribeListener);
}
在上个博客中我们整体说了下服务注册时候的一个流程,关于数据向注册中心的注册细节这里可以详细看下
17.3.2 注册中心领域对象的初始化前面的代码使用url来获取注册中心操作对象如下调用代码:
// url to registry 注册服务对外的接口 final Registry registry = getRegistry(registryUrl);
对应代码如下:
protected Registry getRegistry(final URL registryUrl) {
//这里分为两步先获取注册中心工厂对象
RegistryFactory registryFactory = ScopeModelUtil.getExtensionLoader(RegistryFactory.class, registryUrl.getScopeModel()).getAdaptiveExtension();
//使用注册中心工厂对象获取注册中心操作对象
return registryFactory.getRegistry(registryUrl);
}
关于参数URL有两个在前面已经说过,url信息如下:
接口级注册地址:
registry://127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=dubbo-demo-api-provider&dubbo=2.0.2&pid=9008®istry=zookeeper&release=3.0.8×tamp=1653703292768
应用级注册地址:
service-discovery-registry://8.131.79.126:2181/org.apache.dubbo.registry.RegistryService?application=dubbo-demo-api-provider&dubbo=2.0.2&pid=10275®istry=zookeeper&release=3.0.8×tamp=1653704425920
注册中心工厂对象与注册中心操作对象的获取与执行我们通过Debug来看比较麻烦,这里涉及到很多扩展机制动态生成的代码我们无法看到,这里我直接来贴一下比较关键的一些类型,以Zookeeper注册中心来举例子:
17.4 双注册元数据信息的注册//todo 中午了先做饭吃了后面继续写


![[Dubbo3.0.8源码解析系列]-17-Dubbo3应用级注册之服务提供者的双注册原理 [Dubbo3.0.8源码解析系列]-17-Dubbo3应用级注册之服务提供者的双注册原理](http://www.mshxw.com/aiimages/31/948922.png)
