Dubbo 3.x源码(23)—Dubbo服务引用源码(6)MigrationRuleListener迁移规则监听器

基于Dubbo 3.1,详细介绍了Dubbo服务的发布与引用的源码。

此前我们学习了Dubbo3.1版本的服务引入的总体流程,以及Dubbo服务引用bean的获取以及懒加载原理。当然真正的服务远程引入、以及配置迁移啥的都还没讲,MigrationRuleListener#onRefer方法,该方法才是真正的服务引入入口,MigrationRuleListener以及真正的服务引入的逻辑,我们后面学习现在来学习。

Dubbo 3.x服务引用源码:

  1. Dubbo 3.x源码(11)—Dubbo服务的发布与引用的入口
  2. Dubbo 3.x源码(18)—Dubbo服务引用源码(1)
  3. Dubbo 3.x源码(19)—Dubbo服务引用源码(2)
  4. Dubbo 3.x源码(20)—Dubbo服务引用源码(3)
  5. Dubbo 3.x源码(21)—Dubbo服务引用源码(4)
  6. Dubbo 3.x源码(22)—Dubbo服务引用源码(5)服务引用bean的获取以及懒加载原理
  7. Dubbo 3.x源码(23)—Dubbo服务引用源码(6)MigrationRuleListener迁移规则监听器

Dubbo 3.x服务发布源码:

  1. Dubbo 3.x源码(11)—Dubbo服务的发布与引用的入口
  2. Dubbo 3.x源码(12)—Dubbo服务发布导出源码(1)
  3. Dubbo 3.x源码(13)—Dubbo服务发布导出源码(2)
  4. Dubbo 3.x源码(14)—Dubbo服务发布导出源码(3)
  5. Dubbo 3.x源码(15)—Dubbo服务发布导出源码(4)
  6. Dubbo 3.x源码(16)—Dubbo服务发布导出源码(5)
  7. Dubbo 3.x源码(17)—Dubbo服务发布导出源码(6)

文章目录

  • 1 MigrationRuleListener迁移规则监听器
  • 2 MigrationRuleListener构造器
    • 2.1 setRawRule设置原始规则
    • 2.2 MigrationRule迁移规则
  • 3 onRefer引用服务时迁移
    • 3.1 MigrationRuleHandler#doMigrate执行迁移
  • 4 refreshInvoker刷新invoker
    • 4.1 migrateToApplicationFirstInvoker应用级优先
      • 4.1.1 MigrationInvoker接口级服务发现
        • 4.1.1.1 calcPreferredInvoker计算首选调用者
      • 4.1.2 ServiceDiscoveryMigrationInvoker应用级服务发现
    • 4.2 migrateToForceApplicationInvoker强制应用级
    • 4.3 migrateToForceInterfaceInvoker强制接口级
      • 4.3.1 MigrationInvoker接口级服务发现
      • 4.3.2 ServiceDiscoveryMigrationInvoker应用级服务发现
  • 5 总结

1 MigrationRuleListener迁移规则监听器

当前版本可用的监听器仅一个MigrationRuleListener,它用于通过动态更改规则来控制迁移行为。MigrationRuleListener的onrefer方法是Dubbo2.x 接口级服务发现与Dubbo3.x应用级服务发现之间迁移的关键。

MigrationRuleListener有两个执行点:

  1. refer的时候,consumer行为由默认规则决定。在启动consumer执行refer服务引用的时候会根据默认本地规则对invoker执行的迁移。
  2. 规则更改的时候,根据新接收的规则更改invoker行为。MigrationRuleListener还会从Dubbo配置中心监听动态迁移规则,如果监听到数据更改,还会对所有服务invoker(onRefer的时候会缓存每个invoker)进行新规则迁移。

迁移规则属于consumer应用程序范围。监听器在所有invoker(接口)之间共享,它维持了接口和handler之间的关系。

2 MigrationRuleListener构造器

我们来看看MigrationRuleListener的构造器。构造器中的主要逻辑有两步:

  1. 从远程配置中心加载规则配置文件,用作初始化规则。并且监听配置中心指定ruleKey的迁移规则,group为DUBBO_SERVICEDISCOVERY_MIGRATION,当规则改变时动态更新规则。
  2. 加载本地规则文件,进行延迟初始化(默认延迟60s)。本地文件配置方式本质上是一个延时配置通知的方式,本地文件不会影响默认启动方式,当达到延时时间后触发推送一条内容和本地文件一致的通知。这里的延时时间与规则体中的 delay 字段无关联。 本地文件配置方式可以保证启动以默认行为初始化,当达到延时时触发迁移操作,执行对应的检查,避免启动时就以终态方式启动。
