背景の説明
dubboは、特に強力なスケーラビリティフレームワークです , JAVA SPIのアイデアに基づいて改善されたDubbo SPI , インターフェイスと完全に切り離された実装 .レジストリ、通信プロトコル、シリアライズおよびその他の拡張機能を提供します。そして、さまざまなシナリオのための独自の設計プロセスは、合理的な設計パターンを選択します。
Dubbo SPIの説明
Java SPI
strategyパターンを使います。インターフェイスのみを宣言し、具体的な実装はプログラム内で直接決定せず、プログラム外の設定によって具体的な実装を組み立てるために使用します。インターフェースとメソッドを定義します。インタフェースの実装クラスを書きます。
//SPIインターフェースの定義
public interface HelloService {
void sayHello();
}
//実装クラスの定義
public class ChineseHello implements HelloService {
@Override
public void sayHello() {
System.out.println("こんにちは!");
}
}
//ServiceLoaderを使用して、インターフェースのすべての実装クラスをロードします。
public static void main(String[] args) {
ServiceLoader<HelloService> helloServices = ServiceLoader.load(HelloService.class);
for (HelloService helloService: helloServices){
helloService.sayHello();
}
}
//出力 こんにちは!
Dubbo SPI
公式文書にはこうあります:
JDKの標準SPIは、ポイントのすべての実装の拡張機能の1回限りのインスタンス化され、初期化の拡張機能がある場合は、非常に時間がかかり、使用されていない場合にもロードされ、リソースの無駄です。
拡張機能の読み込みに失敗した場合でも、拡張機能の名前を取得することはできません。JDKの標準のScriptEngineは、スクリプトの種類の名前を取得するgetName()を介して、RubyScriptEngineの場合は、引用jruby.jarのため、RubyScriptEngineクラスの読み込みに失敗した結果、存在しない場合、この失敗の理由は "食べられました!この失敗理由は "eated "であり、Rubyには対応していないため、ユーザがRubyスクリプトを実行すると、本当の失敗理由ではなく、Rubyがサポートされていないことが報告されます。
//カタログのMETA-INF/dubbo/internalHelloServiceのデフォルト実装クラスを
impl=com.test.spi.ChineseHello
//SPIインターフェースの定義
public interface HelloService {
void sayHello();
}
//実装クラスの定義
public class ChineseHello implements HelloService {
@Override
public void sayHello() {
System.out.println("こんにちは!");
}
}
//Dubbo SPIを呼び出して設定ファイルから情報をロードし、デフォルトの実装クラスをロードします。
public class SayHelloMain {
public static void main(String[] args) {
HelloService helloService = ExtensionLoader.getExtensionLoader(HelloService.class)
.getDefaultExtension();
helloService.sayHello();
}
}
//出力 こんにちは!
JAVA SPIのロードの失敗は、様々な理由で例外情報が "飲み込まれる"、問題をトレースすることがより困難に起因する可能性があります。 拡張機能のロードの失敗のDubbo SPIは、最初の実際の例外をスローし、ログを印刷します。パッシブローディングの拡張機能は、たとえ障害の一部が、他の拡張機能やフレームワーク全体の使用には影響しません。
- Dubbo SPI は、IOC と AOP メカニズムを独自に実装しています。
- Dubboはラッピング拡張クラスをサポートしており、AOP機能の拡張ポイントを実装するための汎用抽象ロジックをラッピングクラスに置くことを推奨します。
延長ポイントの構成仕様
Dubbo SPIはJAVA SPIと同様で、対応するSPI設定ファイルをMETA-INF//dubbo/に配置する必要があります。コンフィギュレーションファイルは、キー=拡張ポイント実装クラスのパス名として使用されます。入力されるパラメータ内のDubbo SPIアノテーションのキー。JAVA SPIコンフィギュレーションパスとコンテンツコンフィギュレーションと互換性があります。Dubboは起動時にMETA-INF/services/, META-INF/dubbo/, META-INF/dubbo/internal/の3つのファイルをデフォルトでスウィープします。
拡張ポイントの分類とキャッシング
クラス・キャッシュとインスタンス・キャッシュに分類されます。この2種類のキャッシュは、拡張クラスの種類によって、通常の拡張クラス、ラッパー拡張クラス、適応拡張クラスに分けられます。
クラスキャッシュ: Dubbo SPI が拡張クラスをフェッチすると、まずキャッシュから読み取ります。キャッシュに存在しない場合は、設定ファイルをロードし、設定に従ってクラスをメモリにキャッシュします。
インスタンスキャッシュ: パフォーマンスを考慮し、Dubbo フレームワークはクラスだけでなく、クラスによってインスタンス化されたオブジェクトもキャッシュします。キャッシュが読み込めない場合は、リロードしてキャッシュします。キャッシュされたクラスがインスタンス化されませんので、インスタンス化とキャッシュの需要に応じて、パフォーマンスが向上します。
- 共通拡張クラス:SPI設定ファイルで設定される最も基本的な拡張クラス実装。
- Wrapper拡張クラス :Wrapperクラスは特定の実装を持たず、一般的なロジックを抽象化するだけで、拡張インターフェースの特定の実装のコンストラクタ・メソッドを渡す必要があります。Dubboの自動パッケージング機能に属します。
- 適応型拡張クラス:拡張インターフェイスには複数の実装クラスがあり、どの実装クラスを使うかは実行時に動的に決定されます。これは拡張ポイントの適応機能です。
- その他のキャッシュ:エクステンション・クラス・ローダー・キャッシュ、エクステンション・キャッシュなど。
延長点の特徴
オートパッケージ、オートローディング、アダプティブ、オートアクティベーション。
}
ProteocolFilterWrapperは、Protocolインターフェースを実装していますが、 コンストラクタでProtocol型のパラメータを渡します。従って、ProteocolFilterWrapperはWrapperクラスとして認識されます。これは、一般的な抽象ロジックをカプセル化したり、 サブクラスがより具体的な実装に集中できるように補強するデコレーター・パターンです。
-
@Adaptive({Constants.CLIENT_KEY,Constants.TRANSPORTER_KEY}}) Client connect(URL url,ChannelHandler handler); }.
Adaptive は2つのパラメータを渡します。外部が Transporter#bin メソッドを呼び出すと、キーパラメータ "server" の値を入力パラメータ "URL" から動的に抽出します。もし拡張モジュールの実装クラスとマッチすれば、対応する実装クラスを使用します。マッチしなければ、2 番目のキーを渡します。言い換えると、@Adaptive は複数のパラメータを渡し、例外がスローされるまで順番に実装をマッチさせます。クラスの複数の実装を読み込む必要がある場合はどうしますか?これは最後の機能である自動起動に関係します。
- 自動アクティブ化:@Activateアノテーションを使用すると、対応する拡張ポイントをデフォルトで有効にアクティブ化するようにマークすることができます。また、@Activateアノテーションに異なるパラメータを渡すことで、異なる条件下で拡張ポイントが自動的にアクティブになるように設定することもできます。主な使用シナリオは、拡張ポイントの複数の実装を同時にアクティブにする必要がある場合です。 1.拡張ポイントのアノテーション:@SPI はクラス、インターフェース、列挙で使用でき、Dubbo フレームワークではインターフェースで使用します。インターフェイスをマークする役割は Dubbo SPI 接続、つまり拡張ポイントであり、異なる実装がいくつも存在する可能性があります。ランタイムは特定の実装クラスを見つけるように設定する必要があります。
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
public @interface SPI {
String value() default "";
}
SPIにはvalue属性があり、この属性を通して、このインターフェースのデフォルトの実装クラスを設定するための異なるパラメータを渡すことができます。例えば、Protocol のデフォルトは dubbo です。
@SPI("dubbo")
public interface Protocol {
int getDefaultPort();
@Adaptive
<T> Exporter<T> export(Invoker<T> var1) throws RpcException;
@Adaptive
<T> Invoker<T> refer(Class<T> var1, URL var2) throws RpcException;
void destroy();
}
getExtension(Class型, String名)を通して、多くの場所でDubboが拡張ポイントインターフェースの実装を取得します。この時点で、クラスはチェックサムを行い、インターフェイスであるかどうか、また@SPIアノテーションがあるかどうかを判断します。
拡張ポイント適応アノテーション:@Adaptive
Adaptiveアノテーションはクラス、インターフェイス、列挙、メソッドで使用できますが、Dubboフレームワーク全体でクラスレベルで使用されているのは数カ所だけです。それ以外はすべてメソッドにアノテーションされます。メソッドにアノテーションがある場合、それはメソッド・レベル・アノテーションであり、適応機能のセクションで説明するように、パラメータを介して動的に実装クラスを取得することができます。メソッド・レベルのアノテーションは、最初の getExtension で、自動的に動的なアダプ ティブ・クラスを生成してコンパイルし、動的な実装クラスの効果を実現します。例えば、Protocol インターフェースは、@Adaptive アノテーションを export インターフェースと refer インターフェースに追加します。Dubbo が拡張ポイントを初期化すると、Protocol$Adaptive クラスが生成されます。実際の実装クラスです。これはデコレータパターンに似ています。
@Adaptive注釈付きコード
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface Adaptive {
String[] value() default {};
}
アノテーションは値 value を渡すこともでき、配列になります。
- 1.Adaptiveアノテーションのインターフェイスを初期化するとき、キー値は最初に渡されたURLと照合されます。
- 2、最初のキーは、すべてのキーの一致が完了するまで、後続のキーと一致し続けるためにキーと一致しませんでした。
- 3、まだマッチしていない場合は、「ハンプ・ルール」を使ってマッチします。
- 4.マッチしなかった場合は、IllegalStateExceptionをスローします。
ハンプルール:ラッパークラスがAdaptiveのキー値と一致しない場合、Dubboは自動的にハンプケースに従ってインターフェイス名を分離し、". "で接続します。で接続します。例えば、org.apache.dubbo.xxx.HelloInovkerWrapperのHelloInvokerWrapperは、hello.invoker.wrapperに変更されます。
一部の実装クラスに @Adaptive アノテーションが付けられているのはなぜですか?
- 1、クラスの実装に配置され、主に直接達成するために動的にコードを生成する必要なく、対応する実装を修正するだけで、戦略パターンのように直接クラスの実装を決定します。
- 2、コードの実装では次のとおりです。ExtensionLoaderは、@Adaptiveに関連する2つのオブジェクトをキャッシュし、1つはcachedAdaptiveClassにキャッシュされ、つまり、Classのクラス型のAdaptive固有の実装です;
- 3.もう 1 つのキャッシュは、Class のインスタンス化された特定のオブジェクトである cachedAdaptiveInstance にあります。
- 4は、拡張ポイントの初期化では、クラスの実装で見つかった場合は、@Adaptiveアノテーションを持って、それが直接cachedAdaptiveClass、クラスのその後のインスタンス化に割り当てられている、それはコードの動的生成では、直接インスタンス化cachedAdaptiveClass、およびにキャッシュの強さにされません。cachedAdaptiveInstance.
- 5.インターフェイスメソッドにアノテーションされた場合、パラメータに応じて、動的に拡張ポイントの実装を取得し、適応クラスを生成し、cachedAdaptiveInstanceにキャッシュされます。
public class ExtensionLoader<T> {
//実装クラス型 cachedAdaptiveClass
private volatile Class<?> cachedAdaptiveClass = null;
private final ConcurrentMap<String, Holder<Object>> cachedInstances = new ConcurrentHashMap();
private String cachedDefaultName;
//インスタンス化オブジェクト cachedAdaptiveInstance
private final Holder<Object> cachedAdaptiveInstance = new Holder();
拡張ポイント自動起動アノテーション: @Activate
Activateは、クラス、インターフェイス、列挙クラス、メソッドで指定できます。これは主に、複数の拡張ポイントの実装を異なる条件に基づいてアクティブにする必要がある場合に使用します。たとえば、フィルタ実装はそれぞれ異なる関数であるため、フィルタは同時に複数回アクティブにする必要があります。
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface Activate {
//URLのグループ。
String[] group() default {};
//キー値がルックアップURLに存在する場合は
String[] value() default {};
//このエクステンションにどのエクステンションを含めるかを指定します。
String[] before() default {};
//このエクステンションにどのエクステンションを含めるかを指定します。
String[] after() default {};
//情報の並べ替え
int order() default 0;
}