Dubbo源码篇4-Directort目录与Router路由服务源码分析

欢迎大家关注 github.com/hsfxuebao ,希望对大家有所帮助,要是觉得可以的话麻烦给点一下Star哈

1. 什么是服务路由

服务路由包含一条路由规则,路由规则决定了服务消费者的调用目标,即规定了服务消 费者可调用哪些服务提供者。 Dubbo 目前提供了三种服务路由实现,分别为条件路由 ConditionRouter、 脚本路由 ScriptRouter 和标签路由 TagRouter。其中条件路由是我们最常 使用的。

下面将以条件路由为例来讲解服务路由的用法。

2. 路由规则的设置

2.1 路由规则的设置平台

我们通过 Dubbo 管控平台为某服务/应用设置的路由规则,其不仅会设置到注册中心该 服务下,同时也会设置到配置中心该服务下(如果搭建了配置中心的话)。

当然,我们也可以直接通过配置中心的 GUI 管理端来设置和修改路由规则。例如,通过 Apollo 可视化平台来设置 Apollo 中的动态配置,通过 ZooInspector 来设置 Zookeeper 中的动 态配置等。

不过,通过配置中心管理端所进行的修改,是不会修改到注册中心中的路由规则的。所 以,在设置了配置中心后, 配置中心中的动态配置优先级是高于注册中心中的

2.2 Dubbo 管控平台设置路由规则

我们可以像下面这里创建条件路由规则。但生产中使用最多的是黑白名单,所以,在管 控平台中给出了黑白名称的直接设置界面。不过,通过直接的黑白名单设置与通过条件路由 设置,只设置一种即可,且在当前版本中,两种设置均可用。

2.2.1 设置黑白名单

image.png

上例中设置了黑名单,即 IP 为 192.168.124.7 的消费者是无法调用任意提供者的。

2.2.2 设置条件路由

也可以通过在“条件路由”中的配置来设置“黑白名单”。 image.png

3. 路由规则详解

3.1 初识路由规则

应用粒度路由规则:

scope: application
force: true
runtime: true
enabled: true
key: governance-conditionrouter-consumer
conditions:
# app1 的消费者只能消费所有端口为 20880 的服务提供实例
- application=app1 => address=*:20880
# app2 的消费者只能消费所有端口为 20881 的服务提供实例
- application=app2 => address=*:20881

服务粒度路由规则:

scope: service
force: true
runtime: true
enabled: true
key: org.apache.dubbo.samples.governance.api.DemoService
conditions:
# DemoService 的 sayHello 方法只能消费所有端口为 20880 的服务提供者实例
- method=sayHello => address=*:20880
# DemoService 的 sayHi 方法只能消费所有端口为 20881 的服务提供者实例
- method=sayHi => address=*:20881

3.2 属性详解

scope

必填项。表示路由规则的作用粒度, 其取值将会决定 key 的取值。 其取值范围如下:

  • service:表示服务粒度
  • application:表示应用粒度

Key

必填项。 指定规则体将作用于哪个服务或应用。 其取值取决于 scope 的取值。

  • scope 取值为 application 时, key 取值为 application 名称,即<dubbo:application name=””/>的值。
  • scope 取值为 service 时, key 取值为[group:]service[:version]的组合,即组、接口名称与版本号。

enabled

可选项。指定当前路由规则是否立即生效。 缺省值为 true,表示立即生效。

force

可选项。指定当路由结果为空时,是否强制执行。 如果不强制执行,路由结果为空则路 由规则自动失效,不使用路由。 缺省为 false,不强制执行。

runtime

可选项。指定是否在每次调用时执行路由规则。

  • 若为 false 则表示只在提供者地址列表变更时会预先执行路由规则,并将路由结果进行缓存, 消费者调用时直接从缓存中获取路由结果。

  • 若为 true 则表示每次调用都要重新计算路由规则,其将会直接影响调用的性能。 缺省为 false。

priority