public MigrationRuleListener(ModuleModel moduleModel) {this.moduleModel = moduleModel;//初始化init();
}private void init() {//规则key     消费者应用名 + “.migration”this.ruleKey = moduleModel.getApplicationModel().getApplicationName() + ".migration";//获取Dubbo配置中心this.configuration = moduleModel.getModelEnvironment().getDynamicConfiguration().orElse(null);/** 1 远程配置中心配置文件加载,用作初始化规则*/if (this.configuration != null) {logger.info("Listening for migration rules on dataId " + ruleKey + ", group " + DUBBO_SERVICEDISCOVERY_MIGRATION);//监听配置中心指定ruleKey的迁移规则,group为DUBBO_SERVICEDISCOVERY_MIGRATIONconfiguration.addListener(ruleKey, DUBBO_SERVICEDISCOVERY_MIGRATION, this);//获取配置中心group为DUBBO_SERVICEDISCOVERY_MIGRATION,key为ruleKey的动态迁移规则String rawRule = configuration.getConfig(ruleKey, DUBBO_SERVICEDISCOVERY_MIGRATION);//如果是空的,那么设置为字符串INIT,即默认规则if (StringUtils.isEmpty(rawRule)) {rawRule = INIT;}//设置规则字符串给rawRule字段//解析规则字符串为MigrationRule赋给rule字段setRawRule(rawRule);} else {//使用默认配置规则,因为没有配置配置中心if (logger.isWarnEnabled()) {logger.warn("Using default configuration rule because config center is not configured!");}//设置规则字符串给rawRule字段//解析规则字符串为MigrationRule赋给rule字setRawRule(INIT);}/** 2 本地规则文件延迟初始化*//** 获取本地原始规则* 首先获取JVM参数或者环境变量中指定的dubbo.migration.file,如果用户未指定则尝试加载类路径下的dubbo-migration.yaml文件,默认null*/String localRawRule = moduleModel.getModelEnvironment().getLocalMigrationRule();if (!StringUtils.isEmpty(localRawRule)) {//通过调度任务线程池,延迟的进行通知//本地文件配置方式本质上是一个延时配置通知的方式,本地文件不会影响默认启动方式,当达到延时时间后触发推送一条内容和本地文件一致的通知。//这里的延时时间与规则体中的 delay 字段无关联。 本地文件配置方式可以保证启动以默认行为初始化,当达到延时时触发迁移操作,执行对应的检查,避免启动时就以终态方式启动localRuleMigrationFuture = moduleModel.getApplicationModel().getFrameworkModel().getBeanFactory().getBean(FrameworkExecutorRepository.class).getSharedScheduledExecutor().schedule(() -> {if (this.rawRule.equals(INIT)) {this.process(new ConfigChangedEvent(null, null, localRawRule));}}, getDelay(), TimeUnit.MILLISECONDS);}
}

2.1 setRawRule设置原始规则

在MigrationRuleListener构造器中,将会设置原始迁移规则,原始迁移规则首先尝试从配置中心获取。如果没有配置中心获取没有配置规则,那么设置原始规则为字符串INIT,随后调用setRawRule初始化原始规则。

setRawRule方法会尝试解析原始规则字符串成为MigrationRule规则对象,这里的解析实际上就是对yaml字符串的解析,解析成为map,然后设置给MigrationRule对应的属性即可。

这里的parsseRule方法有些小问题,首先会判断如果rule==null,那么会调用MigrationRule.getInitRule()方法,该方法会创建一个MigrationRule实例,然后在下面的步骤中,判断如果INIT.equals(rawRule),那么还会调用MigrationRule.getInitRule()方法继续创建一个MigrationRule实例,但最终只会返回一个MigrationRule,因此会创建多余的MigrationRule对象,本人觉得这是没有必要的。

/*** 设置原始规则* @param rawRule 原始规则字符串*/
public void setRawRule(String rawRule) {this.rawRule = rawRule;this.rule = parseRule(this.rawRule);
}/*** 解析规则* @param rawRule 原始规则字符串* @return 迁移规则对象*/
private MigrationRule parseRule(String rawRule) {//如果还没有规则,那么初始化一个默认规则,创建一个MigrationRuleMigrationRule tmpRule = rule == null ? MigrationRule.getInitRule() : rule;//如果原始规则字符串为INIT,即没有规则if (INIT.equals(rawRule)) {//那么初始化一个默认规则,创建一个MigrationRuletmpRule = MigrationRule.getInitRule();}//否则,else {try {//解析原始规则字符串成为规则对象tmpRule = MigrationRule.parse(rawRule);} catch (Exception e) {logger.error("Failed to parse migration rule...", e);}}return tmpRule;
}

