日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網為廣大站長提供免費收錄網站服務,提交前請做好本站友鏈:【 網站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(50元/站),

點擊這里在線咨詢客服
新站提交
  • 網站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會員:747

Dubbo自適應擴展機制

Dubbo里除了Service和Config層為API,其它各層均為SPI。相比于JAVA中的SPI僅僅通過接口類名獲取所有實現,Dubbo的實現可以通過接口類名和key值來獲取一個具體的實現。通過SPI機制,Dubbo實現了面向插件編程,只定義了模塊的接口,實現由各插件來完成。

1. 使用方式

1.1 Java SPI

在擴展類的jar包內,放置擴展點配置文件META-INF/service/接口全限定名,內容為:擴展實現類全限定名,多個實現類用換行符分隔。

如下為MySQL中Driver接口的實現:

Dubbo自適應擴展機制

 

package com.mysql.jdbc;
import java.sql.SQLException;
public class Driver extends NonRegisteringDriver implements java.sql.Driver {
    ... }

調用時使用ServiceLoader加載所有的實現并通過循環來找到目標實現類

ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);
Iterator<Driver> driversIterator = loadedDrivers.iterator();try{
    while(driversIterator.hasNext()) {
        driversIterator.next();    }} catch(Throwable t) {
// Do nothing
}

1.2 Dubbo SPI

拿Dubbo中Protocol接口來說,Protocol的定義如下:

package org.Apache.dubbo.rpc;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.extension.Adaptive;
import org.apache.dubbo.common.extension.SPI;
@SPI("dubbo")
public interface Protocol {    int getDefaultPort();
    @Adaptive
    <T> Exporter<T> export(Invoker<T> invoker) throws RpcException;    @Adaptive
    <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException;    void destroy();
}

需要指出的是Invoker繼承了Node接口,而Node接口提供了getUrl方法,每個方法都能從入參獲得URL對象

public interface Node {
    URL getUrl();
    boolean isAvailable();
    void destroy();
}

要求

1.接口上有org.apache.dubbo.common.extension.SPI注解,提供默認的實現

2.對于支持自適應擴展的方法要求方法入參能獲得org.apache.dubbo.common.URL對象,同時方法上有org.apache.dubbo.common.extension.Adaptive注解,Adaptive注解可以提供多個key名,以便從URL中獲取對應key的值,從而匹配到對應的實現(這里Protocol比較特別,沒有提供key名也能根據URL來動態獲取實現,后面會說明)

3.在擴展類的jar包內,放置擴展點配置文件META-INF/dubbo/接口全限定名,內容為:配置名=擴展實現類全限定名,多個實現類用換行符分隔。如Protocol的默認實現:

Dubbo自適應擴展機制

 

注意:META-INF/dubbo/internal為Dubbo內部實現的配置文件路徑

調用時通過ExtensionLoader根據需要來選擇具體的實現類,

ExtensionLoader<Protocol> loader = ExtensionLoader.getExtensionLoader(Protocol.class);

可選方式包括

1.選擇默認的實現

Protocol protocol = loader.getDefaultExtension();

2.根據指定key選擇實現

Protocol protocol = loader.getExtension("dubbo");

3.根據URL參數動態獲取實現

Protocol protocol = loader.getAdaptiveExtension();

2. Dubbo SPI 特性

Dubbo對Java中的標準SPI進行了擴展增強,官方文檔中提到其提供了如下特性:

  • 擴展點自動包裝
  • 擴展點自動裝配
  • 擴展點自適應
  • 擴展點自動激活

在介紹各個特性前先介紹下大概的內部實現。從上面的使用中可以看到Dubbo對SPI擴展的主要實現在ExtensionLoader類中,關于這個類源碼的講解可以看官方文檔,講解的很詳細,這邊主要說下大概過程:

  1. 根據傳入的類型從classpath中查找META-INF/dubbo/internal和META-INF/dubbo路徑下所有對應的擴展點配置文件
  2. 讀取擴展點配置文件中所有的鍵值對
  3. 根據鍵值對緩存Class對象,如果類有同類型參數的構造函數,則為包裝類,會緩存在另一個容器中
  4. 實例化對象,緩存后并返回

