ダボソースコードシリーズ7クラスターフォールトトレラントサービスディレクトリディレクトリ

1.フロンティア

サービスのエクスポートとサービスの参照がすべて完了しました。次に、サービスの呼び出しプロセスを分析する必要がありますが、サービスの呼び出しプロセスには、最初に調査する必要のある非常に重要なモジュール、つまりクラスターのフォールトトレラントモジュールがあります。クラスターフォールトトレラントモジュールには、サービスディレクトリ、サービスルーティングルーター、クラスター、ロードバランスLoadBalanceの4つの部分があります。以下は、説明のために4つの章に分かれています。、まず、サービスディレクトリディレクトリを確認します。

定義:サービスディレクトリディレクトリはInvokerのコレクションであり、このコレクションの要素はレジストリの変更に応じて動的に調整されます

プロバイダーに関連する一部の情報は、サービスディレクトリディレクトリに保存されます。サービスディレクトリを介して、コンシューマーは、IP、ポート、サービス契約などのプロバイダーの情報を取得できます。この情報を使用して、コンシューマーはNettyなどのクライアントを介してリモート呼び出しを行うことができますサービスクラスターでは、プロバイダーの数は静的ではありません。マシンがクラスターに追加される場合は、それに応じてプロバイダーレコードをディレクトリに追加する必要があります。または、プロバイダーの構成が変更された場合は、それに応じてディレクトリ内の元のレコードを更新する必要があります。このように見ると、ディレクトリとレジストリの機能は確かに似ています。実際、ディレクトリはレジストリのサービス構成情報を取得した後、構成情報ごとにInvokerオブジェクトを生成し、Invokerオブジェクトを格納します。このInvokerは、最終的にディレクトリによって保持されるオブジェクトです。Invokerの用途は何ですか?名前を見てください、これはリモートコール機能を備えたオブジェクトです

2.サービスディレクトリ構造

サービスディレクトリディレクトリの全体的な構造は次のとおりです。

上の図から、DirectoryにはStaticDirectoryRegistryDirectoryの2つの特定の実装クラスしかなく、どちらもAbstractDirectoryクラスを継承し、AbstractDirectoryはDirectoryインターフェイスを実装していることがわかります。Directoryインターフェイスは非常に重要なメソッド、つまりリストを定義します。(呼び出し呼び出し)、呼び出し元リストを列挙するために使用されます

DirectoryインターフェースはNodeインターフェースを継承します。Nodeインターフェースは、構成情報URLを取得するためのgetUrl()という非常に重要なメソッドを定義します。このインターフェースを実装するクラスは、Registry、Monitor、Invokerなどの構成情報を外部に提供できます。 dubboなどこのインターフェースを継承しています

RegistryDirectoryは、Directoryインターフェイスを実装するだけでなく、NotifyListenerインターフェイスも実装します。このように、RegistryDirectoryは、レジストリノード情報が変更されたときに、このインターフェイスメソッドを介して変更情報を取得し、変更情報に従って内部Invokerリストを動的に調整できます。

3、ディレクトリソースコード

サービスディレクトリ構造から、ディレクトリソースコードを分析するために必要なのは、AbstractDirectory(抽象クラ​​ス)、StaticDirectory(実装クラス)、RegistryDirectory(実装クラス)の3つのクラスだけです。

3.1 AbstractDirectory

AbstractDirectoryはInvoker列挙プロセスをカプセル化し、特定の列挙ロジックはサブクラスによって実装されます。これは典型的なテンプレートパターンです。次に、AbstractDirectoryのソースコードを見てみましょう。

    public AbstractDirectory(URL url, URL consumerUrl, RouterChain<T> routerChain) {
        if (url == null) {
            throw new IllegalArgumentException("url == null");
        }

        if (url.getProtocol().equals(REGISTRY_PROTOCOL)) {
            // protocol 协议是 registry 时,解码获取 refer 参数值并设置到 url 中,同时移除 monitor 参数
            Map<String, String> queryMap = StringUtils.parseQueryString(url.getParameterAndDecoded(REFER_KEY));
            this.url = url.addParameters(queryMap).removeParameter(MONITOR_KEY);
        } else {
            this.url = url;
        }

        this.consumerUrl = consumerUrl;
        // 设置路由链
        setRouterChain(routerChain);
    }

    @Override
    public List<Invoker<T>> list(Invocation invocation) throws RpcException {
        if (destroyed) {
            throw new RpcException("Directory already destroyed .url: " + getUrl());
        }

        // doList 是模板方法,由子类实现具体的业务逻辑
        return doList(invocation);
    }