2.2 MigrationRule迁移规则

目前存在三种迁移状态:FORCE_INTERFACE(强制接口级),APPLICATION_FIRST(应用级优先)、FORCE_APPLICATION(强制应用级)。

  1. FORCE_INTERFACE:只启用兼容模式下接口级服务发现的注册中心逻辑,调用流量 100% 走原有流程。
  2. APPLICATION_FIRST:开启接口级、应用级双订阅,运行时根据阈值和灰度流量比例动态决定调用流量走向。
  3. FORCE_APPLICATION:只启用新模式下应用级服务发现的注册中心逻辑,调用流量 100% 走应用级订阅的地址。

但实际上,还有更详细的规则配置,规则采用 yaml 格式配置,具体配置下参考如下:

key: 消费者应用名(必填)
step: 状态名(必填)
threshold: 决策阈值(默认1.0)
proportion: 灰度比例(默认100)
delay: 延迟决策时间(默认0)
force: 强制切换(默认 false)
interfaces: 接口粒度配置(可选)- serviceKey: 接口名(接口 + : + 版本号)(必填)threshold: 决策阈值proportion: 灰度比例delay: 延迟决策时间force: 强制切换step: 状态名(必填)- serviceKey: 接口名(接口 + : + 版本号)step: 状态名
applications: 应用粒度配置(可选)- serviceKey: 应用名(消费的上游应用名)(必填)threshold: 决策阈值proportion: 灰度比例delay: 延迟决策时间force: 强制切换step: 状态名(必填)

详细说明:

  1. key: 消费者应用名
  2. step: 状态名(FORCE_INTERFACE、APPLICATION_FIRST、FORCE_APPLICATION)
  3. threshold: 决策阈值(浮点数,具体含义参考后文)
  4. proportion: 灰度比例(0~100,决定调用次数比例)
  5. delay: 延迟决策时间(延迟决策的时间,实际等待时间为 1~2 倍 delay 时间,取决于注册中心第一次通知的时间,对于目前 Dubbo 的注册中心实现次配置项保留 0 即可)
  6. force: 强制切换(对于 FORCE_INTERFACE、FORCE_APPLICATION 是否不考虑决策直接切换,可能导致无地址调用失败问题)
  7. interfaces: 接口粒度配置

参考配置示例如下:

key: demo-consumer
step: APPLICATION_FIRST
threshold: 1.0
proportion: 60
delay: 0
force: false
interfaces:- serviceKey: DemoService:1.0.0threshold: 0.5proportion: 30delay: 0force: truestep: APPLICATION_FIRST- serviceKey: GreetingService:1.0.0step: FORCE_APPLICATION

配置规则如下:

  1. 配置中心配置文件下发(推荐)
    1. Key: 消费者应用名 + “.migration”
    2. Group: DUBBO_SERVICEDISCOVERY_MIGRATION
  2. 启动参数配置
    1. 可通过环境变量或者配置中心配置dubbo.application.service-discovery.migration参数设置,允许值范围:FORCE_INTERFACE、APPLICATION_FIRST、FORCE_APPLICATION。
  3. 本地文件配置
    1. 本地配置文件路径配置项dubbo.migration.file ,默认值dubbo-migration.yaml
    2. 配置文件延迟生效时间(毫秒)配置项dubbo.application.migration.delay,默认值60000

详细规则参见文档:https://dubbo.apache.org/zh/docs/v3.0/advanced/migration-invoker/

3 onRefer引用服务时迁移

现在我们看的onRefer方法就是第一个执行点,即服务引用的时候的迁移,大概步骤为:

  1. 尝试将当前MigrationInvoker以及对应的MigrationRuleHandler存入handlers缓存,并返回migrationRuleHandler,如果已存在缓存则直接返回migrationRuleHandler。handlers缓存用于后续再次更新迁移规则。
  2. 调用migrationRuleHandler.doMigrate(rule),根据本地默认规则执行迁移。
