-MotanのSPIプラグイン拡張メカニズムMotanシリーズ

オープンソースを推奨します

ワンストップのパフォーマンス監視ツール(オープンソースプロジェクト)をお勧めします

ペッパー・メトリックは、オープンソースコンポーネントを持つ同僚と開発され、主な機能は、人気の高いオープンソースコンポーネントの比較的軽量な方法である(jedis / MyBatisの/ motan /ダボ /サーブレット) の統合、収集し、メトリックを計算し、ログに出力をサポートしていますそして、ディスプレイ上の友好grafanaダッシュボードをサポートする、配列データベースのさまざまな互換性のあるデータ形式に変換します。完全なプロジェクトのドキュメント、および新しいプラグインの開発を促進するために設計されたすべてのSPIベースのスケーラブルなアーキテクチャの原則の中で。ドッキングウィンドウ・コンベースの別のデモプロジェクトはすぐに効果の例を参照するには、デモを開始することができますもありますgithub.com/zrbcool/pepを...あなたはそれが役に立つ、そして、トラブルスターにも開発に参加する歓迎見つけた場合、あなたに感謝:)


ポイントを取得...

SPI 0 Motan

SPIと同様のSPIダボのMotanは、それが上の考えネイティブJava SPIに基づいて最適化、およびJavaネイティブSPIの使用に非常によく似ています。

はじめにJavaのネイティブSPIは、関連記事をたくさん持っている、ここではそれらを繰り返すありません。ここでは主に話の使用から始まる、MotanにSPIメカニズムを紹介します。

0.1 SPIの使用

ここではフィルタ機能拡張を実現するためには、例えば、彼が使用する方法について話しています。

フィルタアクションがにあるProvider終了、または要求受信Consumer特別な何かをするための要求を送信するときに傍受するが。ここからConsumer最後の視点、単一の要求応答時間要件の統計的計算を行います。

まず、実装するクラス記述する必要がcom.weibo.api.motan.filter.Filterインターフェイスfilterメソッドを。

@SpiMeta(name = "profiler")
public class ProfilerFilter implements Filter {
    
    @Override
    public Response filter(Caller<?> caller, Request request) {
        // 记录开始时间
        long begin = System.nanoTime();
        try {
            final Response response = caller.call(request);
            return response;
        } finally {
            // 打印本次响应时间
            System.out.println("Time cost : " + (System.nanoTime() - begin));
        }
    }
}
复制代码

第二に、META-INF/services名前のディレクトリを作成しcom.weibo.api.motan.filter.Filter、以下のように、文書内を:

# 例如:com.pepper.metrics.integration.motan.MotanProfilerFilter
#全限定名称#.MotanProfilerFilter
复制代码

そして、プロトコル設定へ filter

ProtocolConfigBean config = new ProtocolConfigBean();
config.setName("motan");
config.setMaxContentLength(1048576);
config.setFilter("profiler"); // 配置filter
return config;
复制代码

最後に、RefererConfigこれを使用することがProtocolConfigできます。

BasicRefererConfigBean config = new BasicRefererConfigBean();
config.setProtocol("demoMotan");
// ... 省略其他配置 ...

复制代码

したがって、でConsumerそれぞれ終了リクエストと印刷の応答時間を傍受することができます。

次に、我々はMotanがあまりにもこれを実行する方法を研究し続けています。

1つのSPI管理

MotanはでSPIを達成しますmotan-core/com/weibo/api/motan/core/extension次のように組織構造は、次のとおりです。

motan-core/com.weibo.api.motan.core.extension
    |-Activation:SPI的扩展功能,例如过滤、排序
    |-ActivationComparator:排序比较器
    |-ExtensionLoader:核心,主要负责SPI的扫描和加载
    |-Scope:模式枚举,单例、多例
    |-Spi:注解,作用在接口上,表明这个接口的实现可以通过SPI的形式加载
    |-SpiMeta:注解,作用在具体的SPI接口的实现类上,标注该扩展的名称

复制代码

内部データ構造の1.1管理