可选项。 用于设置路由规则的优先级, 数字越大,优先级越高, 越靠前执行。 缺省为 0。

3.3 规则体 conditions

必填项。定义具体的路由规则内容, 由 1 到任意多条规则组成。

3.3.1 格式

路由规则由两个条件组成,分别用于对服务消费者和提供者进行匹配。 [服务消费者匹配条件] => [服务提供者匹配条件]

  • 当消费者的 URL 满足匹配条件时,对该消费者执行后面的过滤规则。

  • => 之后为提供者地址列表的过滤条件,所有参数和提供者的 URL 进行对比,消费者最终只拿到过滤后的地址列表。

    例如:路由规则为: host = 10.20.153.10 => host = 10.20.153.11。其表示 IP 为 10.20.153.10的服务消费者只可调用 IP 为 10.20.153.11 机器上的服务,不可调用其他机器上的服务。

不过,这两个条件存在缺省的情况:

  • 服务消费者匹配条件为空,表示不对服务消费者进行限制,所有消费者均将被路由到行后面的提供者。

  • 服务提供者匹配条件为空,表示对符合消费者条件的消费者将禁止调用任何提供者。

    例如:路由规则为: => host != 10.20.153.11,则表示所有消费者均可调用IP为10.20.153.11之外的所有提供者。 再如路由规则为: host = 10.20.153.10 => ,则表示 IP 为 10.20.153.10 的提供者不能调用任何提供者。

3.3.2 符号支持

  • 参数符号:

    • method:将调用方法作为路由规则比较的对象
    • argument:将调用方法参数作为路由规则比较的对象
    • protocol:将调用协议作为路由规则比较的对象
    • host:将 IP 作为路由规则比较的对象
    • port:将端口号作为路由规则比较的对象
    • address:将 IP:端口号作为路由规则比较的对象
    • application:将应用名称作为路由规则比较的对象
  • 条件符号:

    • 等于号(=):将参数类型匹配上参数值作为路由条件
    • 不等号(!=):将参数类型不匹配参数值作为路由条件
  • 取值符号:

    • 逗号(,):多个取值的分隔符, 如: host != 10.20.153.10,10.20.153.11
    • 星号():通配符, 如: host != 10.20.
    • 美元符号( ) :表示引用消费者的参数值,如: h o s t = ):表示引用消费者的参数值, 如: host = host

3.3.3 举例

  • 黑名单
    • host = 10.20.153.10,10.20.153.11 =>
    • IP 为 10.20.153.10 与 10.20.153.11 的主机将被禁用。
  • 白名单
    • host != 10.20.153.10,10.20.153.11 =>
    • 禁用 IP 为 10.20.153.10 与 10.20.153.11 之外的所有主机。
  • 只暴露一部分的提供者
    • => host = 172.22.3.1*,172.22.3.2*
    • 消费者只可访问 IP 为 172.22.3.1与 172.22.3.2的提供者主机。
  • 为重要应用提供额外的机器
    • application != kylin => host != 172.22.3.95,172.22.3.96
    • 应用名称不为 kylin 的应用不能访问 172.22.3.95 与 172.22.3.96 两台提供者主机。即只有名称为 kylin 的消费者可以访问 172.22.3.95 与 172.22.3.96 两台提供者主机。当然, kylin 还可以访问其它提供者主机,而其它消费者也可以访问 172.22.3.95 与 172.22.3.96 之外的所有提供者主机。
  • 读写分离
    • method = find*,list*,get*,is* => host = 172.22.3.94,172.22.3.95,172.22.3.96
    • method != find*,list*,get*,is* => host = 172.22.3.97,172.22.3.98
    • find、 list、 get、 is 开头的消费者方法会被路由到 172.22.3.94、 172.22.3.95 与 172.22.3.96 三台提供者主机,而其它方法则会被路由到 172.22.3.97 与 172.22.3.98 两台提供者主机。
  • 前后台分离
    • application = bops => host = 172.22.3.91,172.22.3.92,172.22.3.93
    • application != bops => host = 172.22.3.94,172.22.3.95,172.22.3.96
    • 应用名称的 bops 的消费者会被路由到 172.22.3.91、 172.22.3.92 与 172.22.3.93 三台提供者主机,而其它消费者则会被路由到 172.22.3.94、 172.22.3.95 与 172.22.3.96 三台提供者。
  • 隔离不同机房网段
    • host != 172.22.3.* => host != 172.22.3.*
    • 不是 172.22.3 网段的消费者是不能访问 172.22.3 网段的提供者的。即只有 172.22.3 网段的消费者才可访问 172.22.3 网段的提供者。当然, 172.22.3 网段的消费者也可访问其它网段的提供者。
  • 只访问本机的服务
    • => host = $host
    • $host 表示获取消费者请求中的消费者主机 IP。故该规则就表示消费者只能访问本机的服务