/*** MigrationRuleListener的方法* <p>* 当订阅服务时执行** @param registryProtocol RegistryProtocol instance* @param invoker          MigrationInvoker* @param consumerUrl      表示当前接口及其配置的使用者url* @param registryURL      注册中心协议url*/
@Override
public void onRefer(RegistryProtocol registryProtocol, ClusterInvoker<?> invoker, URL consumerUrl, URL registryURL) {//尝试将当前MigrationInvoker以及对应的MigrationRuleHandler存入handlers缓存,并返回migrationRuleHandler,如果已存在缓存则直接返回migrationRuleHandlerMigrationRuleHandler<?> migrationRuleHandler = handlers.computeIfAbsent((MigrationInvoker<?>) invoker, _key -> {//如果当前MigrationInvoker不在map中//设置invoker的migrationRuleListener属性((MigrationInvoker<?>) invoker).setMigrationRuleListener(this);//返回根据url和表示当前接口及其配置的使用者url构建MigrationRuleHandlerreturn new MigrationRuleHandler<>((MigrationInvoker<?>) invoker, consumerUrl);});/** 根据本地默认规则执行迁移*/migrationRuleHandler.doMigrate(rule);
}

3.1 MigrationRuleHandler#doMigrate执行迁移

该方法执行迁移规则,默认规则为APPLICATION_FIRST(应用级优先)。当然,也会从配置中心和环境变量中读取dubbo.application.service-discovery.migration配置。

  1. 如果invoker属于ServiceDiscoveryMigrationInvoker,表示当前采用应用级别的服务引用协议,那么采用 FORCE_APPLICATION,强制应用级调用refreshInvoker方法刷新invoker。这种需要设置注册中心url参数registry-type=service,而默认情况下invoker属于MigrationInvoker,表示当前采用接口级别的服务引用协议,因此会走下面的逻辑。
  2. 默认的迁移状态 APPLICATION_FIRST(应用级优先)。当然,也会从配置中心读取配置和从环境变量中读取dubbo.application.service-discovery.migration配置。随后获取threshold,可通过配置中心或者migration.threshold参数指定,默认threshold则是-1。
  3. 根据迁移状态和阈值以及rule调用refreshInvoker方法刷新invoker,这里的rule参数实际上是MigrationRuleListener初始化的rule。
/*** MigrationRuleHandler的方法* 执行迁移** @param rule*/
public synchronized void doMigrate(MigrationRule rule) {//如果invoker属于ServiceDiscoveryMigrationInvoker,表示当前采用应用级别的服务引用协议,这种需要设置注册中心url参数registry-type=service//默认情况下,invoker属于MigrationInvoker,表示当前采用接口级别的服务引用协议if (migrationInvoker instanceof ServiceDiscoveryMigrationInvoker) {//在手动启动了应用级别服务注册发现模式下,比如注册中心url添加了registry-type=service参数//那么采用 FORCE_APPLICATION,强制应用级refreshInvoker(MigrationStep.FORCE_APPLICATION, 1.0f, rule);return;}// initial step : APPLICATION_FIRST//默认的迁移状态 APPLICATION_FIRST(应用级优先)MigrationStep step = MigrationStep.APPLICATION_FIRST;float threshold = -1f;try {//获取配置的类型,默认APPLICATION_FIRSTstep = rule.getStep(consumerURL);//获取migration.threshold阈值,默认1.0threshold = rule.getThreshold(consumerURL);} catch (Exception e) {logger.error("Failed to get step and threshold info from rule: " + rule, e);}/** 刷新invoker*/if (refreshInvoker(step, threshold, rule)) {// 刷新成功更新rulesetMigrationRule(rule);}
}

4 refreshInvoker刷新invoker

该方法会判断新的规则和此前的规则如果不一致,那么执行迁移规则,切换状态,根据不同的迁移状态调用不同的方法,默认APPLICATION_FIRST,即应用级优先。

  1. APPLICATION_FIRST,调用migrationInvoker.migrateToApplicationFirstInvoker(newRule)方法。
  2. FORCE_APPLICATION,调用migrationInvoker.migrateToForceApplicationInvoker(newRule)方法。
  3. FORCE_INTERFACE,调用migrationInvoker.migrateToForceInterfaceInvoker(newRule)方法。
  4. 如果迁移成功,那么将参数设置为currentStep和currentThreshold。

接口级MigrationInvoker和应用级ServiceDiscoveryMigrationInvoker对于这三个方法的实现不同。我们后面单独讲解。

