SpringCloudの偽のコア原則
Spring CloudでのFeignのコア原則を理解しておらず、Spring Cloudのパフォーマンスの最適化と構成の最適化を実際に理解していない場合、SpringCloudを真に習得することは不可能です。
この章では、Feignリモート呼び出しの重要なコンポーネントから始め、Feighリモート呼び出しの実行プロセス、FeignのローカルJDKプロキシインスタンスの作成プロセスを紹介し、SpringCloudのコア知識をすべての人に徹底的に解釈します。そのため、エンジニアの大多数は何が起こっているのかを知っているだけでなく、その理由も知っています。
Feignリモートコールの基本的なプロセス
偽のリモートコール、コアは一連のカプセル化と処理を経て、JAVAアノテーションで定義されたリモートコールAPIインターフェースは最終的にHTTPリクエスト形式に変換され、HTTPリクエストの応答結果はJAVA Beanにデコードされ、次に戻る呼び出し側。Feignリモートコールの基本的なプロセスは、おおよそ次の図に示すとおりです。
上の図からわかるように、Feignはアノテーションを処理してリクエストをテンプレート化し、実際に呼び出されると、パラメーターが渡され、パラメーターに従ってリクエストに適用されてから、実際のリクエストリクエストに変換されます。FeignとJAVAの動的プロキシメカニズムにより、Java開発者はHTTPフレームワークを使用せずにリモートサービスのHTTP呼び出しを完了してHTTP要求メッセージをカプセル化できます。
Feignリモートコールの重要なコンポーネント
マイクロサービスが開始されると、Feignはパッケージをスキャンし、@ FeignClientで注釈が付けられたインターフェースの注釈規則に従って、リモートインターフェースのローカルJDKプロキシプロキシインスタンスを作成します。次に、これらのローカルプロキシプロキシインスタンスをSpringIOCコンテナに挿入します。リモートインターフェイスメソッドが呼び出されると、プロキシプロキシインスタンスは実際のリモートアクセスを完了し、結果を返します。
SpringCloudでのFeignの操作メカニズムと原理を明確に紹介するために、ここではまず、Feignのいくつかの重要なコンポーネントを整理します。
リモートインターフェース用のローカルJDKプロキシプロキシインスタンス
リモートインターフェースのローカルJDKプロキシプロキシインスタンスには、次の特徴があります。
(1)プロキシプロキシインスタンス。@ FeignClientアノテーションが付けられたリモートコールインターフェイスを実装します。
(2)プロキシプロキシインスタンス。HTTPリクエストをカプセル化し、HTTPリクエストを内部で送信できます。
(3)リモートHTTPリクエストの応答を処理し、結果のデコードを完了して、それを呼び出し元に返すことができるプロキシプロキシインスタンス。
例として、インターフェイスDemoClientを呼び出す単純なリモートサービスを取り上げて、リモートインターフェイスのローカルJDKプロキシプロキシインスタンスの作成プロセスを具体的に紹介します。
DemoClientインターフェイスには、リモート呼び出し用の2つの非常に単純な抽象メソッドがあります。1つはリモートURL "/ api / demo / hello / v1"のHTTPリクエストを完了するために使用されるhello()抽象メソッドです。もう1つはエコーです。 (...)抽象メソッド。リモートURL "/ api / demo / echo / {word} / v1"のHTTPリクエストを完了するために使用されます。詳細を下図に示します。
DemoClientインターフェイスコードは次のとおりです。
package com.crazymaker.springcloud.demo.contract.client;
//…省略import
@FeignClient(
value = "seckill-provider", path = "/api/demo/",
fallback = DemoDefaultFallback.class)
public interface DemoClient {
/**
* 测试远程调用
*
* @return hello
*/
@GetMapping("/hello/v1")
Result<JSONObject> hello();
/**
* 非常简单的一个 回显 接口,主要用于远程调用
*
* @return echo 回显消息
*/
@RequestMapping(value = "/echo/{word}/v1", method = RequestMethod.GET)
Result<JSONObject> echo(
@PathVariable(value = "word") String word);
}
上記のコードでは、@ FeignClientアノテーションがDemoClientインターフェイスに追加されていることに注意してください。つまり、Feignが起動すると、ローカルJDKプロキシプロキシインスタンスが作成され、SpringIOCコンテナに登録されます。
それの使い方?@Resourceアノテーションを介してタイプマッチング(ここではタイプはDemoClientインターフェイスタイプ)に従ってSpring IOCコンテナーからプロキシインスタンスを見つけ、必要なメンバー変数にアセンブルできます。
DemoClientのローカルJDKプロキシプロキシインスタンスで使用されるコードは次のとおりです。
package com.crazymaker.springcloud.user.info.controller;
//…省略import
@Api(value = "用户信息、基础学习DEMO", tags = {
"用户信息、基础学习DEMO"})
@RestController
@RequestMapping("/api/user")
public class UserController {
@Resource
DemoClient demoClient; //装配 DemoClient 的本地代理实例
@GetMapping("/say/hello/v1")
@ApiOperation(value = "测试远程调用速度")
public Result<JSONObject> hello() {
Result<JSONObject> result = demoClient.hello();
JSONObject data = new JSONObject();
data.put("others", result);
return Result.success(data).setMsg("操作成功");
}
//…
}
DemoClientのローカルJDKプロキシプロキシインスタンスの作成プロセスはより複雑であり、後で紹介されます。まず、他の2つの重要な論理コンポーネントを見てみましょう。
呼び出しハンドラーInvocationHandler
ご存知のとおり、JDKプロキシを介して動的プロキシクラスを生成するコアステップは、呼び出しハンドラをカスタマイズすることです。具体的には、JDKのjava.lang.reflectパッケージにInvocationHandler呼び出しハンドラインターフェイスを実装し、呼び出しを実装します。このインターフェースの。(...)抽象的なメソッド。
Feignのリモートインターフェイスのプロキシ実装クラスを作成するために、Feignは、feign-coreコアjarパッケージにあるFeignInvocationHandlerクラスと呼ばれる独自のデフォルト呼び出しハンドラーを提供します。もちろん、コールプロセッサは置き換えることができます。FeignをHystrixと組み合わせて使用すると、feign-hystrixjarパッケージに含まれるHystrixInvocationHandlerコールプロセッサクラスに置き換えられます。
デフォルトのコールハンドラFeignInvocationHandler
デフォルトの呼び出しハンドラーFeignInvocationHandlerは比較的単純なクラスであり、非常に重要なマップタイプのメンバーディスパッチマッピングがあり、リモートインターフェイスメソッドからMethodHandlerメソッドハンドラーへのマッピングを保持します。
前の例のDemoClientインターフェイスを例にとると、プロキシ実装クラスのFeignInvocationHandlerのディスパッチメンバーのメモリ構造図を図3に示します。
デフォルトの呼び出しハンドラーFeignInvocationHandleは、リモートメソッド呼び出しを処理するときに、Javaリフレクションメソッドインスタンスに従ってディスパッチマッピングオブジェクトで対応するMethodHandlerメソッドハンドラーを見つけ、それをMethodHandlerに渡して、実際のHTTPリクエストと結果の処理を完了します。前の例のDemoClientリモート呼び出しインターフェイスには、2つのリモート呼び出しメソッドがあります。したがって、プロキシ実装クラスの呼び出しハンドラーであるFeignInvocationHandlerのディスパッチメンバーには、2つのKey-Valueペアがあります。
FeignInvocationHandlerの主要なソースコードは次のとおりです。
package feign;
//...省略import
public class ReflectiveFeign extends Feign {
//...
//内部类:默认的Feign调用处理器 FeignInvocationHandler
static class FeignInvocationHandler implements InvocationHandler {
private final Target target;
//方法实例对象和方法处理器的映射
private final Map<Method, MethodHandler> dispatch;
//构造函数
FeignInvocationHandler(Target target, Map<Method, MethodHandler> dispatch) {
this.target = checkNotNull(target, "target");
this.dispatch = checkNotNull(dispatch, "dispatch for %s", target);
}
//默认Feign调用的处理
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//...
//首先,根据方法实例,从方法实例对象和方法处理器的映射中,
//取得 方法处理器,然后,调用 方法处理器 的 invoke(...) 方法
return dispatch.get(method).invoke(args);
}
//...
}
ソースコードは非常に単純で、invoke(...)メソッドに焦点が当てられています。コアコードは1行だけですが、その機能は複雑です。
(1)Javaリフレクションのメソッドインスタンスに従って、ディスパッチマッピングオブジェクトで対応するMethodHandlerメソッドハンドラーを見つけます。
(2)MethodHandlerメソッドプロセッサのinvoke(...)メソッドを呼び出して、実際のHTTP要求と結果の処理を完了します。
追加された説明:MethodHandlerメソッド・プロセッサーは、JDK動的プロキシー・メカニズムのjava.lang.reflectパッケージにあるInvocationHandler呼び出しプロセッサー・インターフェースとの継承または実装関係を持っていません。MethodHandlerは、非常にシンプルなインターフェイスであるFeignカスタムです。
** MethodHandler MethodHandler **
FeignのメソッドハンドラーMethodHandlerは、InvocationHandlerFactoryインターフェースで定義された独立したインターフェースであり、invoke(...)メソッドは1つだけです。ソースコードは次のとおりです。
//定义在InvocationHandlerFactory接口中
public interface InvocationHandlerFactory {
//…
//方法处理器接口,仅仅拥有一个invoke(…)方法
interface MethodHandler {
//完成远程URL请求
Object invoke(Object[] argv) throws Throwable;
}
//...
}
MethodHandlerのinvoke(...)メソッドは、主に実際のリモートURL要求を完了し、デコードされたリモートURLの応答結果を返す役割を果たします。Feignは、基本的なリモートURL同期要求処理を提供するデフォルトのSynchronousMethodHandler実装クラスを提供します。SynchronousMethodHandlerクラスとそのMethodHandlerとの関係について、おおまかに図4に示します。
メソッドプロセッサを完全に理解するには、SynchronousMethodHandlerメソッドプロセッサのソースコードを読んでください。これはおおよそ次のとおりです。
package feign;
//…..省略import
final class SynchronousMethodHandler implements MethodHandler {
//…
// 执行Handler 的处理
public Object invoke(Object[] argv) throws Throwable {
RequestTemplate requestTemplate = this.buildTemplateFromArgs.create(argv);
Retryer retryer = this.retryer.clone();
while(true) {
try {
return this.executeAndDecode(requestTemplate);
} catch (RetryableException var5) {
//…省略不相干代码
}
}
}
//执行请求,然后解码结果
Object executeAndDecode(RequestTemplate template) throws Throwable {
Request request = this.targetRequest(template);
long start = System.nanoTime();
Response response;
try {
response = this.client.execute(request, this.options);
response.toBuilder().request(request).build();
}
}
}
SynchronousMethodHandlerのinvoke(...)メソッドは、独自のexecuteAndDecode(...)メソッドを呼び出して、実行と結果のデコードを要求します。メソッドの作業手順:
(1)まず、RequestTemplateを介してテンプレートインスタンスをリクエストし、リモートURLリクエストインスタンスリクエストを生成します。
(2)次に、独自の偽のクライアントクライアントメンバーであるexcecute(...)を使用して要求を実行し、応答応答を取得します。
(3)応答の結果をデコードします。
偽のクライアントコンポーネントfeign.Client
クライアントコンポーネントはFeignの非常に重要なコンポーネントであり、URLリクエストのエンドツーエンドの実行を担当します。コアロジック:サーバーに要求を送信し、応答を受信した後に応答をデコードします。
feign.Clientクラスは、クライアントを表す最上位のインターフェイスであり、抽象メソッドは1つだけです。ソースコードは次のとおりです。
package feign;
/**客户端接口
* Submits HTTP {@link Request requests}.
Implementations are expected to be thread-safe.
*/
public interface Client {
//提交HTTP请求,并且接收response响应后进行解码
Response execute(Request request, Options options) throws IOException;
}
feign.Clientの実装クラスが異なるため、HTTPリクエストを完了するための内部コンポーネントとテクノロジーが異なるため、feign.Clientには複数の異なる実装があります。次にいくつかの例を示します。
(1)Client.Defaultクラス:デフォルトのfeign.Clientクライアント実装クラス。内部でHttpURLConnnectionを使用してURL要求処理を完了します。
(2)ApacheHttpClientクラス:内部でApachehttpclientオープンソースコンポーネントを使用してURL要求処理を完了するfeign.Clientクライアント実装クラス。
(3)OkHttpClientクラス:内部でOkHttp3オープンソースコンポーネントを使用してURL要求処理を完了するfeign.Clientクライアント実装クラス。
(4)LoadBalancerFeignClientクラス:Ribben負荷分散テクノロジーを内部的に使用してURL要求処理を完了するfeign.Clientクライアント実装クラス。
さらに、特別なシナリオで使用されるいくつかのfeign.Clientクライアント実装クラスがあり、独自のfeign.Client実装クラスをカスタマイズすることもできます。以下は、上記のいくつかの一般的なクライアント実装クラスの簡単な紹介です。
1つ:Client.Defaultクラス:
デフォルトのクライアントインターフェースの実装クラスとして、JDKに付属するHttpURLConnnectionクラスがClient.Default内で使用され、URLネットワーク要求を実装します。
JKD1.8では、HttpURLConnnectionの下部で非常に単純なHTTP接続プール技術が使用されていますが、そのHTTP接続の再利用能力は実際には非常に弱く、もちろんパフォーマンスは非常に低くなっています。具体的な理由については、以下の「SpringCloudと長い接続の詳細な分析」を参照してください。
2:ApacheHttpClientクラス
ApacheHttpClientクライアントクラス内では、ApacheHttpClientオープンソースコンポーネントを使用してURL要求を処理します。
コード開発の観点から、従来のJDK独自のURLConnectionと比較して、Apache HttpClientは使いやすさと柔軟性を向上させ、クライアントがHttp要求を送信しやすくするだけでなく、開発者がインターフェースをテストするのを容易にします。開発の効率を向上させるだけでなく、コードの堅牢性も促進します。
パフォーマンスの観点から、Apache HttpClientには接続プール機能があり、優れたHTTP接続再利用機能があります。接続プールを使用したApacheHttpClientのパフォーマンス向上倍数については、以下の比較テストを参照してください。
ApacheHttpClientクラスは、feign-httpclientの特別なjarパッケージにあります。使用する場合は、Mavenの依存関係またはその他のメソッドを使用して、一致するバージョンの特別なjarパッケージに注ぐ必要があります。
3:OkHttpClientクラス
OkHttpClientクライアントクラス内では、OkHttp3オープンソースコンポーネントを使用してURL要求処理を完了します。OkHttp3オープンソースコンポーネントは、HttpUrlConnectionとApacheHttpClientを置き換えるためにSquareによって開発されました。OkHttp3はSPDYプロトコル(SPDYはネットワーク遅延を最小限に抑え、ネットワーク速度を上げ、ユーザーのネットワークエクスペリエンスを最適化するためにGoogleが開発したTCPベースのトランスポート層プロトコルです)をより適切にサポートするため、Android4.4以降、GoogleはHttpURLConnectionの置き換えを開始しましたOkHttpを使用してAndroidソースコードのクラスをリクエストします。つまり、Androidモバイルアプリ開発の場合、OkHttp3コンポーネントは基本的な開発コンポーネントの1つです。
4:LoadBalancerFeignClientクラス
LoadBalancerFeignClientは、内部でRibbenクライアントの負荷分散テクノロジーを使用してURLリクエスト処理を完了します。原則として、デリゲートパッケージプロキシモードが使用されます。Ribbenロードバランシングコンポーネントが適切なサーバーサーバーを計算した後、内部パッケージデリゲートプロキシクライアントがサーバーサーバーへのHTTP要求を完了します。カプセル化されたデリゲートクライアントプロキシインスタンスタイプはClientです。 .Defaultデフォルトクライアント、ApacheHttpClientクライアントクラスまたはOkHttpClient高性能クライアントクラス、またはその他のカスタムfeign.Clientクライアント実装タイプ。
次の図に示すように、LoadBalancerFeignClient負荷分散クライアント実装クラス。
リモートコール実行プロセスに対応
Feignリモート呼び出しインターフェースのJDKProxyインスタンスには複数のInvokeHandler呼び出しハンドラーがあるため、Feignリモート呼び出しの実行プロセスは少し異なりますが、リモート呼び出し実行プロセスの主な手順は同じです。ここでは、主に2種類のJDKプロキシインスタンスのInvokeHandlerコールプロセッサに関連するリモートコール実行プロセスを紹介します。
(1)デフォルトのコールハンドラFeignInvocationHandlerに関連するリモートコール実行プロセス。
(2)HystrixコールプロセッサHystrixInvocationHandlerに関連するリモートコール実行プロセス。
導入プロセスでは、以前のDemoClientのJDKプロキシリモートダイナミックプロキシインスタンスの実行プロセスを例として取り上げ、Feighリモートコールの実行プロセスを示して分析します。
** FeignInvocationHandler関連のリモート呼び出し実行フロー**
FeignInvocationHandlerはデフォルトの呼び出しハンドラーです。Feignに特別な構成が行われていない場合、Feignはこの呼び出しハンドラーを使用します。前のDemoClientのJDKプロキシリモート動的プロキシインスタンスのhello()リモート呼び出し実行プロセスと組み合わせて、ここでは、FeignInvocationHandlerに関連するリモート呼び出し実行プロセスの詳細な概要を次の図に示します。
リモートコール実行プロセス全体は、次のように大きく4つのステップに分けられます。
手順1:Spring IOCコンテナインスタンスを介してプロキシインスタンスをアセンブルし、リモート呼び出しを行います。
前述のように、Feignを起動すると、@ FeignClientアノテーションが付けられたすべてのリモートインターフェイス(DemoClientインターフェイスを含む)のローカルJDKプロキシプロキシインスタンスが作成され、SpringIOCコンテナに登録されます。ここでは、当面、このプロキシプロキシインスタンスをDemoClientProxyと呼びます。後で、このプロキシプロキシインスタンスの具体的な作成プロセスについて詳しく説明します。
次に、このインスタンスのUserController呼び出しコードで、@ Resourceアノテーションを使用してタイプまたは名前(ここではタイプはDemoClientインターフェイスタイプ)に従って照合し、Spring IOCコンテナーからプロキシインスタンスを見つけて、メンバーにアセンブルします。 @ResourceアノテーションがVariableにある場合、この例のメンバー変数の名前はdemoClientです。
hello()をリモートで呼び出す必要がある場合は、demoClientメンバー変数を介してJDKプロキシ動的プロキシインスタンスのhello()メソッドを直接呼び出すことができます。
ステップ2:InvokeHandlerを実行して、プロセッサのinvoke(...)メソッドを呼び出します
前述のように、JDKプロキシ動的プロキシインスタンスの実際のメソッド呼び出しプロセスは、InvokeHandler呼び出しプロセッサによって具体的に完了されます。したがって、ここでのDemoClientProxyプロキシインスタンスは、デフォルトのFeignInvocationHandlerコールハンドラインスタンスのinvoke(...)メソッドを呼び出します。
FeignInvocationHandler呼び出しプロセッサの詳細な紹介を通じて、デフォルトの呼び出しプロセッサFeignInvocationHandleがリモート呼び出しメソッドインスタンスとメソッドプロセッサのKey-Valueマッピングを維持していることを誰もがすでに知っています。invoke(...)メソッドのFeignInvocationHandleは、Javaによって反映されたメソッドインスタンスに従って、ディスパッチマッピングオブジェクト内の対応するMethodHandlerメソッドプロセッサを検索し、Javaによって反映されたメソッドインスタンスが実際のHTTPリクエストと結果の処理を完了します。
したがって、ステップ2で、FeignInvocationHandleはディスパッチマッピングからhello()メソッドに対応するMethodHandlerメソッドハンドラーを見つけ、そのinvoke(...)メソッドを呼び出します。
ステップ3:MethodHandlerメソッドハンドラーのinvoke(...)メソッドを実行します
MethodHandlerメソッドプロセッサに関する非常に詳細なコンポーネントの紹介を通じて、feignのデフォルトのメソッドプロセッサがSynchronousMethodHandlerであり、そのinvoke(...)メソッドが主に内部メンバーのfeignクライアントメンバークライアントを介してリモートURL要求の実行を完了することを誰もが知っています。結果。
feign.Clientクライアントには多くの種類があり、種類が異なれば、URL要求の処理を完了するための特定の方法も異なります。
ステップ4:feign.Clientクライアントメンバーを介して、リモートURLリクエストの実行を完了し、リモート結果を取得します
MethodHandlerメソッドプロセッサインスタンスのクライアントクライアントがデフォルトのfeign.Client.Default実装クラスである場合は、JDKに付属のHttpURLConnnectionクラスを使用して、リモートURL要求の実行を完了し、リモート結果を取得します。
MethodHandlerメソッドプロセッサインスタンスのクライアントクライアントがApacheHttpClientクライアント実装クラスである場合は、Apache httpclientオープンソースコンポーネントを使用して、リモートURL要求の実行を完了し、リモート結果を取得します。
上記の4つの手順により、SpringCloudでの偽のリモート呼び出し実行プロセスと操作メカニズムを明確に理解できるはずです。
実際、デフォルトの呼び出しフローを簡潔に導入するために、上記のフローでは実際には1つのステップが省略されています。3番目のステップは実際には2つの小さなステップに分割できます。どうして?SynchronousMethodHandlerは、リモートURL要求を直接完了しませんが、負荷分散メカニズムを介して、適切なリモートサーバーサーバーを見つけてから、実際のリモートURL要求を完了します。つまり、SynchronousMethodHandlerインスタンスのクライアントメンバーは、実際にはfeign.Client.Defaultタイプではなく、LoadBalancerFeignClientクライアントの負荷分散タイプです。したがって、上記の3番目のステップは、さらに細かく分割すると、おおまかに次のようになります。(1)まず、クライアントの負荷分散LoadBalancerFeignClientインスタンスを本質的に担当するSynchronousMethodHandlerの内部クライアントインスタンスを介して、最初にリモートサーバーサーバーを見つけます。(2 )次に、LoadBalancerFeignClientインスタンスによってラップされたfeign.Client.Default内部クラスインスタンスは、サーバー側サーバーにURL要求処理を完了するように要求します。
最後に、FeignInvocationHandlerに関連するデフォルトのリモート呼び出し実行プロセスは、操作メカニズムと呼び出しパフォーマンスの観点から本番環境の要件を満たすことができないことを説明します。なぜですか。主な理由は次のとおりです。
(1)リモート通話中のヒューズの監視および回復メカニズムはありません。
(2)高性能HTTP接続プール技術も使用されていません。
転送元:https://www.cnblogs.com/crazymakercircle/p/11965726.html