private static ConcurrentMap<Class<?>, ExtensionLoader<?>> extensionLoaders = new ConcurrentHashMap<Class<?>, ExtensionLoader<?>>();
private ConcurrentMap<String, T> singletonInstances = null;
private ConcurrentMap<String, Class<T>> extensionClasses = null;
private Class<T> type;
private ClassLoader classLoader; // 类加载使用的ClassLoader
复制代码

extensionLoadersクラス変数である、彼はによって管理され@Spi、その注釈ラベル・インターフェース・ExtensionLoaderグローバルマネージャーとしてマッピング、すべてのSPI。

singletonInstances現在のメンテナンスExtensionLoaderシングルトン延長。

extensionClasses現在のメンテナンスExtensionLoader拡張クラスオブジェクトのすべてのインスタンスは、(class.newInstanceによって作成された)複数のケースを作成するために使用されます。

type現在のメンテナンス@Spi注釈注釈インタフェースclassオブジェクト。

1.2 ExtensionLoader初期化

Motan次の方法で(フィルタSPIは、一例として上記)ExtensionLoaderを初期化することができます。

// 初始化 Filter 到全局管理器 `extensionLoaders` 中
ExtensionLoader extensionLoader = ExtensionLoader.getExtensionLoader(Filter.class);

复制代码

その後、我々は、特定のを見てExtensionLoader.getExtensionLoader(Filter.class)「何をやりました。

public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) {
    // 检查type是否为空、type是否是接口类型、type是否被@Spi标注,检查失败会抛出异常
    checkInterfaceType(type);
    // 尝试从上文提到的 `extensionLoaders` 管理器中获取已有的ExtensionLoader
    ExtensionLoader<T> loader = (ExtensionLoader<T>) extensionLoaders.get(type);

    // 获取失败的话,尝试扫描并加载指定type的扩展,并初始化之
    if (loader == null) {
        loader = initExtensionLoader(type);
    }
    return loader;
}
复制代码

そして、見てみましょうinitExtensionLoader何をした方法。

// synchronized锁控制,防止并发初始化
public static synchronized <T> ExtensionLoader<T> initExtensionLoader(Class<T> type) {
    ExtensionLoader<T> loader = (ExtensionLoader<T>) extensionLoaders.get(type);
    // 二层检查,防止并发问题
    if (loader == null) {
        // type会赋值给实例变量 `type`,并初始化实例变量 `classLoader` 为当前线程上线文的ClassLoader
        loader = new ExtensionLoader<T>(type);
        // 添加到全局管理器 `extensionLoaders` 中
        extensionLoaders.putIfAbsent(type, loader);

        loader = (ExtensionLoader<T>) extensionLoaders.get(type);
    }

    return loader;
}
复制代码

この時点で、我々は初期化FilterインタフェースをExtensionLoader、そしてそれをホストしているextensionLoaders途中。

1.3 SPIインスタンス指定された拡張子を読み込みスキャンと取得

Motan それはあなたが最初に特定の拡張子の特定のインスタンスを取得するときに、拡張例のすべてをスキャンし、ロードします、遅延ロード戦略です。

たとえば、私たちは道によって上記で作成した名前を取得することができますprofilerFilterインタフェースの拡張インスタンスを。

// 初始化 Filter 到全局管理器 `extensionLoaders` 中
ExtensionLoader extensionLoader = ExtensionLoader.getExtensionLoader(Filter.class);
Filter profilterFilter = extensionLoader.getExtension("profiler");
复制代码

Filterインターフェイスは、ExtensionLoader実際には初めてですextensionLoader.getExtension("profiler")しながら、完成します。具体的に次のような外観getExtensionの方法は何でした。

public T getExtension(String name) {
    // Notes:就是通过这个checkInit方法扫描和加载的
    checkInit();
    // .. 暂时省略 ..
}
复制代码

勉強し続けるcheckInit方法を。

private volatile boolean init = false;
private void checkInit() {
    // 用init标记,只初始化一次
    if (!init) {
        loadExtensionClasses();
    }
}