ロジックは非常に単純なので、ここでは繰り返しません。StaticDirectoryのソースコードを見てみましょう。

3.2StaticDirectory

StaticDirectoryは静的サービスディレクトリです。名前が示すように、そこに格納されているInvokerは変更されませんしたがって、理論的には、不変のリスト関数と非常によく似ています。このクラスの特定のソースコードを見てみましょう。
 

public class StaticDirectory<T> extends AbstractDirectory<T> {
    private static final Logger logger = LoggerFactory.getLogger(StaticDirectory.class);

    private final List<Invoker<T>> invokers;

    public StaticDirectory(List<Invoker<T>> invokers) {
        this(null, invokers, null);
    }

    public StaticDirectory(List<Invoker<T>> invokers, RouterChain<T> routerChain) {
        this(null, invokers, routerChain);
    }

    public StaticDirectory(URL url, List<Invoker<T>> invokers) {
        this(url, invokers, null);
    }

    public StaticDirectory(URL url, List<Invoker<T>> invokers, RouterChain<T> routerChain) {
        super(url == null && CollectionUtils.isNotEmpty(invokers) ? invokers.get(0).getUrl() : url, routerChain);
        if (CollectionUtils.isEmpty(invokers)) {
            throw new IllegalArgumentException("invokers == null");
        }
        this.invokers = invokers;
    }

    @Override
    public Class<T> getInterface() {
        // 获取接口类
        return invokers.get(0).getInterface();
    }

    // 检测 directory 是否可用
    @Override
    public boolean isAvailable() {
        if (isDestroyed()) {
            return false;
        }
        for (Invoker<T> invoker : invokers) {
            if (invoker.isAvailable()) {
                // 只要有一个 invoker 可用,就认为当前 directory 是可用的
                return true;
            }
        }
        return false;
    }

    // 销毁 directory 目录
    @Override
    public void destroy() {
        if (isDestroyed()) {
            return;
        }
        super.destroy();
        // 遍历销毁 directory 目录下的所有 invoker
        for (Invoker<T> invoker : invokers) {
            invoker.destroy();
        }
        invokers.clear();
    }

    // 构建路由链
    public void buildRouterChain() {
        RouterChain<T> routerChain = RouterChain.buildChain(getUrl());
        routerChain.setInvokers(invokers);
        this.setRouterChain(routerChain);
    }

    @Override
    protected List<Invoker<T>> doList(Invocation invocation) throws RpcException {
        List<Invoker<T>> finalInvokers = invokers;
        if (routerChain != null) {
            try {
                // 经过路由链过滤掉不需要的 invoker
                finalInvokers = routerChain.route(getConsumerUrl(), invocation);
            } catch (Throwable t) {
                logger.error("Failed to execute router: " + getUrl() + ", cause: " + t.getMessage(), t);
            }
        }
        // 返回 invoker 列表
        return finalInvokers == null ? Collections.emptyList() : finalInvokers;
    }

}

StaticDirectoryソースコードには複雑なロジックはありません。実行する必要があるのは、ルーティングチェーンを介して呼び出し元をフィルタリングすることだけです。これについては後の章で説明します。他のコンテンツは理解しやすいので、ここでは説明しません。あまりにも詳細に入る。

3.3 RegistryDirectory

RegistryDirectoryはNotifyListenerインターフェイスを実装する動的なサービスディレクトリですレジストリサービスの構成が変更されると、RegistryDirectoryは現在のサービスに関連する変更を受け取ることができます。変更通知を受信した後、RegistryDirectoryは構成変更情報に従って呼び出し元リストを更新できますRegistryDirectoryには、次の3つの重要なロジックがあります。

1)、呼び出し元リストを列挙します

2)、サービス構成の変更の通知を受け取ります

3)、保存されている呼び出し元リストを更新します

次に、1、2、3の順に全員のソースコードを分析します。

3.3.1呼び出し元リストを列挙する

