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

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中到底是如何引入的。

相关文章:

  • C# —— 字符串的相关属性和方法
  • 事务AOP
  • Java毕业设计 基于SSM助学贷款管理系统
  • 打印mybatis的sql日志
  • 微信小程序,分享和反馈功能
  • C#标志位的使用
  • SpringMVC的使用
  • python写一个ai agent对接仓库管理系统的业务流程
  • 汽车IVI中控开发入门及进阶(二十七):车载摄像头vehicle camera
  • corona渲染器与vray比哪个好?支持云渲染平台吗
  • 部署yum仓库
  • kotlin 中的字符
  • 注意力机制和Transformer模型各部分功能解释
  • WPF框架,修改ComboBox控件背景色 ,为何如此困难?
  • TTL 232难兄难弟对比
  • 2017届校招提前批面试回顾
  • DataBase in Android
  • Docker下部署自己的LNMP工作环境
  • FineReport中如何实现自动滚屏效果
  • iBatis和MyBatis在使用ResultMap对应关系时的区别
  • Linux后台研发超实用命令总结
  • Rancher如何对接Ceph-RBD块存储
  • 安装python包到指定虚拟环境
  • 基于遗传算法的优化问题求解
  • 如何用vue打造一个移动端音乐播放器
  • 算法-图和图算法
  • 想使用 MongoDB ,你应该了解这8个方面!
  • 小程序button引导用户授权
  • 看到一个关于网页设计的文章分享过来!大家看看!
  • “十年磨一剑”--有赞的HBase平台实践和应用之路 ...
  • mysql 慢查询分析工具:pt-query-digest 在mac 上的安装使用 ...
  • Play Store发现SimBad恶意软件,1.5亿Android用户成受害者 ...
  • 不要一棍子打翻所有黑盒模型,其实可以让它们发挥作用 ...
  • (3)医疗图像处理:MRI磁共振成像-快速采集--(杨正汉)
  • (JSP)EL——优化登录界面,获取对象,获取数据
  • (第8天)保姆级 PL/SQL Developer 安装与配置
  • (附源码)springboot美食分享系统 毕业设计 612231
  • (深度全面解析)ChatGPT的重大更新给创业者带来了哪些红利机会
  • (算法)前K大的和
  • (转)Sublime Text3配置Lua运行环境
  • ***详解账号泄露:全球约1亿用户已泄露
  • .net 4.0 A potentially dangerous Request.Form value was detected from the client 的解决方案
  • .NET C# 使用 SetWindowsHookEx 监听鼠标或键盘消息以及此方法的坑
  • .NET 中 GetHashCode 的哈希值有多大概率会相同(哈希碰撞)
  • .Net7 环境安装配置
  • .NET中使用Protobuffer 实现序列化和反序列化
  • @Transactional事务注解内含乾坤?
  • @zabbix数据库历史与趋势数据占用优化(mysql存储查询)
  • [ solr入门 ] - 利用solrJ进行检索
  • [ 代码审计篇 ] 代码审计案例详解(一) SQL注入代码审计案例
  • [2017][note]基于空间交叉相位调制的两个连续波在few layer铋Bi中的全光switch——
  • [AIGC] 解题神器:Python中常用的高级数据结构
  • [Android]一个简单使用Handler做Timer的例子
  • [bzoj4010][HNOI2015]菜肴制作_贪心_拓扑排序
  • [Bzoj4722]由乃(线段树好题)(倍增处理模数小快速幂)