詳細なリボン

目次

1。概要

2.使用する

2.1. インポート

2.2. 有効にする

2.3. スイッチ負荷分散アルゴリズム

3. ロードバランシングのソースコード分析

3.1. インターフェース

3.2. 抽象クラス

3.3. サーバーの選択

3.4. 原子性

4. カスタム負荷分散アルゴリズム


1。概要

リボンは、Netflix によってオープンソース化されているクライアント負荷分散ライブラリであり、Spring Cloud Netflix プロジェクトのコア コンポーネントの 1 つです。これは主に、マイクロサービス アーキテクチャでサービスの負荷を分散し、システムの可用性とパフォーマンスを向上させるために使用されます。リボンは通信コンポーネントではなく、サービス呼び出し元と通信コンポーネントの間の中間層であり、主に負荷分散と、この要求の処理に適したサービス ノードの選択に使用されます。

Spring Cloud システム全体の通信コンポーネントは、実際には負荷分散コンポーネント + HTTP 通信コンポーネントをカプセル化しています。HTTP リクエストを開始するために使用される HTTP 通信コンポーネントについては何も言うことはありません。また、市場には多くの HTTP 通信コンポーネントがあります。 Spring Boot、Apache の RestTemplate、Apache の Apache HttpClient など。負荷分散コンポーネントとしてリクエストの分散を決定する負荷分散コンポーネントについては、検討する価値があり、通信コンポーネント全体の中核であると言えます。

Spring Cloud 2020.0 以降、Spring Cloud は正式にリボンを非推奨としてマークし、代替として Spring Cloud LoadBalancer が推奨されます。これは標準化の考慮事項に基づいています。Spring エコシステム全体は常に「すべてを含む」ことを望んでいたため、コミュニティは負荷分散コンポーネントに固定実装を直接採用することを当然望んでいませんが、サードパーティ ソリューションが実現できることを望んでいます。スムーズなアクセス、切り替え。

リボンは、将来的には負荷分散コンポーネントの最初の選択肢ではなくなりますが、最も古典的な負荷分散コンポーネントとして、その基礎となるアイデアの一部は依然として後続のソリューションで使用されています。実際、リボンのソース コードを理解していれば、基本的には負荷分散の原理を理解できており、すべてが変わりません。

2.使用する

2.1. インポート

ブロガーの前回の記事では、eureka+communication コンポーネントを使用してサービスの登録と呼び出しを完了する方法を詳しく紹介し、リボンの使用方法についても詳しく紹介しました。以下を移動できます。

Eurekaサービスの登録と呼び出しの詳細説明__BugManのブログ - CSDNブログ

一般的には次のとおりです。

Eureka はリボンを統合しています。eureka をインポートした後、リボンを個別にインポートする必要はありませんが、正しいバージョン番号を選択する必要があることに注意してください。リボンが削除された上位バージョンを選択しないでください。この記事で使用されている依存バージョン:

もちろん、Spring Cloud 全体のバージョン番号を直接ダウングレードすることもできます。Spring Cloud のバージョン番号の問題については、ブロガーの別の記事に移動して詳細を説明します。

Spring Cloud のバージョンの問題を詳しく説明_BugMan のブログ - CSDN ブログ

2.2. 有効にする

依存関係を導入した後、HTTP 通信コンポーネントで負荷分散を有効にすることができます。例として Spring Boot に付属する RestTemplate を使用すると、クライアント (コンシューマー) が http リクエストを開始するたびに、ローカル サーバー上で負荷分散操作が実行されます。その後、サービスアクセスに進みます。

2.3. スイッチ負荷分散アルゴリズム

負荷分散のコアインターフェイス Irule には複数の実装クラスがあり、各実装クラスは異なる負荷分散アルゴリズムを実装します。

一般的に使用されるのは、ポーリング、ランダム、利用可能、再試行などです。

  • RoundRobinルール、ポーリング

  • ランダムルール、ランダム

  • AvailabilityFilteringRule: 障害のあるサービスを除外、トリップ、アクセスし、残りをポーリングします。

  • RetryRule は、ポーリングに従ってサービスを取得します。サービスの取得に失敗した場合は、指定された時間内に再試行します。

  • 切り替える必要がある場合は、@Bean を @Configuration に直接挿入するだけです。

3. ロードバランシングのソースコード分析

3.1. インターフェース

IRuleインターフェース:

  • 選択: サーバーを選択します
  • get/setLoadBalancer: バランサーの取得と変更

ILoadBalancer:

サーバーを処理し、サーバーの検索と登録を担当します。

3.2. 抽象クラス

AbstractLoadBalancer は Irule インターフェイスを実装し、バランサーの get/set メソッドを書き換えて、サブクラスによって書き換えられる抽象メソッドを 1 つだけ残して、選択します。

3.3. サーバーの選択

すべての実装クラスは抽象クラス AbstractLoadBalancer を継承します。それぞれがchooseメソッドを書き換えます。つまり、異なる負荷分散ルールを実装します(ここでは例としてリボンのデフォルトの負荷分散ルールであるRoundRobinRuleを使用します)。

選択メソッドは、各負荷分散クラスのコア メソッドである負荷分散戦略です (ここでは、リボンの既定の負荷分散ルールである RoundRobinRule を例として取り上げます)。

バランサーは登録センターと通信し、サーバーの総数、利用可能なサーバーの総数など、現在のシステム内のすべてのサーバーの関連情報を記録します。

バランサーにサーバーの総数と使用可能なサーバーの総数を問い合わせ、これら 2 つの値に基づいて計算を実行して、トラフィックを伝送するサーバーを選択します。

public Server choose(ILoadBalancer lb, Object key) {
        if (lb == null) {
            log.warn("no load balancer");
            return null;
        } else {
            Server server = null;
            int count = 0;

            while(true) {
                if (server == null && count++ < 10) {
                    List<Server> reachableServers = lb.getReachableServers();
                    List<Server> allServers = lb.getAllServers();
                    int upCount = reachableServers.size();
                    int serverCount = allServers.size();
                    if (upCount != 0 && serverCount != 0) {
                        int nextServerIndex = this.incrementAndGetModulo(serverCount);
                        server = (Server)allServers.get(nextServerIndex);
                        if (server == null) {
                            Thread.yield();
                        } else {
                            if (server.isAlive() && server.isReadyToServe()) {
                                return server;
                            }

                            server = null;
                        }
                        continue;
                    }

                    log.warn("No up servers available from load balancer: " + lb);
                    return null;
                }

                if (count >= 10) {
                    log.warn("No available alive servers after 10 tries from load balancer: " + lb);
                }

                return server;
            }
        }
    }

プロセス全体を通じてサーバー関連の情報をカプセル化する Server クラスには、サーバーの詳細なパラメーターが含まれています。

 

3.4. 原子性

選択プロセス中、アトミック性を確保するために、スピン ロック (CAS) を使用して、毎回 1 つのアクセス スレッドのみが処理され、残りのスレッドが自己選択の待機状態になるようにします。

CompareAndSet(期待値、修正値)

期待値、つまりバージョン番号。

変更値。バージョン番号を更新する値です。

期待値が変化したか(前後で同じか)を判定し、変化していない場合は期待値を変更後の値に更新します。

true を返し、それ以外の場合は false を返します。

4. カスタム負荷分散アルゴリズム

トップレベルのインターフェースがあり、負荷分散アルゴリズムを切り替えることができる場合、負荷分散アルゴリズムをカスタマイズするのは自然なことですが、サーバーの重みに基づいて負荷分散を行う場合の負荷分散アルゴリズムは次のとおりです。

public class CustomWeightedRandomRule extends AbstractLoadBalancerRule {
    private AtomicInteger totalWeight = new AtomicInteger(0);

    @Override
    public Server choose(Object key) {
        ILoadBalancer lb = getLoadBalancer();
        if (lb == null) {
            return null;
        }

        List<Server> allServers = lb.getAllServers();
        int serverCount = allServers.size();
        if (serverCount == 0) {
            return null;
        }

        int randomWeight = ThreadLocalRandom.current().nextInt(totalWeight.get());
        int currentWeight = 0;

        for (Server server : allServers) {
            currentWeight += getWeight(server);
            if (randomWeight < currentWeight) {
                return server;
            }
        }

        // Fallback to the default server if no server is selected
        return super.choose(key);
    }

    private int getWeight(Server server) {
        // Return the weight of the server (custom logic)
        // Example: return server.getMetadata().getWeight();
        return 1; // Default weight is 1
    }

    @Override
    public void initWithNiwsConfig(IClientConfig clientConfig) {
        super.initWithNiwsConfig(clientConfig);
        // Calculate the total weight of all servers
        List<Server> allServers = getLoadBalancer().getAllServers();
        totalWeight.set(allServers.stream().mapToInt(this::getWeight).sum());
    }
}

おすすめ

転載: blog.csdn.net/Joker_ZJN/article/details/131150360