4. Directory目录

Directory 代表了多个 invoker (对于消费端来说,每个 invoker 代表了一个服务提供者),其 内部维护着一个 List,并且这个 List 的内容是动态变化的, 比如, 当服务提供者集群新增 或者减少机器时,服务注册中心就会推送当前服务提供者的地址列表,然后 Directory 中 的 List 就会根据服务提供者地址列表相应变化 。

在 Dubbo 中, 接口 Directory 的实现有 RegistryDirectory 和 StaticDirectory 两种,其中 前者管理的 invoker 列表是根据服务注册中心的推送变化而变化的,而后者是当消费端使用了多注册中心时,其把所有服务注册中心的 invoker 列表汇集到一个 invoker 列表中。

4. 源码追踪

4.1 添加激活 RouterFactory 创建的 Router 到 Directory

4.1.1 整体调用图

image.png

4.1.2 源码分析

RegistryProtocol.doCreateInvoker():

protected <T> ClusterInvoker<T> doCreateInvoker(DynamicDirectory<T> directory, Cluster cluster, Registry registry, Class<T> type) {
   ...
    // 将所有RouterFactory激活扩展类创建的router添加到directory
    directory.buildRouterChain(urlToRegistry);
    // 服务订阅
    directory.subscribe(toSubscribeUrl(urlToRegistry));
    // 将多个invoker伪装为一个具有复合功能的invoker
    return (ClusterInvoker<T>) cluster.join(directory);
}

接下来DynamicDirectory.buildRouterChain():

public void buildRouterChain(URL url) {
    // 创建一个RouterChain,并设置到directory
    this.setRouterChain(RouterChain.buildChain(url));
}

RouterChain.buildChain():

public static <T> RouterChain<T> buildChain(URL url) {
    // 创建RouterChain
    return new RouterChain<>(url);
}

private RouterChain(URL url) {
    loopPool = executorRepository.nextExecutorExecutor();

    // -------------- 对普通路由的处理 --------------------
    // 获取激活RouterFactory实例
    List<RouterFactory> extensionFactories = ExtensionLoader.getExtensionLoader(RouterFactory.class)
        .getActivateExtension(url, ROUTER_KEY);

    // 对所有RouterFactory实例的遍历
    List<Router> routers = extensionFactories.stream()
        // 流中的元素由factory映射为了该factory创建出的router
        .map(factory -> factory.getRouter(url))   
        .collect(Collectors.toList());

    // 将routers写入到routerChain
    initWithRouters(routers);

    // -------------- 对状态路由的处理 --------------------
    List<StateRouterFactory> extensionStateRouterFactories = ExtensionLoader.getExtensionLoader(
        StateRouterFactory.class)
        .getActivateExtension(url, STATE_ROUTER_KEY);

    List<StateRouter> stateRouters = extensionStateRouterFactories.stream()
        .map(factory -> factory.getRouter(url, this))
        .sorted(StateRouter::compareTo)
        .collect(Collectors.toList());

    // init state routers
    initWithStateRouters(stateRouters);
}

