記事ディレクトリ
序文
前回の記事「Eureka登録センターの原理、サービスの登録と発見を徹底理解する」では、Eurekaを使ってサービスの登録とプルを実現し、@LoadBalanced
アノテーションを追加することで負荷分散を実現する方法を紹介しました。この自動化の背後には多くの疑問が隠されています。
- サービスはいつ廃止されますか?
- 負荷分散はどのように実装されますか?
- 負荷分散の原則と戦略は何ですか?
この記事の目的は、Eureka を使用して負荷分散を実現する原理を深く調査し、マイクロサービス アーキテクチャにおけるサービス呼び出しの内部メカニズムをより明確に理解できるようにすることです。これらの疑問に答えることで、サービス検出と負荷分散がどのように動作するかをより深く理解し、高性能で安定した分散システムを構築するための強固な基盤を築くことができます。
1. リボンとは何ですか?
リボンは、HTTP および TCP クライアントに基づくロード バランサーです。マイクロサービス アーキテクチャでは、サービスの呼び出しには通常、負荷分散、つまり呼び出し用に複数のサービス プロバイダーの 1 つを選択することが含まれます。リボンは、シンプルかつ効果的な負荷分散ソリューションを提供します。
リボンはもともと Netflix によって開発され、後に Spring Cloud プロジェクトの一部になりました。その主な機能は、サービス コンシューマとプロバイダーの間でバランスのとれたトラフィック分散を実現し、各サービス プロバイダーが適切なリクエストを受信できるようにし、サービスの過負荷やリソースの浪費を回避することです。
具体的には、リボンは次の機能を実装します。
-
負荷分散アルゴリズム:リボンは、ポーリング、ランダム、加重ポーリングなどのさまざまな負荷分散アルゴリズムをサポートしており、サービス利用者が実際のシナリオに基づいて適切な負荷分散戦略を選択できるようにします。
-
サービス インスタンスの自動検出:リボンは、Eureka などのサービス登録センターと統合して、利用可能なサービス インスタンスのリストを自動的に取得します。
-
フェールオーバーと再試行のメカニズム:リボンにはフェールオーバーと再試行の機能があり、サービス プロバイダーに障害が発生した場合に他の正常なインスタンスに自動的に切り替えることができ、システムの安定性と可用性が向上します。
Spring Cloud では、リボンは負荷分散クライアント コンポーネントとして、マイクロサービス呼び出しリクエストをインターセプトし、ターゲット サービス インスタンスを動的に選択して、要求された負荷を分散し、サービス呼び出しのきめ細かい制御を実現します。
2. 負荷分散を実現するためのリボンの原理
2.1 負荷分散プロセス
リボンの負荷分散のフローチャートは次のとおりです。
このプロセスの詳細な説明は次のとおりです。
- まず、サービス コンシューマーがリクエストを開始し、リボン ロード バランサーがリクエストを受信した後、リクエスト パス内のサービス名 (例: ) を取得します
userservice
。 - 次に、ロード バランサーは、取得したサービス名を使用して、対応するサービスを Eureka Service からプルします。
- 実際の運用環境では、通常、サービスには複数のインスタンスがあるため、取得されるのは、このサービスのすべての通常のインスタンスの IP 番号とポート番号を含むサービス リストです。
- このリストを取得した後、ロード バランサーは現在採用されているロード バランシング ポリシーを使用して適切なサービスを選択し、そのサービスにアクセスします。
このプロセスにより、サービス リクエストが複数のインスタンスに合理的に分散され、負荷分散が実現されます。
2.2 負荷分散のためのリボンソースコード分析
まず、負荷分散を実装するためにリボンで使用されるクラスはLoadBalancerInterceptor
負荷分散インターセプターと呼ばれ、IDEA を通じてそのソース コードを表示できます。
ClientHttpRequestInterceptor
クライアント HTTP リクエスト インターセプターであるインターフェイスが実装されていることがわかりました。
RestTemplate
これは、発生する HTTP リクエストをインターセプトします。ClientHttpRequestInterceptor
これはインターフェイスであり、intercept
メソッドが含まれています。したがって、LoadBalancerInterceptor
このインターフェイスを実装するクラスとして、intercept
メソッドもオーバーライドする必要があります。この時点で、このメソッドにブレークポイントを設定してデバッグを追跡できます。コードの実行:
-
request.getURI()
リクエストアドレスを取得します。
-
originalUri.getHost()
要求されたアドレスのホスト名が取得されますが、このとき取得されるのはサービスの名前ですuserservice
。
3. サービスの名前を見つけたら、次に行うことは、対応するサービスを EurekaServer からプルし、このメソッドは取得したサービス名を (リボン負荷分散クライアント) に渡して処理しますRibbonLoadBalancerClient
。
4.コードのデバッグを続行し、execute
次のメソッドを入力します。
LoadBalancer
さらに下に進むと、「Dynamic Service List Balancer」というオブジェクトが表示されます
。このオブジェクトの内容を見ると、サービス リスト内のサービスの数が 3 つであり、これら 3 つのサービスが含まれていることがわかります。user-service
EurekaServerに登録されている3つのサービスを取得します。
したがって、getLoadBalancer
このメソッドの機能は、サービス名に基づいて EurekaServer 内のサービス リストを検索することです。サービス リストを見つけたら、次のステップは負荷分散操作を実行することであると大胆に推測できます。
-
この時点で、次の
getServer
メソッドを入力します。
-
次に、メソッドが呼び出され
chooseServer
、次のメソッドに入ります。
8. 次にchooseServer
メソッドを入力し、最後にrule.choose
メソッドを見つけます。
この時点でルール オブジェクトを確認すると、それがインターフェイスであることがわかります。
これはインターフェイスであるため、実装クラスがあります。
このとき発見された実装クラスがロードバランシングルールです。一般的なルールには、ランダム化、ポーリングなどが含まれます。
- 最後に、デフォルトのルールに従って、ポート 8082 のサービスが選択されます。
次に、代わりに実際の IP とポート番号を使用してuserservice
、指定したサービスにアクセスできます。
上記は、Ribbon のロード バランシング実装のソース コード分析であり、デバッグ手法を通じてサービス ディスカバリとロード バランシングの実装プロセスを深く調査し、サービス ディスカバリとロード バランシングの操作方法をより深く理解するのに役立ちました。
3. リボン負荷分散戦略
3.1 負荷分散戦略
上記のソース コード分析を通じて、リボンの負荷分散ルールが IRule と呼ばれるインターフェイスによって定義されており、各サブインターフェイスがルールであることを見つけるのは難しくありません。
IRule インターフェイスの継承システムを次の図に示します。
リボンの負荷分散戦略は次のように要約できます。
組み込みの負荷分散ルールクラス | ルールの説明 |
---|---|
ラウンドロビンルール | サービス リストをポーリングしてサーバーを選択するだけです。これは、リボンのデフォルトの負荷分散ルールです。 |
可用性フィルタリングルール | ショートサーキットと高い同時実行性を持つサーバーを無視する |
WeightedResponseTimeRule | 各サーバーに重み値を与えます。サーバーの応答時間が長いほど、サーバーの重みは小さくなります。このルールはサーバーをランダムに選択し、この重み値はサーバーの選択に影響します。 |
ゾーン回避ルール | サーバーの選択は、その地域で利用可能なサーバーに基づいて行われます。ゾーンを使用してサーバーを分類します。このゾーンは、コンピューター室、ラックなどとして理解できます。次に、ゾーン内の複数のサービスをポーリングします。 |
BestAvailableルール | ショートサーキットしているサーバーを無視し、同時実行数が低いサーバーを選択します。 |
ランダムルール | 利用可能なサーバーをランダムに選択します |
再試行ルール | リトライ機構の選択ロジック |
リボンはこれらの組み込みの負荷分散ルールを提供し、カスタムの負荷分散ルールもサポートしています。実際のアプリケーションでは、ビジネス特性に基づいて適切な負荷分散戦略を選択することが非常に重要です。以下に、リボン負荷分散ポリシーへの変更を示します。
3.2 リボン負荷分散戦略の変更をデモンストレーションする
負荷分散ルールは、次の 2 つの方法で IRule 実装を定義することで変更できます。
- コード メソッド:
order-service
スタートアップOrderApplication
クラスで、新しいクラスを定義しIRule
、@Bean
アノテーションを使用して Spring コンテナーに登録します。
@Bean
public IRule randomRule(){
return new RandomRule();
}
- 構成ファイルの方法: ファイルに
order-service
新しいapplication.yml
構成を追加すると、ルールを変更することもできます。
# 修改 Ribbon 负载均衡策略
userservice:
ribbon:
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule # 负载均衡规则
たとえば、負荷分散ポリシーを変更した後にアクセス順序を再度使用した結果は次のとおりですorder-service
。user-service
サービスはポーリング方式ではなく、ランダムな方式で選択されていることがわかります。
4. リボンのハングリーロード
4.1 View リボンの遅延読み込み
サービスを再起動しorder-service
、ブラウザで注文にアクセスすると、次の現象が発生します。
サービスが開始されるとorder-service
、初めてサービスにアクセスするのに 300 ミリ秒以上かかることがわかります。
その後、何度もアクセスすると、消費時間が 10 ミリを超えていることがわかります。
上記の現象から、リボンは、シングルトン モードの遅延モードと同様に、デフォルトで遅延読み込みモードを採用していることがわかります。LoadBalanceClient
インスタンスは必要な場合にのみ作成され、リクエスト時間は非常に長くなります。
4.2 リボンのハングリーロードモード
前述の遅延読み込みの時間がかかる問題を解決するために、Ribbon にはハングリー ローディング モードも用意されており、プロジェクトの開始時にハングリー ローディングが作成され、最初の訪問の時間が短縮されます。
次の構成を通じてハングリー ロードを有効にします。
order-service
この時点でサービスを再起動します。
サービスを開始すると、さらに多くのログがあることがわかります。
このログの内容は、LoadBalanceClient
インスタンスのロードによって生成されたログです。
order-service
再度初めてサービスにアクセスすると、所要時間が短縮されていることがわかります。