doListメソッドは、呼び出し元リストの列挙を実装します。ソースコードは次のとおりです。

    @Override
    public List<Invoker<T>> doList(Invocation invocation) {
        if (forbidden) {
            // 1. No service provider 2. Service providers are disabled
            // provider 关闭或禁用了服务,此时抛出 No provider 异常
            throw new RpcException(RpcException.FORBIDDEN_EXCEPTION, "No provider available from registry " +
                    getUrl().getAddress() + " for service " + getConsumerUrl().getServiceKey() + " on consumer " +
                    NetUtils.getLocalHost() + " use dubbo version " + Version.getVersion() +
                    ", please check status of providers(disabled, not registered or in blacklist).");
        }

        if (multiGroup) {
            // 多个 group 的话直接返回 invoker 列表
            return this.invokers == null ? Collections.emptyList() : this.invokers;
        }

        List<Invoker<T>> invokers = null;
        try {
            // Get invokers from cache, only runtime routers will be executed.
            // route 路由器职责链过滤满足条件的 Invoker 列表
            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;
    }

ここでの主なロジックは、ルートルーターチェーンを介して条件を満たす呼び出し元のリストをフィルタリングすることでもあります。これはシンプルで理解しやすいため、ここでは繰り返しません。

3.3.2サービス構成変更の通知の受信
RegistryDirectoryは、レジストリの構成の変更に応じて動的に調整される動的サービスディレクトリですRegistryDirectoryはNotifyListenerインターフェースを実装しているため、レジストリ変更通知はこのインターフェースを介して取得できます。特定の実装ロジックはnotifyメソッドにあります。

notifyメソッドのソースコードは次のとおりです。

    @Override
    public synchronized void notify(List<URL> urls) {
        // 定义三个 集合,分别为 provider 的 configurators URL、routers URL 和 providers URL
        Map<String, List<URL>> categoryUrls = urls.stream()
                .filter(Objects::nonNull)
                .filter(this::isValidCategory)
                .filter(this::isNotCompatibleFor26x)
                .collect(Collectors.groupingBy(url -> {
                    if (UrlUtils.isConfigurator(url)) {
                        // configurators URL
                        return CONFIGURATORS_CATEGORY;
                    } else if (UrlUtils.isRoute(url)) {
                        // routers URL
                        return ROUTERS_CATEGORY;
                    } else if (UrlUtils.isProvider(url)) {
                        // providers URL
                        return PROVIDERS_CATEGORY;
                    }
                    return "";
                }));

        List<URL> configuratorURLs = categoryUrls.getOrDefault(CONFIGURATORS_CATEGORY, Collections.emptyList());
        // configurators URL 转成 Configurator
        this.configurators = Configurator.toConfigurators(configuratorURLs).orElse(this.configurators);

        List<URL> routerURLs = categoryUrls.getOrDefault(ROUTERS_CATEGORY, Collections.emptyList());
        // routes URL 转成 Router
        toRouters(routerURLs).ifPresent(this::addRouters);

        // providers
        // 提供者 URL 列表
        List<URL> providerURLs = categoryUrls.getOrDefault(PROVIDERS_CATEGORY, Collections.emptyList());
        // 刷新 invoker 列表
        refreshOverrideAndInvoker(providerURLs);
    }

notifyメソッドは、主に次のロジックを実装します。

1)URLのプロトコルまたはカテゴリパラメータに従って、URLは分類されて保存されます

2)toConfiguratorsメソッドとtoRoutersメソッドは、URLをそれぞれConfiguratorとRouterに変換します

3)、プロバイダーのURLリストに従って呼び出し元リストを更新します

次の焦点は、呼び出し元リストのソースコードを分析して更新することです。

3.3.3呼び出し元リストを更新する

