- 一、前言
- 1. 前提
- 二、提供者
- 1. RegistryProtocol#overrideUrlWithConfig
- 1.1 ServiceConfigurationListener
- 1.1.1 DynamicConfiguration#getDynamicConfiguration
- 1.1.2 DynamicConfiguration#addListener
- 1.1.3 AbstractConfiguratorListener#process
- 1.2 ProviderConfigurationListener
- 2. RegistryService#subscribe
- 3. 总结
- 三、消费者
- 1. ConsumerConfigurationListener & ReferenceConfigurationListener
- 1.1 RegistryDirectory#mergeUrl
本系列为个人Dubbo学习笔记衍生篇,是正文篇之外的衍生内容,内容来源于《深度剖析Apache Dubbo 核心技术内幕》, 过程参考官方源码分析文章。仅用于个人笔记记录。本文分析基于Dubbo2.7.0版本,由于个人理解的局限性,若文中不免出现错误,感谢指正。
1. 前提
Dubbo 在 2.7 版本之后提供了新的监听方式。本文来分析 Dubbo 2.6 和 Dubbo 2.7 版本之间对于配置更新的差异。
首先我们需要知道 Dubbo 2.7提供了 应用级别配置 和 服务级别配置
- 应用级别配置:配置会作用于指定的应用名的应用。Dubbo 2.7 及以上新增的配置方式,会在配置中心上创建节点。
- 服务级别配置:配置只会作用于指定的接口服务。
综述,如下图(下图中的zk充当注册中心和配置中心,因此会创建下面三个节点):
- 节点 ① :Dubbo 2.6 版本使用的配置节点。当我们使用 dubbo-admin 进行配置时会创建该节点由于Dubbo 2.6 版本不存在配置中心和元数据中心,所以节点会在注册中心上创建,路径规则为 /dubbo/{服务接口全路径}/configurations
- 节点 ② :Dubbo 2.7 版本使用的配置节点,应用级别配置,该节点创建在配置中心上。路径规则为 /dubbo/config/{应用名}/configurations
- 节点 ③ :Dubbo 2.7 版本使用的配置节点,服务级别配置,该节点创建在配置中心上。路径规则为 /dubbo/config/{分组}/{服务接口全路径}:{版本号}/configurations。该节点的内容等价于节点 ①的内容(实际节点内容由于生成规则不同有所不同,但其所包含的配置内容是等价的)。
本文使用的 dubbo-admin-0.2.0 来进行动态配置,该版本会兼容 2.6 和 2.7 版本,因此如果进行服务级别动态配置,则上图中的 ① 和 ③ 两个配置节点都会创建。如果进行应用级别动态配置,则会创建 ② 的配置节点,因为 Dubbo2.6版本不支持 应用级别配置。
关于上述内容,在 Dubbo笔记 ㉒ :配置中心 中有较为完整的描述,如有需要可阅读。
基于上述前提,我们来看下面的内容。
二、提供者我们知道提供者在服务暴露时的核心方法是 RegistryProtocol#export,这里我们忽略其他代码,仅看如下代码:
@Override publicExporter export(final Invoker originInvoker) throws RpcException { .... // 获取服务接口 URL, 获取注册中心 URL // 获取订阅覆盖 URL final URL overrideSubscribeUrl = getSubscribedOverrideUrl(providerUrl); // 生成 对 overrideSubscribeUrl 的监听器 final OverrideListener overrideSubscribeListener = new OverrideListener(overrideSubscribeUrl, originInvoker); overrideListeners.put(overrideSubscribeUrl, overrideSubscribeListener); // 1. 从配置中心加载最新的配置并监听配置 providerUrl = overrideUrlWithConfig(providerUrl, overrideSubscribeListener); .... // 将服务注册到注册中心 // Deprecated! Subscribe to override rules in 2.6.x or before. // 2. 订阅并监听配置 registry.subscribe(overrideSubscribeUrl, overrideSubscribeListener); .... // 收尾工作 return new DestroyableExporter<>(exporter); }
这里我们主要关注两个方法 :
- RegistryProtocol#overrideUrlWithConfig :Dubbo 2.7 及以上提供的新的订阅方式。通过 ProviderConfigurationListener 和 ServiceConfigurationListener 监听器来监听动态配置,处理应用级别和服务级别的配置。
- RegistryService#subscribe :Dubbo 2.7 以下版本使用的订阅方式,可以用于处理服务级别的配置。
下面我们详细来看这两个方法的具体实现:
1. RegistryProtocol#overrideUrlWithConfig
该函数是 Dubbo2.7 版本提供的,Dubbo 2.6 版本所有数据都存在注册中心上,Dubbo 2.7版本分成了注册中心,配置中心,和元数据中心。
private final ProviderConfigurationListener providerConfigurationListener = new ProviderConfigurationListener(); private final MapserviceConfigurationListeners = new ConcurrentHashMap<>(); private URL overrideUrlWithConfig(URL providerUrl, OverrideListener listener) { // 获取配置中心应用级别配置并覆盖本地配置 providerUrl = providerConfigurationListener.overrideUrl(providerUrl); // 创建服务级别配置监听器 ServiceConfigurationListener serviceConfigurationListener = new ServiceConfigurationListener(providerUrl, listener); serviceConfigurationListeners.put(providerUrl.getServiceKey(), serviceConfigurationListener); // 获取配置中心服务级别配置并覆盖本地配置 return serviceConfigurationListener.overrideUrl(providerUrl); }
这里我们的关键点在于两个监听器 :
- ProviderConfigurationListener : 应用级别监听器,因为一个程序只能有一个应用名,所以这里之需要一个 ProviderConfigurationListener 实例即可。
- ServiceConfigurationListener :服务级别监听器。因为一个程序可以提供多个 Dubbo 接口服务,所以这使用 serviceConfigurationListeners 来保存不同接口的监听器。
我们这里先来看 ServiceConfigurationListener 之后再来看 ProviderConfigurationListener 。
1.1 ServiceConfigurationListenerServiceConfigurationListener 用于 Dubbo 2.7 及以后 来处理 服务级别的配置,在 ServiceConfigurationListener 的构造函数中会通过 this.initWith(providerUrl.getEncodedServiceKey() + CONFIGURATORS_SUFFIX) 来监听当前服务的配置。其实现如下:
private class ServiceConfigurationListener extends AbstractConfiguratorListener { private URL providerUrl; private OverrideListener notifyListener; public ServiceConfigurationListener(URL providerUrl, OverrideListener notifyListener) { this.providerUrl = providerUrl; this.notifyListener = notifyListener; // 这里需要注意,在ServiceConfigurationListener 创建时会通过该方法订阅配置中心的配置。 this.initWith(providerUrl.getEncodedServiceKey() + CONFIGURATORS_SUFFIX); } privateURL overrideUrl(URL providerUrl) { return RegistryProtocol.getConfigedInvokerUrl(configurators, providerUrl); } @Override protected void notifyOverrides() { notifyListener.doOverrideIfNecessary(); } }
这里的关键在于 ServiceConfigurationListener#initWith ,其实现在其父类中完成,如下:
// AbstractConfiguratorListener#initWith protected final void initWith(String key) { // 1. 获取配置中心 DynamicConfiguration dynamicConfiguration = DynamicConfiguration.getDynamicConfiguration(); // 2. 添加监听器 dynamicConfiguration.addListener(key, this); // 根据 key 获取配置中心上的配置 String rawConfig = dynamicConfiguration.getConfig(key); if (!StringUtils.isEmpty(rawConfig)) { // 3. 如果配置不为空,则会解析配置并更新本地配置 process(new ConfigChangeEvent(key, rawConfig)); } }
下面我们按照注释的顺序一一来看:
1.1.1 DynamicConfiguration#getDynamicConfigurationDynamicConfiguration#getDynamicConfiguration 实现如下:
static DynamicConfiguration getDynamicConfiguration() { // 获取动态配置中心,这里即获取的配置中心的对象 Optionaloptional = Environment.getInstance().getDynamicConfiguration(); // 如果没有配置中心,则获取默认的 DynamicConfiguration return (DynamicConfiguration) optional.orElseGet(() -> getExtensionLoader(DynamicConfigurationFactory.class) .getDefaultExtension() .getDynamicConfiguration(null)); }
我们这里以 zk 为配置中心,所以获取的是 ZookeeperDynamicConfiguration 实例。ZookeeperDynamicConfiguration 会在构造函数中建立与 ZK 的连接并获取节点内容缓存到本地。
1.1.2 DynamicConfiguration#addListenerDynamicConfiguration#addListener 是实现监听服务级别配置的的核心方法,这里调用的是 DynamicConfiguration 接口的默认方法如下
// org.apache.dubbo.configcenter.DynamicConfiguration#addListener(java.lang.String, org.apache.dubbo.configcenter.ConfigurationListener) default void addListener(String key, ConfigurationListener listener) { // 分组指定为 dubbo addListener(key, DEFAULT_GROUP, listener); }
随后通过如下调用顺序:
ZookeeperDynamicConfiguration#addListener -> CacheListener#addListener
CacheListener#addListener 实现如下:
private Map> keyListeners = new ConcurrentHashMap<>(); public void addListener(String key, ConfigurationListener configurationListener) { // 将 key 和 configurationListener 保存到了 keyListeners 中。 Set listeners = this.keyListeners.computeIfAbsent(key, k -> new CopyOnWriteArraySet<>()); listeners.add(configurationListener); }
这里可以看到,这里会将当前接口服务的key 和监听器保存到 CacheListener#keyListeners 中。而当我们进行动态配置时会修改配置中心的节点,而当 zk 节点变化时会触发 CacheListener#childEvent 方法,其实现如下:
@Override public void childEvent(Curatorframework aClient, TreeCacheEvent event) throws Exception { TreeCacheEvent.Type type = event.getType(); ChildData data = event.getData(); if (type == TreeCacheEvent.Type.INITIALIZED) { initializedLatch.countDown(); return; } // TODO, ignore other event types if (data == null) { return; } // TODO We limit the notification of config changes to a specific path level, for example // /dubbo/config/service/configurators, other config changes not in this level will not get notified, // say /dubbo/config/dubbo.properties if (data.getPath().split("/").length >= 5) { byte[] value = data.getData(); // 根据节点path 转换为 key String key = pathToKey(data.getPath()); ConfigChangeType changeType; switch (type) { case NODE_ADDED: changeType = ConfigChangeType.ADDED; break; case NODE_REMOVED: changeType = ConfigChangeType.DELETeD; break; case NODE_UPDATED: changeType = ConfigChangeType.MODIFIED; break; default: return; } ConfigChangeEvent configChangeEvent = new ConfigChangeEvent(key, new String(value, StandardCharsets.UTF_8), changeType); // 根据转换的key 来获取对应的监听器,该监听器在 Setlisteners = keyListeners.get(key); if (CollectionUtils.isNotEmpty(listeners)) { listeners.forEach(listener -> listener.process(configChangeEvent)); } } }
那么这里我们就可以得知:当配置中心上有节点变动时会触发 CacheListener#childEvent 方法,而在 CacheListener#childEvent 方法中会通过 pathToKey(data.getPath()); 将path 转为 key,随后依据 key 从 keyListeners 找到对应节点的监听器,并执行 listener.process(configChangeEvent) 来触发监听器 *** 作。
上述理论上如此,但需要注意的是:
当我们使用zk 作为配置中心时 Dubbo的服务级别配置是通过 RegistryService#subscribe 完成更新,而不是 通过 ServiceConfigurationListener 完成,因为当配置节点更新时并不会触发监听器 *** 作, 原因在于keyListeners 中的key 和 pathToKey(data.getPath()); 转化的key 并不一致。
并且
- 该种情况仅针对于 服务级别配置,应用级别配置并不会出现该种情况。
- 个人测试,如果使用 Nacos 来作为配置中心则可以执行 ServiceConfigurationListener 的监听器逻辑,所以推测可能是 Dubbo对 zk 作为配置中心时的一个缺陷 ?
上述问题的原因如下 :
在 Dubbo 2.7.0 的版本下,ServiceConfigurationListener 在初始化时会传入监听器的 key,而这个key是通过 providerUrl.getEncodedServiceKey() + CONFIGURATORS_SUFFIX 生成。
其中 URL#getEncodedServiceKey 实现如下,可以看到仅仅是将 serviceKey 的第一个 / 替换为了 *
public String getEncodedServiceKey() { String serviceKey = this.getServiceKey(); serviceKey = serviceKey.replaceFirst("/", "*"); return serviceKey; }
所以实际上 CacheListener#keyListeners 中监听的 key 为dubbo*com.kingfish.service.impl.SimpleDemoService:1.0.0.configurators。也即是说对于 com.kingfish.service.impl.SimpleDemoService 这个服务接口,我们对应的监听器的key 为 dubbo*com.kingfish.service.impl.SimpleDemoService:1.0.0.configurators 。
如下图:
而当我们修改动态配置时,即zk节点发生变化,会触发 CacheListener#childEvent 方法。其中会根据节点路径转换为对应的key,如下:
1. 修改的节点路径如下: data.getPath() = /dubbo/config/dubbo/com.kingfish.service.impl.SimpleDemoService:1.0.0/configurators 2. key 的生成规则如下: key = pathToKey(data.getPath()) = dubbo.com.kingfish.service.impl.SimpleDemoService:1.0.0.configurators
也即是说,当我们修改 com.kingfish.service.impl.SimpleDemoService 这个服务接口的动态配置时,Dubbo 根据 zk 节点路径转化的Key 为 dubbo.com.kingfish.service.impl.SimpleDemoService:1.0.0.configurators。而我们实际的key为 dubbo*com.kingfish.service.impl.SimpleDemoService:1.0.0.configurators 。从而导致我们无法通过path转化的key找到对应服务的监听器。
如下图,由于变更节点转化的key 与 keyListeners中的key 不匹配导致 无法根据变更节点找到对应的监听器来进行处理:
AbstractConfiguratorListener#process -> RegistryProtocol.ServiceConfigurationListener#notifyOverrides -> RegistryProtocol.OverrideListener#doOverrideIfNecessary
在 RegistryProtocol.OverrideListener#doOverrideIfNecessary 中 完成了本地的配置更新,如下:
public synchronized void doOverrideIfNecessary() { final Invoker> invoker; if (originInvoker instanceof InvokerDelegate) { invoker = ((InvokerDelegate>) originInvoker).getInvoker(); } else { invoker = originInvoker; } //The origin invoker // 获取 原始 URL URL originUrl = RegistryProtocol.this.getProviderUrl(invoker); String key = getCacheKey(originInvoker); ExporterChangeableWrapper> exporter = bounds.get(key); if (exporter == null) { logger.warn(new IllegalStateException("error state, exporter should not be null")); return; } //The current, may have been merged many times // 获取当前 URL,当前URL,可能已经合并过很多次,所以并不一定等同于 originUrl URL currentUrl = exporter.getInvoker().getUrl(); //Merged with this configuration // 使用 originUrl 与当前配置合并后,产生新 url,如果动态配置更新了,则新的URl和旧URl肯定不同 URL newUrl = getConfigedInvokerUrl(configurators, originUrl); // 合并服务级别配置 newUrl = getConfigedInvokerUrl(serviceConfigurationListeners.get(originUrl.getServiceKey()) .getConfigurators(), newUrl); // 合并应用级别配置 newUrl = getConfigedInvokerUrl(providerConfigurationListener.getConfigurators(), newUrl); // currentUrl != newUrl, 则说明配置有变动,则重新发布服务 if (!currentUrl.equals(newUrl)) { // 重新发布服务 RegistryProtocol.this.reExport(originInvoker, newUrl); ... } }1.2 ProviderConfigurationListener
ProviderConfigurationListener 的实现与 ServiceConfigurationListener 基本相同,不同的是,ServiceConfigurationListener 监听的服务级别, ProviderConfigurationListener监听的是应用级别的配置,所以这里监听的节点会有所不同。
private class ProviderConfigurationListener extends AbstractConfiguratorListener { public ProviderConfigurationListener() { // 传入 监听的 key。这里为 {应用名}.configurators this.initWith(ApplicationModel.getApplication() + CONFIGURATORS_SUFFIX); } // 获取现有配置规则,并在导出之前覆盖提供程序URL。当服务发布时会调用此方法来对配置进行刷新。 privateURL overrideUrl(URL providerUrl) { return RegistryProtocol.getConfigedInvokerUrl(configurators, providerUrl); } // 当监听节点有变化时会触发该方法,此方法会调用监听器的doOverrideIfNecessary 方法来刷新本地缓存的配置。 @Override protected void notifyOverrides() { // 遍历所有的 overrideListeners 并调用 doOverrideIfNecessary 来刷新 配置。 overrideListeners.values().forEach(listener -> ((OverrideListener) listener).doOverrideIfNecessary()); } }
对于应用级别的动态配置,即是是 ZK 作为配置中心也不会出现上面 服务级别动态配置无法触发监听器的情况。
2. RegistryService#subscribe关于这部分的具体代码,详阅:Dubbo笔记 ⑤ : 服务发布流程 - Protocol#export 中 服务订阅 部分的内容。
我们这里直接说结果 :由于 Dubbo 2.7之前的版本只有注册中心,并没有配置中心和元数据中心,所以动态配置的内容会保存在注册中心上。以 zk 为例,如下图:
RegistryService#subscribe 会监听 图 ① 标注的节点,而上面我们提到,当我们创建服务级别的配置时,dubbo-admin-0.2.0 为了 兼容,会同时创建 图① 和 图 ② 节点。
而我们上面提到 当配置中心是zk 时ServiceConfigurationListener 并不起作用,所以此时的服务级别的动态配置更新就是通过 RegistryService#subscribe 监听 图① 节点的变化完成。
3. 总结如下表
对于 Dubbo 2.6 及以下,通过 ServiceConfigurationListener 和 ProviderConfigurationListener 监听器完成更新。但是对于 zk作为配置中心的场景,ServiceConfigurationListener 失效,通过 Dubbo 2.6 版本的旧逻辑来完成更新。
对于 Dubbo 2.7及以上,通过 RegistryService#subscribe 方法中的逻辑完成更新。
应用配置和服务级别配置的优先级为: 应用级别 > 服务级别
因为配置的刷新是通过 RegistryProtocol.OverrideListener#notify 方法完成,在这个方法中会先 加载 ServiceConfigurationListeners 的配置,后加载 ProviderConfigurationListener 的配置。即后加载的配置会覆盖之前加载的配置,故应用级别 > 服务级别。
消费者的流程和提供者相似,下面来简单介绍。
当消费者启动时,Dubbo 会按照如下流程执行:
RegistryProtocol#refer -> RegistryProtocol#doRefer -> RegistryDirectory#subscribe
其中 RegistryDirectory#subscribe 的实现如下:
private static final ConsumerConfigurationListener consumerConfigurationListener = new ConsumerConfigurationListener(); private ReferenceConfigurationListener serviceConfigurationListener; public void subscribe(URL url) { setConsumerUrl(url); // 1. 订阅应用级别配置 consumerConfigurationListener.addNotifyListener(this); // 2. 订阅当前引用的接口服务的服务级别配置 serviceConfigurationListener = new ReferenceConfigurationListener(this, url); // 3. Dubbo 2.6 版本的订阅方式 registry.subscribe(url, this); }
这里我们又看到了两个监听器,我们直接说明:
- RegistryDirectory.ConsumerConfigurationListener#addNotifyListener : Dubbo 2.7 版本新增,订阅当前消费者应用级别配置.
- RegistryDirectory.ReferenceConfigurationListener :Dubbo 2.7 版本新增,订阅当前引用的接口服务的配置。这里需要对于提供者只需要关注自身的动态配置即可,但是对于消费者则需要订阅包括提供者列表 providers,动态配置信息 configurators,路由配置 routers 的信息。
- RegistryService#subscribe : Dubbo 2.6 版本旧逻辑,订阅当前引用的服务的配置,包括提供者列表 providers,动态配置信息 configurators,路由配置 routers 的信息。
注: 对于 路由节点,消费者会在 ListenableRouter#init 和 TagRouter#notify 方法中也会调用 DynamicConfiguration#addListener 来增加监听器进行监听。
RegistryService#subscribe 我们在上面已经提到过,需要注意的是 对于消费者来说RegistryDirectory#subscribe 不仅仅只订阅了configurators,而是订阅了 providers、configurators、routers 节点,当providers、configurators、routers 子节点变化时会通过回调通知RegistryDirectory。
下面我们来看 ConsumerConfigurationListener 和 ReferenceConfigurationListener 的实现。
1. ConsumerConfigurationListener & ReferenceConfigurationListenerConsumerConfigurationListener和 ReferenceConfigurationListener 都是 RegistryDirectory 的内部静态类,其实现基本相同,如下:
private static class ConsumerConfigurationListener extends AbstractConfiguratorListener { Listlisteners = new ArrayList<>(); ConsumerConfigurationListener() { // 应用级别监听 this.initWith(ApplicationModel.getApplication() + Constants.CONFIGURATORS_SUFFIX); } // 添加监听器 void addNotifyListener(RegistryDirectory listener) { this.listeners.add(listener); } @Override protected void notifyOverrides() { // 当消费者的应用级别配置更新时会遍历所有监听器进行配置刷新 // 即触发 RegistryDirectory#refreshInvoker 来刷新本地 listeners.forEach(listener -> listener.refreshInvoker(Collections.emptyList())); } } private static class ReferenceConfigurationListener extends AbstractConfiguratorListener { private RegistryDirectory directory; private URL url; ReferenceConfigurationListener(RegistryDirectory directory, URL url) { this.directory = directory; this.url = url; // 服务级别监听 this.initWith(url.getEncodedServiceKey() + Constants.CONFIGURATORS_SUFFIX); } @Override protected void notifyOverrides() { // to notify configurator/router changes // 刷新引用的服务配置 // 触发 RegistryDirectory#refreshInvoker 来刷新本地 directory.refreshInvoker(Collections.emptyList()); } }
当动态配置更新后,会触发 RegistryDirectory#refreshInvoker 来刷新本地配置,按照如下流程调用后 来到了RegistryDirectory#mergeUr 方法中。
RegistryDirectory#refreshInvoker -> RegistryDirectory#toInvokers -> RegistryDirectory#mergeUrl1.1 RegistryDirectory#mergeUrl
在 RegistryDirectory#mergeUrl 方法中 Dubbo 完成了配置合并,合并的优先级是
动态配置 > Jvm 本地配置 > 消费者端配置(xml > Properties) > 提供者端配合
下面我们来看具体实现:
private URL mergeUrl(URL providerUrl) { // providerUrl 是 提供者端, queryMap 是消费者端的参数 // 1. 合并消费者端的参数。这一步会使用消费者参数覆盖提供者参数 providerUrl = ClusterUtils.mergeUrl(providerUrl, queryMap); // Merge the consumer side parameters // 2. 动态配置的参数合并 providerUrl = overrideWithConfigurator(providerUrl); providerUrl = providerUrl.addParameter(Constants.CHECK_KEY, String.valueOf(false)); // Do not check whether the connection is successful or not, always create Invoker! // The combination of directoryUrl and override is at the end of notify, which can't be handled here this.overrideDirectoryUrl = this.overrideDirectoryUrl.addParametersIfAbsent(providerUrl.getParameters()); // Merge the provider side parameters if ((providerUrl.getPath() == null || providerUrl.getPath() .length() == 0) && "dubbo".equals(providerUrl.getProtocol())) { // Compatible version 1.0 //fix by tony.chenl DUBBO-44 String path = directoryUrl.getParameter(Constants.INTERFACE_KEY); if (path != null) { int i = path.indexOf('/'); if (i >= 0) { path = path.substring(i + 1); } i = path.lastIndexOf(':'); if (i >= 0) { path = path.substring(0, i); } providerUrl = providerUrl.setPath(path); } } return providerUrl; } // 2. 覆盖参数配置 private URL overrideWithConfigurator(URL providerUrl) { // override url with configurator from "override://" URL for dubbo 2.6 and before // 2.1 Dubbo 2.6 及以前的版本的覆盖策略 providerUrl = overrideWithConfigurators(this.configurators, providerUrl); // override url with configurator from configurator from "app-name.configurators" // 2.2 Dubbo 2.7 版本,通过 consumerConfigurationListener 监听动态消费者应用级别配置,在此进行配置合并 providerUrl = overrideWithConfigurators(consumerConfigurationListener.getConfigurators(), providerUrl); // override url with configurator from configurators from "service-name.configurators" // 2.3 提供者服务级别配置合并 if (serviceConfigurationListener != null) { providerUrl = overrideWithConfigurators(serviceConfigurationListener.getConfigurators(), providerUrl); } return providerUrl; }
我们这里假设 消费者端 本地的原始配置为 URL0,则 :
- 在消费者启动时,会加载 JVM 配置,并覆盖本地配置 URL0。RegistryDirectory 在初始化时会将合并后的消费者配置保存到 RegistryDirectory#queryMap 中。这一步完成了 Jvm 配置 覆盖 消费者端本地配置 ,诞生了合并后的新配置 URL1。
- 在上面的 RegistryDirectory#mergeUrl 的代码中, ClusterUtils.mergeUrl(providerUrl, queryMap); 会使用 queryMap 中的配置覆盖providerUrl 中的配置,而 queryMap 中保存的是URL1,providerUrl 为从注册中心拉取的提供者的配置。这一步完成了 URL1 覆盖提供者的配置,诞生了合并后的新配置 URL2.
- 在随后的 RegistryDirectory#overrideWithConfigurator 方法中,会拉取动态配置并覆盖 URL2 的配置,这一步完成了动态配置 覆盖 URL2,诞生了合并后的最终配合 URL3。
综合上面的过程得到优先级的合并规则 :
动态配置 > Jvm 本地配置 > 消费者端配置(xml > Properties) > 提供者端配合
以上:内容部分参考
《深度剖析Apache Dubbo 核心技术内幕》
https://dubbo.apache.org/zh/docs/v2.7/dev/source/
如有侵扰,联系删除。 内容仅用于自我记录学习使用。如有错误,欢迎指正
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)