/*** MigrationRuleHandler的方法** 刷新invoker** @param step      迁移状态* @param threshold 阈值* @param newRule   新迁移规则* @return 是否迁移成功*/
private boolean refreshInvoker(MigrationStep step, Float threshold, MigrationRule newRule) {if (step == null || threshold == null) {throw new IllegalStateException("Step or threshold of migration rule cannot be null");}MigrationStep originStep = currentStep;//如果和之前的规则不同,则执行迁移默认情况下初次启动是currentStep为null,currentThreshold为0.0if ((currentStep == null || currentStep != step) || !currentThreshold.equals(threshold)) {boolean success = true;//根据迁移状态执行迁移switch (step) {//应用级优先,默认规则case APPLICATION_FIRST:migrationInvoker.migrateToApplicationFirstInvoker(newRule);break;//强制应用级case FORCE_APPLICATION:success = migrationInvoker.migrateToForceApplicationInvoker(newRule);break;//强制接口级case FORCE_INTERFACE:default:success = migrationInvoker.migrateToForceInterfaceInvoker(newRule);}//如果迁移成功,那么设置为currentStep和currentThresholdif (success) {setCurrentStepAndThreshold(step, threshold);logger.info("Succeed Migrated to " + step + " mode. Service Name: " + consumerURL.getDisplayServiceKey());report(step, originStep, "true");} else {// migrate failed, do not save new step and rulelogger.warn("Migrate to " + step + " mode failed. Probably not satisfy the threshold you set "+ threshold + ". Please try re-publish configuration if you still after check.");report(step, originStep, "false");}return success;}// ignore if step is same with previous, will continue override rule for MigrationInvokerreturn true;
}

4.1 migrateToApplicationFirstInvoker应用级优先

4.1.1 MigrationInvoker接口级服务发现

接口级服务发现MigrationInvoker(默认invoker)如果采用应用级优先策略(默认情况),那么将会调用MigrationInvoker#migrateToApplicationFirstInvoker方法进行策略转换迁移。

该方法将会进行接口级别和应用级别的服务双引入,这和此前的服务双导出是对应的,最后会选择首选invoker,引用级别的invoker优先。

/*** MigrationInvoker的方法* <p>* 迁移至应用级优先** @param newRule 新规则*/
@Override
public void migrateToApplicationFirstInvoker(MigrationRule newRule) {CountDownLatch latch = new CountDownLatch(0);/** 刷新接口Invoker* 涉及到消费者注册到注册中心、拉取服务提供者信息转换为invoker,向注册中心订阅服务等操作* 说白了就是接口级别的服务引入。*/refreshInterfaceInvoker(latch);/** 刷新服务发现Invoker* 应用级别的服务引入*/refreshServiceDiscoveryInvoker(latch);/** 计算首选invoker,应用级别的invoker优先*///直接计算首选调用者,不会等到地址通知,当地址稍后通知时,将重新进行计算calcPreferredInvoker(newRule);
}
4.1.1.1 calcPreferredInvoker计算首选调用者
/*** MigrationInvoker的方法* <p>* 计算首选invoker,应用级别的invoker优先** @param migrationRule 迁移规则*/
private synchronized void calcPreferredInvoker(MigrationRule migrationRule) {//如果其中一个应用级invoker或者接口级invoker为null,则说明只有一个能用,那么无需计算首选invoker,直接返回if (serviceDiscoveryInvoker == null || invoker == null) {return;}//迁移比较Set<MigrationAddressComparator> detectors = ScopeModelUtil.getApplicationModel(consumerUrl == null ? null : consumerUrl.getScopeModel()).getExtensionLoader(MigrationAddressComparator.class).getSupportedExtensionInstances();if (CollectionUtils.isNotEmpty(detectors)) {// pick preferred invoker 选择首选的调用程序// the real invoker choice in invocation will be affected by promotion 在调用中真正的调用者选择将受到提升的影响//主要是比较是否有directory,以及新旧服务提供者invoker占比是否大于等于thresholdif (detectors.stream().allMatch(comparator -> comparator.shouldMigrate(serviceDiscoveryInvoker, invoker, migrationRule))) {this.currentAvailableInvoker = serviceDiscoveryInvoker;} else {this.currentAvailableInvoker = invoker;}}
}

4.1.2 ServiceDiscoveryMigrationInvoker应用级服务发现

应用级服务发现ServiceDiscoveryMigrationInvoker(通常需要特别设置,例如设置注册中心url参数registry-type=service)如果采用应用级优先策略,那么将会调用ServiceDiscoveryMigrationInvoker#migrateToApplicationFirstInvoker方法进行策略转换迁移。

该方法将仅会进行应用级别的服务引入,最后会选择引入的invoker作为可用invoker。

/*** ServiceDiscoveryMigrationInvoker的方法* * @param newRule 新规则*/
@Override
public void migrateToApplicationFirstInvoker(MigrationRule newRule) {CountDownLatch latch = new CountDownLatch(0);/** 刷新服务发现Invoker* 应用级别的服务引入*/refreshServiceDiscoveryInvoker(latch);/** 设置当前可用invoker,仅仅是服务发现invoker*/setCurrentAvailableInvoker(getServiceDiscoveryInvoker());
}