4.2 读取配置中心的路由规则

CacheableRouterFactory:

public abstract class CacheableRouterFactory implements RouterFactory {
    private ConcurrentMap<String, Router> routerMap = new ConcurrentHashMap<>();

    @Override
    public Router getRouter(URL url) {
        // 从缓存map中获取当前服务标识key对应的router,
        // 若没有,则创建一个router,放入到缓存map后返回新创建的router
        return routerMap.computeIfAbsent(url.getServiceKey(), k -> createRouter(url));
    }

    protected abstract Router createRouter(URL url);
}

接下来ListenableRouter#ListenableRouter:

public ListenableRouter(URL url, String ruleKey) {
    super(url);
    this.setForce(false);
    // 从配置中心读取路由规则,初始化到这里
    this.init(ruleKey);
}

private synchronized void init(String ruleKey) {
    if (StringUtils.isEmpty(ruleKey)) {
        return;
    }
    // 由服务标识与Router后辍构成router key
    String routerKey = ruleKey + RULE_SUFFIX;
    this.getRuleRepository().addListener(routerKey, this);
    // 从配置中心读取动态路由规则
    String rule = this.getRuleRepository().getRule(routerKey, DynamicConfiguration.DEFAULT_GROUP);
    if (StringUtils.isNotEmpty(rule)) {
        this.process(new ConfigChangedEvent(routerKey, DynamicConfiguration.DEFAULT_GROUP, rule));
    }
}

接下来,直接到ZookeeperDynamicConfiguration#doGetConfig:

protected String doGetConfig(String pathKey) throws Exception {
    // 读取zk节点中指定path的节点内容
    return zkClient.getContent(pathKey);
}

读取配置中心的路由配置,这是刚才我们设备黑名单的在zk中的配置如下: image.png

4.3 读取注册中心的路由规则

image.png 我们直接进入RegistryDirectory.notify():

public synchronized void notify(List<URL> urls) {
    if (isDestroyed()) {
        return;
    }

    Map<String, List<URL>> categoryUrls = urls.stream()
            .filter(Objects::nonNull)  // 过滤掉为null的元素
            .filter(this::isValidCategory)  // 过滤掉不可用分类元素
            .filter(this::isNotCompatibleFor26x)  // 过滤掉不兼容2.6版本的元素
            .collect(Collectors.groupingBy(this::judgeCategory));

    // 处理configurators分类节点
    List<URL> configuratorURLs = categoryUrls.getOrDefault(CONFIGURATORS_CATEGORY, Collections.emptyList());
    this.configurators = Configurator.toConfigurators(configuratorURLs).orElse(this.configurators);

    // 处理routers分类节点
    List<URL> routerURLs = categoryUrls.getOrDefault(ROUTERS_CATEGORY, Collections.emptyList());
    // 读取routers子节点下的路由规则,并添加到directory中
    toRouters(routerURLs).ifPresent(this::addRouters);

    // providers
    // 处理providers分类节点
    List<URL> providerURLs = categoryUrls.getOrDefault(PROVIDERS_CATEGORY, Collections.emptyList());
    ...
    // 刷新invoker
    refreshOverrideAndInvoker(providerURLs);
}

private Optional<List<Router>> toRouters(List<URL> urls) {
    if (urls == null || urls.isEmpty()) {
        return Optional.empty();
    }

    List<Router> routers = new ArrayList<>();
    // 遍历routers子节点下的所有Url
    for (URL url : urls) {
        if (EMPTY_PROTOCOL.equals(url.getProtocol())) {
            continue;
        }
        // 获取router属性值,这里就是condition
        String routerType = url.getParameter(ROUTER_KEY);
        if (routerType != null && routerType.length() > 0) {
            // 将url的protocol由router://...变为condition://...
            url = url.setProtocol(routerType);
        }
        try {
            // 创建相应的router
            Router router = ROUTER_FACTORY.getRouter(url);
            // 添加到routers中
            if (!routers.contains(router)) {
                routers.add(router);
            }
        } catch (Throwable t) {
            logger.error("convert router url to router error, url: " + url, t);
        }
    }

    return Optional.of(routers);
}