2.1 擴展點自動包裝

在返回真正實例前,會用該類型的包裝類進行包裝,既采用裝飾器模式進行功能增強。

if (CollectionUtils.isNotEmpty(wrApperClasses)) {
    // 循環創建 Wrapper 實例
    for (Class<?> wrapperClass : wrapperClasses) {
        // 將當前 instance 作為參數傳給 Wrapper 的構造方法,并通過反射創建 Wrapper 實例。
        // 然后向 Wrapper 實例中注入依賴,最后將 Wrapper 實例再次賦值給 instance 變量
        instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
    }
}

其實就是使用靜態代理來實現AOP

Dubbo自適應擴展機制

 

2.2 擴展點自動裝配

返回實例前會遍歷所有的setXXX方法,判斷set方法參數是否存在自適應對象,如果存在則通過ExtensionLoader加載自適應對象然后進行賦值,可以通過方法上加org.apache.dubbo.common.extension.DisableInject注解來屏蔽該功能。該功能其實就是實現了IOC,具體可以看ExtensionLoader的injectExtension方法。

2.3 擴展點自適應

同上面介紹的一樣,Dubbo的SPI可以根據傳入的URL參數中攜帶的數據來動態選擇具體的實現。

2.4 擴展點自動激活

上面介紹過,Adaptive注解是加在方法上的,類似的有個注解org.apache.dubbo.common.extension.Activate是加在實現類上。當加在Class上時,ExtensionLoader會將對應的key和Class信息緩存到另一個容器中,后續可以通過ExtensionLoader獲取某一類的實現列表,既如下方法

public List<T> getActivateExtension(URL url, String[] values){
    ...}

3. ExtensionLoader自適應擴展機制

ExtensionLoader自適應擴展機制的大概實現邏輯是這樣的:Dubbo會為拓展接口生成具有代理功能的代碼,然后通過 javassist 或 jdk 編譯這段代碼,得到 Class 類。最后再通過反射創建代理類,在代理類中,就可以通過URL對象的參數來確定到底調用哪個實現類。主要實現在createAdaptiveExtensionClass方法中。

private Class<?> createAdaptiveExtensionClass() {
    String code = new AdaptiveClassCodeGenerator(type, cachedDefaultName).generate();
    ClassLoader classLoader = findClassLoader();    org.apache.dubbo.common.compiler.Compiler compiler = ExtensionLoader.getExtensionLoader(org.apache.dubbo.common.compiler.Compiler.class).getAdaptiveExtension();
    return compiler.compile(code, classLoader);
}

上面的Protocol接口經過處理后的內容如下:

public class Protocol$Adaptive implements org.apache.dubbo.rpc.Protocol {
    public void destroy() {
        throw new UnsupportedOperationException(
            "The method public abstract void org.apache.dubbo.rpc.Protocol.destroy() of interface org.apache.dubbo.rpc.Protocol is not adaptive method!");
    }    public int getDefaultPort() {
        throw new UnsupportedOperationException(
            "The method public abstract int org.apache.dubbo.rpc.Protocol.getDefaultPort() of interface org.apache.dubbo.rpc.Protocol is not adaptive method!");
    }    public org.apache.dubbo.rpc.Exporter export(org.apache.dubbo.rpc.Invoker arg0) throws org.apache.dubbo.rpc.RpcException {
        if (arg0 == null) {
            throw new IllegalArgumentException("org.apache.dubbo.rpc.Invoker argument == null");
        }        if (arg0.getUrl() == null) {
            throw new IllegalArgumentException("org.apache.dubbo.rpc.Invoker argument getUrl() == null");
        }        org.apache.dubbo.common.URL url = arg0.getUrl();        String extName = (url.getProtocol() == null ? "dubbo" : url.getProtocol());
        if (extName == null) {
            throw new IllegalStateException(
                "Failed to get extension (org.apache.dubbo.rpc.Protocol) name from url (" + url.toString() + ") use keys([protocol])");
        }        org.apache.dubbo.rpc.Protocol extension = (org.apache.dubbo.rpc.Protocol) ExtensionLoader            .getExtensionLoader(org.apache.dubbo.rpc.Protocol.class).getExtension(extName);
        return extension.export(arg0);
    }    public org.apache.dubbo.rpc.Invoker refer(java.lang.Class arg0, org.apache.dubbo.common.URL arg1) throws org.apache.dubbo.rpc.RpcException {
        if (arg1 == null) {
            throw new IllegalArgumentException("url == null");
        }        org.apache.dubbo.common.URL url = arg1;        String extName = (url.getProtocol() == null ? "dubbo" : url.getProtocol());
        if (extName == null) {
            throw new IllegalStateException(
                "Failed to get extension (org.apache.dubbo.rpc.Protocol) name from url (" + url.toString() + ") use keys([protocol])");
        }        org.apache.dubbo.rpc.Protocol extension = (org.apache.dubbo.rpc.Protocol) ExtensionLoader            .getExtensionLoader(org.apache.dubbo.rpc.Protocol.class).getExtension(extName);
        return extension.refer(arg0, arg1);
    }}