private static final String PREFIX = "META-INF/services/";
private synchronized void loadExtensionClasses() {
    if (init) {
        return;
    }
    // 扫描和加载
    extensionClasses = loadExtensionClasses(PREFIX);
    singletonInstances = new ConcurrentHashMap<String, T>();

    init = true;
}
复制代码

loadExtensionClassesスキャンする方法META-INF/services/すべてのファイルを、ファイルの内容を解析し、それを呼び出しloadClass、次のようにメソッドが実装されている、方法を:

// classNames 就是每个文件中的具体扩展实现的全限定名称。
private ConcurrentMap<String, Class<T>> loadClass(List<String> classNames) {
    ConcurrentMap<String, Class<T>> map = new ConcurrentHashMap<String, Class<T>>();

    for (String className : classNames) {
        try {
            Class<T> clz;
            if (classLoader == null) {
                clz = (Class<T>) Class.forName(className);
            } else {
                clz = (Class<T>) Class.forName(className, true, classLoader);
            }

            checkExtensionType(clz);
            // 获取 @SpiMeta 注解声明的名称
            String spiName = getSpiName(clz);

            if (map.containsKey(spiName)) {
                failThrows(clz, ":Error spiName already exist " + spiName);
            } else {
                map.put(spiName, clz);
            }
        } catch (Exception e) {
            failLog(type, "Error load spi class", e);
        }
    }

    return map;
}
复制代码

物事を行う方法は、すべての合法的な拡張クラスを取得することです。最終的な戻り値は、インスタンス変数に代入されextensionClasses、それによって走査及びローディング作業を完了する。

PS:上記、extensionClassesからは@SpiMeta KVマッピングクラス名とそれに対応する拡張子を特定の実装です。テキストの上にProfilerFilter、例えば、それは、KEY =プロファイラ、VALUE = ProfilerFilter.class

特定の拡張機能の実装のための1.4 SPI

ちょうど探し続けgetExtensionプロセスの一部が省略されます。

public T getExtension(String name) {
    checkInit();

    if (name == null) {
        return null;
    }

    try {
        Spi spi = type.getAnnotation(Spi.class);

        // 获取单例
        if (spi.scope() == Scope.SINGLETON) {
            return getSingletonInstance(name);
        } else {
            // 获取多例
            Class<T> clz = extensionClasses.get(name);

            if (clz == null) {
                return null;
            }

            return clz.newInstance();
        }
    } catch (Exception e) {
        failThrows(type, "Error when getExtension " + name, e);
    }

    return null;
}
复制代码

例の状況を複数取得することを容易に直接良いの正面からロードすることができextensionClasses、それが取得した場合、取得newInstance()新しいインスタンスを。

単一の外観の例次の例:getSingletonInstanceメソッド。

private T getSingletonInstance(String name) throws InstantiationException, IllegalAccessException {
    T obj = singletonInstances.get(name);

    if (obj != null) {
        return obj;
    }

    Class<T> clz = extensionClasses.get(name);

    if (clz == null) {
        return null;
    }

    synchronized (singletonInstances) {
        obj = singletonInstances.get(name);
        if (obj != null) {
            return obj;
        }

        obj = clz.newInstance();
        singletonInstances.put(name, obj);
    }

    return obj;
}
复制代码

単一の実施形態の取得、それは実施例の単一セットから優先的に試みるsingletonInstances単一の実施形態(具体例、またはまったく対応する名前)シングルトンセットに追加された本実施例に記載し、その後の試行ではない、取得していない場合、取得extensionClassesに取得しますあなたはまだ取得できない場合は、本当になくなって、取得した場合、コレクションに新しいシングルトンインスタンスになります。

これらは、達成するための具体的なMotan獲得モードSPI展開しています。

上記はMotan SPIメカニズムです。


私は、マイクロチャネル公衆番号に注意を歓迎します

いいえ公共ありません

おすすめ

転載: juejin.im/post/5d7f26a06fb9a06b2a2070df