这样就可以将 注册中心的 路由配置加载进入了

4.4 RegistryDirectiry 中invoke列表的更新

下面看看 Regist叩Directory 中 invoker 列表的更新流程,如图所示:

image.png

通过图可知,创建完 RegistryDirectory 后,调用了其 subscribe()方法,这里假设使用的服务注册中心为 ZooKeeper,这样就会去 ZooKeeper 订阅 需要调用的服务提供者 的地址列表, 然后添加了 一个监听器,当 ZooKeeper 服务端发现服务提供者地址列表发生变化后,会将地址列表推送到服务消费端,然后 zkClient 会回调该监听器的notify()方法。

在步骤 3 设置完监昕器后,同步返回了订阅的服务地址列表、路由规则、 配置信息等, 然后 同步调用了 RegistrγDirectory 的 notify()方法:

public synchronized void notify(List<URL> urls) {
    if (isDestroyed()) {
        return;
    }

    Map<String, List<URL>> categoryUrls = urls.stream()
            .filter(Objects::nonNull)  // 过滤掉为null的元素
            .filter(this::isValidCategory)  // 过滤掉不可用分类元素
            .filter(this::isNotCompatibleFor26x)  // 过滤掉不兼容2.6版本的元素
            .collect(Collectors.groupingBy(this::judgeCategory));

    // 处理configurators分类节点
    List<URL> configuratorURLs = categoryUrls.getOrDefault(CONFIGURATORS_CATEGORY, Collections.emptyList());
    this.configurators = Configurator.toConfigurators(configuratorURLs).orElse(this.configurators);

    // 处理routers分类节点
    List<URL> routerURLs = categoryUrls.getOrDefault(ROUTERS_CATEGORY, Collections.emptyList());
    // 读取routers子节点下的路由规则,并添加到directory中
    toRouters(routerURLs).ifPresent(this::addRouters);

    // providers
    // 处理providers分类节点
    List<URL> providerURLs = categoryUrls.getOrDefault(PROVIDERS_CATEGORY, Collections.emptyList());
    /**
     * 3.x added for extend URL address
     */
    ExtensionLoader<AddressListener> addressListenerExtensionLoader = ExtensionLoader.getExtensionLoader(AddressListener.class);
    List<AddressListener> supportedListeners = addressListenerExtensionLoader.getActivateExtension(getUrl(), (String[]) null);
    if (supportedListeners != null && !supportedListeners.isEmpty()) {
        for (AddressListener addressListener : supportedListeners) {
            providerURLs = addressListener.notify(providerURLs, getConsumerUrl(),this);
        }
    }
    // 刷新invoker
    refreshOverrideAndInvoker(providerURLs);
}

步骤 6 从 ZooKeeper 返回 的服务提供者 的信息里获取对应的路 由 规则, 并使用步骤 7 保存到 RouterChain 里.

步骤 9 根据服务降级信息 , 重写 URL C 也就是把 mock=retum null 等信息拼接到 URL 中〉并保存到 overrideDirectoryUrl 中,然后执行步骤 10,把服务提供者的 URL 列表转换 为 invoker 列表,并保存到 RouterChain 里:

// 更新本地invoker列表
private void refreshInvoker(List<URL> invokerUrls) {
    Assert.notNull(invokerUrls, "invokerUrls should not be null");

    if (invokerUrls.size() == 1
            && invokerUrls.get(0) != null
            && EMPTY_PROTOCOL.equals(invokerUrls.get(0).getProtocol())) {  // 判断是否有empty://开头
        this.forbidden = true; // Forbid to access  禁止远程调用
        this.invokers = Collections.emptyList();
        routerChain.setInvokers(this.invokers);
        destroyAllInvokers(); // Close all invokers  将缓存中的所有invoker删除
    } else {
        this.forbidden = false; // Allow to access  允许远程访问
        // 缓存map,更新本地invoker列表,就是更新这里
        // key为URL,value为该URL对应的invoker实例(invoker委托对象)
        // 先将缓存map发生变更前的值进行暂存
        Map<URL, Invoker<T>> oldUrlInvokerMap = this.urlInvokerMap; // local reference
        if (invokerUrls == Collections.<URL>emptyList()) {
            invokerUrls = new ArrayList<>();
        }

        // 这段代码可以增加provider的可用性,
        // 如果provider和注册中心网络抖动,导致注册不成功,但是之前注册成功并且更新到本地缓存了,这种情况我们可以直接调用
        if (invokerUrls.isEmpty() && this.cachedInvokerUrls != null) {
            invokerUrls.addAll(this.cachedInvokerUrls);
        } else {
            this.cachedInvokerUrls = new HashSet<>();
            this.cachedInvokerUrls.addAll(invokerUrls);//Cached invoker urls, convenient for comparison
        }

        // 走到这里invokerUrls仍为空,则说明真的是没有任何可用的invoker
        if (invokerUrls.isEmpty()) {
            return;
        }

        // 从缓存map中获取相应的invoker,若存在,则返回,并将其从缓存map中删除
        // 若不存在,则创建一个invoker
        Map<URL, Invoker<T>> newUrlInvokerMap = toInvokers(invokerUrls);// Translate url list to Invoker map

        /**
         * If the calculation is wrong, it is not processed.
         *
         * 1. The protocol configured by the client is inconsistent with the protocol of the server.
         *    eg: consumer protocol = dubbo, provider only has other protocol services(rest).
         * 2. The registration center is not robust and pushes illegal specification data.
         *
         */
        if (CollectionUtils.isEmptyMap(newUrlInvokerMap)) {
            logger.error(new IllegalStateException("urls to invokers error .invokerUrls.size :" + invokerUrls.size() + ", invoker.size :0. urls :" + invokerUrls
                    .toString()));
            return;
        }

        // 获取到最新的可用invoker列表
        List<Invoker<T>> newInvokers = Collections.unmodifiableList(new ArrayList<>(newUrlInvokerMap.values()));
        // pre-route and build cache, notice that route cache should build on original Invoker list.
        // toMergeMethodInvokerMap() will wrap some invokers having different groups, those wrapped invokers not should be routed.
        routerChain.setInvokers(newInvokers);
        this.invokers = multiGroup ? toMergeInvokerList(newInvokers) : newInvokers;
        // 将缓存map更新为新map
        this.urlInvokerMap = newUrlInvokerMap;

        try {
            // 删除老map中剩余的invoker
            destroyUnusedInvokers(oldUrlInvokerMap, newUrlInvokerMap); // Close the unused Invoker
        } catch (Exception e) {
            logger.warn("destroyUnusedInvokers error. ", e);
        }

        // notify invokers refreshed
        this.invokersChanged();
    }
}

4.5 服务路由过滤

image.png image.png

RouterChain 里也保存了可用服务提供者对应的 invokers 列表和路由规则信 息,当服务消费方的集群容错策略要获取可用服务提供者对应的 invoker 列表时,会调用 RouterChain 的 route()方法,其内部根据路由规则信息和 invokers 列表来提供服务,流程 如图所示:

image.png

我们直接看org.apache.dubbo.registry.integration.DynamicDirectory#doList:

public List<Invoker<T>> doList(Invocation invocation) {
    ...
    List<Invoker<T>> invokers = null;
    try {
        // Get invokers from cache, only runtime routers will be executed.
        invokers = routerChain.route(getConsumerUrl(), invocation);  // 进行路由
    } catch (Throwable t) {
        logger.error("Failed to execute router: " + getUrl() + ", cause: " + t.getMessage(), t);
    }

    return invokers == null ? Collections.emptyList() : invokers;
}