4.2 migrateToForceApplicationInvoker强制应用级

接口级服务发现MigrationInvoker和应用级服务发现ServiceDiscoveryMigrationInvoker如果采用强制应用级策略,那么都将会调用MigrationInvoker#migrateToForceApplicationInvoker方法进行策略转换迁移,也就是说他们的实现是一样的代码。

该方法将仅会进行应用级别的服务引入,最后会选择引入的invoker作为可用invoker。

/*** MigrationInvoker的方法** 强制应用级* @param newRule 新规则* @return*/
@Override
public boolean migrateToForceApplicationInvoker(MigrationRule newRule) {CountDownLatch latch = new CountDownLatch(1);/** 刷新服务发现Invoker* 应用级别的服务引入*/refreshServiceDiscoveryInvoker(latch);//invoker不存在,忽略阈值检查if (invoker == null) {// invoker is absent, ignore threshold checkthis.currentAvailableInvoker = serviceDiscoveryInvoker;return true;}// wait and compare threshold//等待并比较阈值waitAddressNotify(newRule, latch);//强制规则if (newRule.getForce(consumerUrl)) {// force migrate, ignore threshold checkthis.currentAvailableInvoker = serviceDiscoveryInvoker;this.destroyInterfaceInvoker();return true;}//迁移比较Set<MigrationAddressComparator> detectors = ScopeModelUtil.getApplicationModel(consumerUrl == null ? null : consumerUrl.getScopeModel()).getExtensionLoader(MigrationAddressComparator.class).getSupportedExtensionInstances();if (CollectionUtils.isNotEmpty(detectors)) {if (detectors.stream().allMatch(comparator -> comparator.shouldMigrate(serviceDiscoveryInvoker, invoker, newRule))) {this.currentAvailableInvoker = serviceDiscoveryInvoker;this.destroyInterfaceInvoker();return true;}}// compare failed, will not change stateif (step == MigrationStep.FORCE_INTERFACE) {destroyServiceDiscoveryInvoker();}return false;
}

4.3 migrateToForceInterfaceInvoker强制接口级

4.3.1 MigrationInvoker接口级服务发现

接口级服务发现MigrationInvoker(默认invoker)如果采用强制接口级,那么将会调用MigrationInvoker#migrateToForceApplicationInvoker方法进行策略转换迁移。

该方法将会进行接口级别的服务引入和迁移。

/*** MigrationInvoker的方法* <p>* 强制接口级** @param newRule 新规则* @return*/
@Override
public boolean migrateToForceInterfaceInvoker(MigrationRule newRule) {CountDownLatch latch = new CountDownLatch(1);/** 刷新接口Invoker* 涉及到消费者注册到注册中心、拉取服务提供者信息转换为invoker,向注册中心订阅服务等操作* 说白了就是接口级别的服务引入。*/refreshInterfaceInvoker(latch);//serviceDiscoveryInvoker不存在,忽略阈值检查if (serviceDiscoveryInvoker == null) {// serviceDiscoveryInvoker is absent, ignore threshold checkthis.currentAvailableInvoker = invoker;return true;}//等待并比较阈值waitAddressNotify(newRule, latch);//强制规则if (newRule.getForce(consumerUrl)) {// force migrate, ignore threshold checkthis.currentAvailableInvoker = invoker;this.destroyServiceDiscoveryInvoker();return true;}//迁移比较Set<MigrationAddressComparator> detectors = ScopeModelUtil.getApplicationModel(consumerUrl == null ? null : consumerUrl.getScopeModel()).getExtensionLoader(MigrationAddressComparator.class).getSupportedExtensionInstances();if (CollectionUtils.isNotEmpty(detectors)) {if (detectors.stream().allMatch(comparator -> comparator.shouldMigrate(invoker, serviceDiscoveryInvoker, newRule))) {this.currentAvailableInvoker = invoker;this.destroyServiceDiscoveryInvoker();return true;}}// compare failed, will not change stateif (step == MigrationStep.FORCE_APPLICATION) {destroyInterfaceInvoker();}return false;
}

4.3.2 ServiceDiscoveryMigrationInvoker应用级服务发现

应用级服务发现ServiceDiscoveryMigrationInvoker(通常需要特别设置,例如设置注册中心url参数registry-type=service)如果采用强制接口级策略,那么将会调用ServiceDiscoveryMigrationInvoker#migrateToForceApplicationInvoker方法进行策略转换迁移。