呼び出し元リストを更新するロジックは、主にrefreshOverrideAndInvokerメソッドにあります。これは、レジストリの変更に伴ってRegistryDirectoryが変更されるようにするための鍵です。プロセス全体のソースコードは次のとおりです。

    private void refreshOverrideAndInvoker(List<URL> urls) {
        // mock zookeeper://xxx?mock=return null
        // 获取配置 overrideDirectoryUrl 值
        overrideDirectoryUrl();
        // 刷新 invoker 列表
        refreshInvoker(urls);
    }


    /**
     * Convert the invokerURL list to the Invoker Map. The rules of the conversion are as follows:
     * <ol>
     * <li> If URL has been converted to invoker, it is no longer re-referenced and obtained directly from the cache,
     * and notice that any parameter changes in the URL will be re-referenced.</li>
     * <li>If the incoming invoker list is not empty, it means that it is the latest invoker list.</li>
     * <li>If the list of incoming invokerUrl is empty, It means that the rule is only a override rule or a route
     * rule, which needs to be re-contrasted to decide whether to re-reference.</li>
     * </ol>
     *
     * @param invokerUrls this parameter can't be null
     */
    // TODO: 2017/8/31 FIXME The thread pool should be used to refresh the address, otherwise the task may be accumulated.
    private void refreshInvoker(List<URL> invokerUrls) {
        Assert.notNull(invokerUrls, "invokerUrls should not be null");

        // invokerUrls 中只有一个元素 && url 的协议头 Protocol 为 empty,表示禁用所有服务
        if (invokerUrls.size() == 1
                && invokerUrls.get(0) != null
                && EMPTY_PROTOCOL.equals(invokerUrls.get(0).getProtocol())) {
            // 禁用服务标识
            this.forbidden = true; // Forbid to access
            // 空 invoker 列表
            this.invokers = Collections.emptyList();
            routerChain.setInvokers(this.invokers);
            // 销毁所有的 invoker
            destroyAllInvokers(); // Close all invokers
        } else {
            // Allow to access
            this.forbidden = false;
            // local reference
            // 原来的 invoker 映射map
            Map<String, Invoker<T>> oldUrlInvokerMap = this.urlInvokerMap;
            if (invokerUrls == Collections.<URL>emptyList()) {
                invokerUrls = new ArrayList<>();
            }
            if (invokerUrls.isEmpty() && this.cachedInvokerUrls != null) {
                // 添加缓存 url 到 invokerUrls 中
                invokerUrls.addAll(this.cachedInvokerUrls);
            } else {
                this.cachedInvokerUrls = new HashSet<>();
                //Cached invoker urls, convenient for comparison
                // 缓存 invokerUrls
                this.cachedInvokerUrls.addAll(invokerUrls);
            }
            if (invokerUrls.isEmpty()) {
                return;
            }
            // Translate url list to Invoker map
            // 将 url 转换成 url 到 invoker 映射 map,map 中 url 为 key,value 为 invoker
            Map<String, Invoker<T>> newUrlInvokerMap = toInvokers(invokerUrls);

            /**
             * 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;
            }

            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);
            // 多个 group 时需要合并 invoker
            this.invokers = multiGroup ? toMergeInvokerList(newInvokers) : newInvokers;
            this.urlInvokerMap = newUrlInvokerMap;

            try {
                // Close the unused Invoker
                // 销毁不需要的 invoker
                destroyUnusedInvokers(oldUrlInvokerMap, newUrlInvokerMap);
            } catch (Exception e) {
                logger.warn("destroyUnusedInvokers error. ", e);
            }
        }
    }

呼び出し元リストを更新すると、主に次のロジックが実装されます。

1)受信パラメータInvokerUrlsの数とURLのプロトコルヘッダーに従って、すべてのサービスを無効にする必要があるかどうかを判断します。必要に応じて、禁止をtrueに設定し、すべての呼び出し元を破棄します。

2)URLを呼び出し元に変換し、<url、Invoker>のマッピング関係を取得します

3)複数のグループの呼び出し元を結合し、それらを呼び出し元変数に割り当てます

4)無駄な呼び出し元を破棄して、消費者がオフラインプロバイダーに電話をかけないようにします

toInvokersメソッド、つまりurlをinvokerに変換し、<url、Invoker>のマッピング関係を取得するプロセスを分析してみましょう。ソースコードは次のとおりです。

    /**
     * Turn urls into invokers, and if url has been refer, will not re-reference.
     *
     * @param urls
     * @return invokers
     */
    private Map<String, Invoker<T>> toInvokers(List<URL> urls) {
        Map<String, Invoker<T>> newUrlInvokerMap = new HashMap<>();
        if (urls == null || urls.isEmpty()) {
            // 列表为空,直接返回
            return newUrlInvokerMap;
        }
        Set<String> keys = new HashSet<>();
        // 获取 consumer 端配置的协议
        String queryProtocols = this.queryMap.get(PROTOCOL_KEY);
        for (URL providerUrl : urls) {
            // If protocol is configured at the reference side, only the matching protocol is selected
            // 如果在 consumer 端配置了协议,则只选择匹配的协议
            if (queryProtocols != null && queryProtocols.length() > 0) {
                boolean accept = false;
                String[] acceptProtocols = queryProtocols.split(",");
                for (String acceptProtocol : acceptProtocols) {
                    // provider 协议是否被 consumer 协议支持
                    if (providerUrl.getProtocol().equals(acceptProtocol)) {
                        accept = true;
                        break;
                    }
                }
                if (!accept) {
                    // provider 协议不被 consumer 协议支持,则忽略此 url
                    continue;
                }
            }
            // 忽略 empty 协议
            if (EMPTY_PROTOCOL.equals(providerUrl.getProtocol())) {
                continue;
            }
            // 通过 SPI 检测 provider 端协议是否被 consumer 端支持,不支持则抛出异常
            if (!ExtensionLoader.getExtensionLoader(Protocol.class).hasExtension(providerUrl.getProtocol())) {
                logger.error(new IllegalStateException("Unsupported protocol " + providerUrl.getProtocol() +
                        " in notified url: " + providerUrl + " from registry " + getUrl().getAddress() +
                        " to consumer " + NetUtils.getLocalHost() + ", supported protocol: " +
                        ExtensionLoader.getExtensionLoader(Protocol.class).getSupportedExtensions()));
                continue;
            }
            // 合并 url 参数,顺序是 : override > -D > Consumer > Provider
            URL url = mergeUrl(providerUrl);

            // The parameter urls are sorted
            // url 参数拼接的字符串且被排序了,
            // 例如:dubbo://192.168.1.247:20887/org.apache.dubbo.config.spring.api.DemoService?anyhost=true&application=service-class&
            // bean.name=org.apache.dubbo.config.spring.api.DemoService&bind.ip=192.168.1.247&bind.port=20887&
            // class=org.apache.dubbo.config.spring.impl.DemoServiceImpl&deprecated=false&dubbo=2.0.2&dynamic=true&generic=false&
            // interface=org.apache.dubbo.config.spring.api.DemoService&methods=sayName,getBox&owner=world&pid=24316&register=true&
            // release=&side=provider&timestamp=1572405725011
            String key = url.toFullString();
            if (keys.contains(key)) {
                // Repeated url
                // 忽略重复 key
                continue;
            }
            keys.add(key);
            // Cache key is url that does not merge with consumer side parameters, regardless of how the consumer combines parameters, if the server url changes, then refer again
            // 原来本地缓存的 <url, Invoker>
            Map<String, Invoker<T>> localUrlInvokerMap = this.urlInvokerMap; // local reference
            // 获取现有的 url 对应的 invoker
            Invoker<T> invoker = localUrlInvokerMap == null ? null : localUrlInvokerMap.get(key);
            // Not in the cache, refer again
            // 原来缓存没有或者provider配置变化了
            if (invoker == null) {
                try {
                    boolean enabled = true;
                    if (url.hasParameter(DISABLED_KEY)) {
                        // 获取 url 中 disable 配置值,取反,然后赋值给 enable 变量
                        enabled = !url.getParameter(DISABLED_KEY, false);
                    } else {
                        // 获取 url 中 enable 配置值
                        enabled = url.getParameter(ENABLED_KEY, true);
                    }
                    if (enabled) {
                        // 调用 Protocol 的 refer 方法构建获取 Invoker,
                        // 具体调用流程是 refer(ProtocolListenerWrapper) -> refer(ProtocolFilterWrapper) -> refer(AbstractProtocol)
                        invoker = new InvokerDelegate<>(protocol.refer(serviceType, url), url, providerUrl);
                    }
                } catch (Throwable t) {
                    logger.error("Failed to refer invoker for interface:" + serviceType + ",url:(" + url + ")" + t.getMessage(), t);
                }
                if (invoker != null) {
                    // Put new invoker in cache
                    // 新 invoker 放入缓存
                    newUrlInvokerMap.put(key, invoker);
                }
            } else {
                newUrlInvokerMap.put(key, invoker);
            }
        }
        keys.clear();
        return newUrlInvokerMap;
    }

toInvokersメソッドは、主に次のロジックを実装します。

1)コンシューマーでサポートされていない空のプロトコルとプロバイダープロトコルのURLを無視します

2)、URLパラメータをマージします。順序は次のとおりです。override> -D> Consumer> Provider