org.apache.dubbo.rpc.cluster.RouterChain#route:

public List<Invoker<T>> route(URL url, Invocation invocation) {

    AddrCache<T> cache = this.cache.get();
    ...
    // 获取属性invokers集合值,这个是本地缓存的invoker集合
    BitList<Invoker<T>> finalBitListInvokers = new BitList<>(invokers, false);
    // 处理状态路由
    for (StateRouter stateRouter : stateRouters) {
        if (stateRouter.isEnable()) {
            RouterCache<T> routerCache = cache.getCache().get(stateRouter.getName());
            finalBitListInvokers = stateRouter.route(finalBitListInvokers, routerCache, url, invocation);
        }
    }

    List<Invoker<T>> finalInvokers = new ArrayList<>(finalBitListInvokers.size());

    for(Invoker<T> invoker: finalBitListInvokers) {
        finalInvokers.add(invoker);
    }

    // 处理普通路由
    for (Router router : routers) {
        finalInvokers = router.route(finalInvokers, url, invocation);
    }
    return finalInvokers;
}

我们重点看一下 条件路由的处理:org.apache.dubbo.rpc.cluster.router.condition.ConditionRouter#route:

public <T> List<Invoker<T>> route(List<Invoker<T>> invokers, URL url, Invocation invocation)
        throws RpcException {
    // 若router不可用,则直接返回所有invoker
    if (!enabled) {
        return invokers;
    }

    if (CollectionUtils.isEmpty(invokers)) {
        return invokers;
    }
    try {
        // matchWhen() 判断当前消费者与条件规则中前半部分(规则体中=>之前的内容)是否匹配
        // 若没有匹配上,则返回所有invoker
        if (!matchWhen(url, invocation)) {
            return invokers;
        }

        // 用于存放过滤出的invoker
        List<Invoker<T>> result = new ArrayList<Invoker<T>>();

        // thenCondition 代表规则体=>之后的表达式
        // 若该表达式为空,说明当前路由规则为黑白名单
        if (thenCondition == null) {
            logger.warn("The current consumer in the service blacklist. consumer: " + NetUtils.getLocalHost() + ", service: " + url.getServiceKey());
            return result;
        }

        // 代码走到这里,说明thenCondition不为null
        // matchThen() 用于判断invoker与thenCondition是否匹配
        // 遍历所有invoker,查找到所有可用的invoker
        for (Invoker<T> invoker : invokers) {
            if (matchThen(invoker.getUrl(), url)) {
                result.add(invoker);
            }
        }
        if (!result.isEmpty()) {
            return result;
        } else if (this.isForce()) {
            logger.warn("The route result is empty and force execute. consumer: " + NetUtils.getLocalHost() + ", service: " + url.getServiceKey() + ", router: " + url.getParameterAndDecoded(RULE_KEY));
            return result;
        }
    } catch (Throwable t) {
        logger.error("Failed to execute condition router rule: " + getUrl() + ", invokers: " + invokers + ", cause: " + t.getMessage(), t);
    }
    return invokers;
}

就是 按照我们配置的各种条件进行过滤匹配,然后返回可用的Invoker集合。

总结一下就是,在服务消费端应用中,每个需要消费的服务都被包装为ReferenceConfig , 在应用启动时会调用每个服务对应的 ReferenceConfig 的 get()方法, 然后会为每个服务创建一个自己的 RegistryDirectory 对象,每个 RegistryDirectory 管理该服务提供者的地址列表、路由规则、动态配置等信息,当服务提供者的信息发生变化时,Regist叩Directory 会动态地得到变化通知,并自动更新。

下篇文章,我们看一下服务降级。

参考文章

Dubbo3.0源码注释github地址
dubbo源码系列
dubbo源码分析专栏

猜你喜欢

转载自juejin.im/post/7118920142297235469
今日推荐