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迁移规则监听器

当前版本可用的监听器仅一个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_MIGRATION
        configuration.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) {
    //如果还没有规则,那么初始化一个默认规则,创建一个MigrationRule
    MigrationRule tmpRule = rule == null ? MigrationRule.getInitRule() : rule;
    //如果原始规则字符串为INIT,即没有规则
    if (INIT.equals(rawRule)) {
        //那么初始化一个默认规则,创建一个MigrationRule
        tmpRule = 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.0
    threshold: 0.5
    proportion: 30
    delay: 0
    force: true
    step: APPLICATION_FIRST
  - serviceKey: GreetingService:1.0.0
    step: 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,如果已存在缓存则直接返回migrationRuleHandler
    MigrationRuleHandler<?> migrationRuleHandler = handlers.computeIfAbsent((MigrationInvoker<?>) invoker, _key -> {
        //如果当前MigrationInvoker不在map中
        //设置invoker的migrationRuleListener属性
        ((MigrationInvoker<?>) invoker).setMigrationRuleListener(this);
        //返回根据url和表示当前接口及其配置的使用者url构建MigrationRuleHandler
        return 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_FIRST
        step = rule.getStep(consumerURL);
        //获取migration.threshold阈值,默认1.0
        threshold = 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)) {
        // 刷新成功更新rule
        setMigrationRule(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.0
    if ((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和currentThreshold
        if (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 rule
            logger.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 MigrationInvoker
    return 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占比是否大于等于threshold
        if (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 check
        this.currentAvailableInvoker = serviceDiscoveryInvoker;
        return true;
    }

    // wait and compare threshold
    //等待并比较阈值
    waitAddressNotify(newRule, latch);
    //强制规则
    if (newRule.getForce(consumerUrl)) {
        // force migrate, ignore threshold check
        this.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 state
    if (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 check
        this.currentAvailableInvoker = invoker;
        return true;
    }

    //等待并比较阈值
    waitAddressNotify(newRule, latch);
    //强制规则
    if (newRule.getForce(consumerUrl)) {
        // force migrate, ignore threshold check
        this.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 state
    if (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中到底是如何引入的。

  • 22
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

刘Java

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值