3)ローカルキャッシュインボーカーを取得します。キャッシュがない場合、またはプロバイダー構成が変更されていない場合は、&&プロバイダーが無効になっていない場合は、新しいインボーカーを作成してキャッシュに入れますnewUrlInvokerMap

次に、複数の呼び出し元グループをマージするロジックであるtoMergeInvokerListメソッドについて説明します。ソースコードは次のとおりです。

    private List<Invoker<T>> toMergeInvokerList(List<Invoker<T>> invokers) {
        List<Invoker<T>> mergedInvokers = new ArrayList<>();
        Map<String, List<Invoker<T>>> groupMap = new HashMap<>();
        // 遍历 invoker 进行分组
        for (Invoker<T> invoker : invokers) {
            // url 中获取 group 配置
            String group = invoker.getUrl().getParameter(GROUP_KEY, "");
            groupMap.computeIfAbsent(group, k -> new ArrayList<>());
            // invoker 放入同一组中
            groupMap.get(group).add(invoker);
        }

        if (groupMap.size() == 1) {
            // 只有一个分组,直接取出值
            mergedInvokers.addAll(groupMap.values().iterator().next());
        } else if (groupMap.size() > 1) {
            // 多个分组时,遍历分组中的 invoker列表,并调用集群的 join 方法合并每个组的 invoker 列表
            for (List<Invoker<T>> groupList : groupMap.values()) {
                StaticDirectory<T> staticDirectory = new StaticDirectory<>(groupList);
                staticDirectory.buildRouterChain();
                // 通过集群类 的 join 方法合并每个分组对应的 Invoker 列表
                mergedInvokers.add(CLUSTER.join(staticDirectory));
            }
        } else {
            mergedInvokers = invokers;
        }
        return mergedInvokers;
    }