该方法将仅会进行应用级别的服务引入,最后会选择引入的invoker作为可用invoker。

/*** ServiceDiscoveryMigrationInvoker的方法* * @param newRule 新规则* @return*/
@Override
public boolean migrateToForceInterfaceInvoker(MigrationRule newRule) {CountDownLatch latch = new CountDownLatch(0);/** 刷新服务发现Invoker* 应用级别的服务引入*/refreshServiceDiscoveryInvoker(latch);/** 设置当前可用invoker,仅仅是服务发现invoker*/setCurrentAvailableInvoker(getServiceDiscoveryInvoker());return true;
}

5 总结

本次我们学习了MigrationRuleListener迁移规则监听器,为什么要学习他呢?除了在Dubbo服务2.x升级到3.x的迁移过程中的作用之外,实际上它内部的refreshInterfaceInvoker和refreshServiceDiscoveryInvoker这两个方法还兼具接口和应用级别的服务注册、引入、订阅、更新的功能。

我们目前正在学习的服务引入,所以说下面我们将进去refreshInterfaceInvoker和refreshServiceDiscoveryInvoker这两个方法,去看看接口和应用级别的服务在dubbo3.1中到底是如何引入的。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/29860.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

什么洗地机好用又实惠?四大口碑优品推荐,超级火爆

作为一个家电工作者&#xff0c;近年来测评了不少洗地机&#xff0c;相对于传统的清洁习惯&#xff0c;即先扫地&#xff0c;再拖地&#xff0c;洗地机能够在一拖一拉之间&#xff0c;便完成地面上的清洁&#xff0c;而且人们也不用低头弯腰的去清洁&#xff0c;可谓是省时省力…

【单片机毕业设计选题24013】-基于STM32的城市垃圾分类引导系统

系统功能: 1、系统具有语音识别功能&#xff0c;可以对厨余垃圾、其他垃圾、有害垃圾、可回收垃圾进行语音识别&#xff1b; 2、系统可根据语音识别结果直接开启对应类别的垃圾桶&#xff0c;引导分类投放&#xff1b; 3、系统具有语音播报功能&#xff0c;可以语音播报出识…

使用 swiper 轮播 echarts 图表,地图点击失效

问题 使用 swiper 轮播 echarts 图表&#xff0c;地图点击失效&#xff0c;伪代码如下 <Swiper><SwiperSlide>...</SwiperSlide>// 轮播中有地图<SwiperSlide><EchartsMap/></SwiperSlide><SwiperSlide>...</SwiperSlide> &…

Java学习 - MySQL常用性能优化 + Explain查看执行计划

常用性能优化方式 服务器硬件优化&#xff1a;加机器&#xff0c;加内存MySQL服务器优化&#xff1a;更改参数&#xff0c;增加缓冲等等SQL本身优化&#xff1a;减少子查询&#xff0c;减少连接查询的使用反范式设计优化&#xff1a;为了减少连接查询使用&#xff0c;可以允许…

同三维TT806-1 USB单路网络视频流/U盘采集卡

同三维TT806-1 USB单路网络视频流/U盘采集卡 (1路网络音视频信号或U盘直播推流器) 支持采集1路网络视频流或U盘音视频信号&#xff0c;USB输出到电脑 同时还可流推2个直播平台&#xff0c;可设置6组定时推流&#xff0c;有线网络 可录像到U盘&#xff0c;支持定时录像 一…

05 部署YUM软件仓库

5.1部署YUM软件仓库 5.1.1准备网络安装源 YUM软件仓库通常借助于HTTP或FTP协议来发布&#xff0c;这样可以面向网络中的所有客户机提供软件源服务。 1.准备软件仓库目录 在Center 7 系统的安装光盘中&#xff0c;已针对软件目录Packages建立好repodata数据&#xff0c;因此…

input标签删除文件之后再次上传同一文件无法触发change

HTML<input type"file" accept".pdf" id"upload-btn" />jsdocument.getElementById(upload-btn).addEventListener(change, function (e) {const file e.target.files[0]; }); 修改&#xff1a; //删除文件document.querySelector(#demo…

Web前端仿项目:探索实践之路

Web前端仿项目&#xff1a;探索实践之路 在Web前端领域&#xff0c;仿项目是一种常见且有效的学习方式。通过模仿已有的项目&#xff0c;我们可以深入了解前端技术的实际应用&#xff0c;提升自己的实践能力。然而&#xff0c;如何有效地进行Web前端仿项目&#xff0c;却是一个…

【向量检索】之向量数据库Milvus,Faiss详解及应用案例