前面提到過,Protocol是一個比較特殊的接口,Adaptive注解里沒有指定Key,因而沒法通過URL攜帶的參數獲取具體的實現類。從上面處理過后的內容來看,可以看到,對于Protocol接口直接使用的URL的protocol屬性字段的值來進行判斷的。

如下例子是在Adaptive里有提供key的實現處理過后的內容:

public java.lang.String sayHello(org.apache.dubbo.common.URL arg0) {
    if (arg0 == null) {
        throw new IllegalArgumentException("url == null");
    }    org.apache.dubbo.common.URL url = arg0;    String extName = url.getParameter("keyA", "dobbo");
    if (extName == null) {
        throw new IllegalStateException("Failed to get extension (cn.demo.spi.Factory) name from url (" + url.toString() + ") use keys([keyA])");
    }    cn.demo.spi.Factory extension = (cn.demo.spi.Factory) ExtensionLoader.getExtensionLoader(cn.demo.spi.Factory.class).getExtension(extName);
    return extension.sayHello(arg0);
}

可以發現這種情況直接使用的URL的getParameter方法從攜帶的參數中獲取對應的值。

通過查看AdaptiveClassCodeGenerator的實現可以發現該類的generateExtNameAssignment里對protocol做了特殊的判斷。AdaptiveClassCodeGenerator完成了代理類內容的創建,大概過程為:根據原接口定義(type),包裝出根據URL參數動態調用getExtension方法的實現類,然后將動作實際委托給了ExtensionLoader.getExtensionLoader(type).getExtension(extName);方法。其中extName為方法上的Adaptive注解指定的key的對應值,獲取過程為:

  1. 如果Adaptive注解配置的key為空,則使用類名作為擴展名
  2. 如果擴展名為protocol,則從URL的protocol里獲取對應的值
  3. 如果擴展名不為protocol,且方法參數里有org.apache.dubbo.rpc.Invocation,則從URL.getMethodParameter里獲取
  4. 以上都沒有則直接從URL.getParameter中獲取。

Protocol接口由于沒有在Adaptive注解里指定key,則會使用類名protocol作為默認的擴展名,從而命中第2條規則。

通過自適應擴展機制,Dubbo框架的各層的核心實現都是基于接口的,而將具體的實現下放到插件中。根據加載的插件不同,各層能夠選擇適合的實現而不會影響核心的邏輯流程。如Protocol接口,表示通信協議,可選的實現包括dubbo、rmi、http等。

分享到:
標簽:Dubbo
用戶無頭像

網友整理

注冊時間:

網站:5 個   小程序:0 個  文章:12 篇

  • 51998

    網站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會員

趕快注冊賬號,推廣您的網站吧!
最新入駐小程序

數獨大挑戰2018-06-03

數獨一種數學游戲,玩家需要根據9

答題星2018-06-03

您可以通過答題星輕松地創建試卷

全階人生考試2018-06-03

各種考試題,題庫,初中,高中,大學四六

運動步數有氧達人2018-06-03

記錄運動步數,積累氧氣值。還可偷

每日養生app2018-06-03

每日養生,天天健康

體育訓練成績評定2018-06-03

通用課目體育訓練成績評定