複数の組み合わせと呼び出し元リストのロジックは非常に単純です。コードコメントはすでに非常に詳細であるため、ここでは繰り返しません。

最後に、無駄な請求書を破棄するdestroyUnusedInvokersメソッドを分析します。ソースコードは次のとおりです。

    /**
     * Check whether the invoker in the cache needs to be destroyed
     * If set attribute of url: refer.autodestroy=false, the invokers will only increase without decreasing,there may be a refer leak
     *
     * @param oldUrlInvokerMap
     * @param newUrlInvokerMap
     */
    private void destroyUnusedInvokers(Map<String, Invoker<T>> oldUrlInvokerMap, Map<String, Invoker<T>> newUrlInvokerMap) {
        if (newUrlInvokerMap == null || newUrlInvokerMap.size() == 0) {
            // 新的 invoker 为空,表明禁用了所有的 provider,这里销毁所有的 invoker
            destroyAllInvokers();
            return;
        }
        // check deleted invoker
        // 记录需要被销毁的 invoker 列表
        List<String> deleted = null;
        if (oldUrlInvokerMap != null) {
            Collection<Invoker<T>> newInvokers = newUrlInvokerMap.values();
            for (Map.Entry<String, Invoker<T>> entry : oldUrlInvokerMap.entrySet()) {
                // 新的 invoker 列表中不包含原有的 invoker,则该 invoker 需要被销毁
                if (!newInvokers.contains(entry.getValue())) {
                    if (deleted == null) {
                        deleted = new ArrayList<>();
                    }
                    // 该 invoker 添加到销毁列表中
                    deleted.add(entry.getKey());
                }
            }
        }

        // 销毁列表中有数据,需要销毁 invoker
        if (deleted != null) {
            for (String url : deleted) {
                if (url != null) {
                    // 缓存中移除要销毁的 invoker
                    Invoker<T> invoker = oldUrlInvokerMap.remove(url);
                    if (invoker != null) {
                        try {
                            // 销毁 invoker
                            invoker.destroy();
                            if (logger.isDebugEnabled()) {
                                logger.debug("destroy invoker[" + invoker.getUrl() + "] success. ");
                            }
                        } catch (Exception e) {
                            logger.warn("destroy invoker[" + invoker.getUrl() + "] failed. " + e.getMessage(), e);
                        }
                    }
                }
            }
        }
    }

これまで、呼び出し元リストの更新ロジックを分析してきました。プロセス全体の概要は次のとおりです。

1)、入力パラメーターにURLが1つだけ含まれていて、URLプロトコルヘッダーが空であるかどうかを確認します

2)最初のステップの結果が真の場合、それはすべてのサービスが無効になっていることを意味し、この時点ですべての呼び出し元が破棄されます

3)最初のステップでの検出結果が偽の場合は、URLを呼び出し元に変換し、<url、Invoker>のマッピング関係を取得します。

4)、Invokerの複数のグループをマージします

5)、役に立たない発動者を破壊する

4、まとめ

この記事では、サービスディレクトリに関連するソースコードを詳しく紹介します。ロジックは比較的単純ですが、ローカルサービスディレクトリとレジストリサービスの構成を一貫させるために実行する必要のあることがまだたくさんあります。サービスディレクトリディレクトリは、Dubboクラスタのフォールトトレランスの一部であり、比較的基本的な部分でもあります。理解しやすいようです。皆さんに理解していただきたいと思います。

参照:

https://dubbo.apache.org/zh-cn/docs/source_code_guide/directory.html

おすすめ

転載: blog.csdn.net/ywlmsm1224811/article/details/103118375