Reference https://www.modb.pro/db/509268 笔记︱几款多模态向量检索引擎&#xff1a;Faiss 、milvus、Proxima、vearch、Jina等 - 知乎 (zhihu.com) 向量数据库入坑指南&#xff1a;聊聊来自元宇宙大厂 Meta 的相似度检索技术 Faiss - 苏洋的文章 - 知乎 常用的三种索引方…

shiny实现点击跳转下一个标签栏

在 Shiny 应用中实现点击按钮跳转到下一个标签栏&#xff0c;可以使用 shiny 的内置函数 updateTabsetPanel 来控制标签栏的切换。以下是一个简单的示例代码&#xff0c;演示了如何通过点击按钮来跳转到下一个标签栏&#xff1a; library(shiny)ui <- fluidPage(titlePanel…

fastapi修改docs文档页面favicon.ico图标

如下图&#xff0c;文档页面默认使用的是tiangolo大神的Logo 如果打开的标签比较多&#xff0c;就不好区分了&#xff0c;想要修改这个logo&#xff0c;可以用fastapi-cdn-host一行代码搞定 fastapi_cdn_host.patch_docs(app, favicon_url/static/logo.png) 例如&#xff1a;…

macbook pro 鼠标键 导致键盘失灵

问题 关闭鼠标键之后 所有键盘还是不可用&#xff08;开关机键除外&#xff09; 解决 开机按住commands进入单用户模式exit重启电脑

react-2 jsx的学习

1.什么是JSX&#xff1f; 概念&#xff1a;JSX就是Javascript和XML&#xff08;HTML&#xff09;的缩写&#xff0c;表示在js代码中编写HTML模板结构&#xff0c;它是React中编写UI模板的方式&#xff0c;如下图就是jsx: 优势&#xff1a;1.HTML的声明式模板写法&#xff1b;…

IDEA GIt 提交提示 “Contents are identica“

当前问题的描述 IDEA 中使用 Git 提交代码时候, 会发现出现在变动列表只用, 文件本身是蓝色的, 也代表文件有改变&#xff0c;但比较上一次的版本和本地版本是一致的, 显示 no difference 并且显示 Contents are identica, 即内容无改变, 但是文件还是蓝色的; 此时拉取代码, 如…

铠侠全面复产:NAND价格还会涨吗?

近期&#xff0c;日本经济新闻&#xff08;Nikkei&#xff09;报道指出&#xff0c;经历长达20个月的产能削减后&#xff0c;全球第四大三维NAND闪存制造商铠侠已全面恢复生产。这一转变不仅标志着铠侠再次全力投入到市场份额的争夺中&#xff0c;也可能预示着闪存市场价格即将…

MySQL-DML-约束

079-对表中数据进行增删改 DML语句 当我们对表中的数据进行增删改的时候,称它为DML语句。(数据操纵语言),主要包括:insert、delete、update insert 增 语法格式: insert into 表名(字段名1,字段名2,字段名3,...) values(值1,值2,值3,...);表名后面的小括号当中的字段…

网络流量轮廓

示例场景 假设你是一个公司网络管理员&#xff0c;负责维护和监控公司内部网络的安全和性能。你的任务是确保网络流量正常&#xff0c;没有异常行为&#xff0c;同时优化网络资源分配。 数据采集 你使用网络流量监控工具&#xff08;如Wireshark、NetFlow等&#xff09;来收…

Unity URP下通过相机让部分Render不受后处理渲染

我们有时候不想某些对象受到后处理影响&#xff0c;找到了这样一个决绝办法&#xff0c;通过增加一个Overlay相机只照射这个模型来实现&#xff0c;下面看看如何实现。 第一步 首先我们拖一个测试场景&#xff0c;有如下一些元素 一个盒子&#xff0c;以后后处理&#xff0c…

docker部署dm数据库

官方文档参考 官网地址&#xff1a;https://eco.dameng.com/document/dm/zh-cn/start/dm-install-docker.html 下载镜像地址 docker部署 1、加载镜像 docker load -i dm8_20240613_x86_rh6_64_rq_ent_8.1.3.140_pack5.tar使用docker images&#xff0c;查看镜像和镜像标签…

HuggingFace烧钱做了一大批实验,揭示多模态大模型哪些trick真正有效

构建多模态大模型时有很多有效的trick&#xff0c;如采用交叉注意力机制融合图像信息到语言模型中&#xff0c;或直接将图像隐藏状态序列与文本嵌入序列结合输入至语言模型。 但是这些trick为什么有效&#xff0c;其计算效率如何&#xff0c;往往解释得很粗略或者或者缺乏充分…