Dubbo的擴展機制

Dubbo的擴展機制

Dubbo擴展原理

ExtensionLoader的設計和實現方式

自定義擴展例子


  • Dubbo的擴展原理

Dubbo採用的是內核+擴展的體系結構,除了Service和Config層,其他層的功能都是可擴展的(Proxy、Registry、Cluster、Monitor、Protocol、Exchange、Transport、Serialize)。
也就是說,這些層的功能模塊,都可以通過配置的方式靈活地切換實現,並不需要修改框架的代碼。
Dubbo 使用 URL 總線模式(包含了Key-Value)傳遞配置信息,所有的狀態數據信息都可以從URL中解析獲取。

Dubbo 自身的功能也是通過擴展點實現的,也就是 Dubbo 的所有功能點都可被用戶自定義擴展所替換。
Dubbo本身也爲各層提供了多種實現,比如Registry,Dubbo就提供了MulticastRegistry、RedisRegistry、ZookeeperRegistry等實現。(使用Redis作爲註冊中心)。

Dubbo同樣支持用戶自定義的擴展實現。

Dubbo的擴展點由Java的SPI擴展點發現機制擴展而來。
自定義的擴展方式可以支持延遲加載,不用每次都一次性加載出所有的配置功能;Dubbo擴展還支持以key=value的方式進行配置;Dubbo擴展還支持輕量級的依賴注入,會將擴展點實現類中的另外的擴展點引用“順便”給初始化了。

  • ExtensionLoader的設計和實現方式
    擴展加載器,是Dubbo自定義擴展了SPI功能的加載器,它有一個getExtensionLoader方法,用來獲取某個接口的ExtensionLoader,每個接口只有一個該對象。
    查看它提供的方法:

    對外方法主要有3個類別,

    activeExtension
    adaptiveExtension
    defaultExtension


除了需要提供獲取擴展的API,它主要關注如下的功能:
1、在運行時靈活適配不同的擴展實現
2、能支持擴展緩存,以避免重複加載
3、能自動裝配擴展的嵌套的情況 前面說過,每種功能Dubbo提供了多種實現,在使用的時候不能硬編碼決定使用哪一個實現。除了支持Dubbo自身的實現以外,還需要提供對外部實現的支持,這樣才能確保整個框架的靈活性。`@Adaptive` 就是爲了解決這個問題而設計的(顯然,一個接口不能有多個@Adaptive實現)。 1、一種情況是對某個實現進行適配。

@Adaptive 註解可以可以用來標註在某個接口的實現類上,表示這個實現並不是用來做業務邏輯處理的,而是用來適配這個接口的各種實現的。
比如:AdaptiveExtensionFactory被標註了 @Adaptive ,在調用 ExtensionLoader

ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension()

方法時,將返回AdaptiveExtensionFactory實例,它用來適配SpiExtensionFactorySpringExtensionFactory實現,具體用哪個,會根據運行時的狀態來確定。

@Adaptive 註解同樣可以標註在接口的方法上。ExtensionLoader通過分析接口配置的adaptvie規則來動態生成adaptvie類。
@Adaptive有一個value屬性,通過這個值來設置規則。由於Dubbo採取了URL總線的方式來傳遞數據,所以加上該註解的方法參數,需要可以獲得URL對象,或者直接就是URL類型或者子類型的對象。以value的值爲key,去URL裏面查找。
如果查找不到,則會把類名拆分,例如:com.alibaba.dubbo.xxx.YyyInvokerWrapper將會拆分成String[] {“yyy.invoker.wrapper”},用於在URL中查找配置。
如果不能獲取到,則會返回SPI標註的接口中聲明的名稱的擴展。

@Adaptive接口標註的實現,是一個適配器

2、另一種情況是Dubbo框架動態生成適配器類
如果沒有找到@SPI接口的@Adaptive實現,ExtensionLoader會動態創建適配器類。
ExtensionLoader通過分析接口配置的adaptive規則,動態生成類,並且加載到ClassLoader中。
該註解有一個value屬性,通過這個屬性,可以設置該接口的adaptive規則,所以結合URL總線設計,參數都需要從URL中獲取。
所以這樣的方法都需要提供URL對象作爲參數。
以Transporter類爲例:

@SPI("netty")
public interface Transporter {

    @Adaptive({Constants.SERVER_KEY, Constants.TRANSPORTER_KEY})
    Server bind(URL url, ChannelHandler handler) throws RemotingException;

    @Adaptive({Constants.CLIENT_KEY, Constants.TRANSPORTER_KEY})
    Client connect(URL url, ChannelHandler handler) throws RemotingException;
}
這個接口是一個擴展接口,默認擴展名爲`netty`。它有兩個方法, 一個是bind,提供URL參數,註解說明,它是配置服務端的transporter功能; 一個是connect,提供URL參數,註解說明,它是配置客戶端的transporter功能; 它沒有Adaptive實現,但是它的方法提供了URL參數。 ![](http://owu0nfc62.bkt.clouddn.com/transporter.png) `ExtensionLoader.getExtensionLoader(Transporter.class).getAdaptiveExtension();` 的時候,按如下調用鏈 `getAdaptiveExtension` -> `createAdaptiveExtension` -> `getAdaptiveExtensionClass` -> `createAdaptiveExtensionClass` -> `createAdaptiveExtensionClassCode` `createAdaptiveExtensionClassCode`方法,將會構造一個臨時的Adaptive類文件(當然,它需要實現擴展接口,例如Transporter),名字爲`接口名+$Adaptive` 關於接口方法的實現,它將從URL中獲取接口方法註解上的參數,Transporter接口中,如:Constants.SERVER_KEY, Constants.TRANSPORTER_KEY。 用於構造Transporter實現,並且最終生成如下的代碼:
package com.alibaba.dubbo.remoting;
import com.alibaba.dubbo.common.extension.ExtensionLoader;
public class Transporter$Adpative implements com.alibaba.dubbo.remoting.Transporter {
public com.alibaba.dubbo.remoting.Client connect(com.alibaba.dubbo.common.URL arg0, com.alibaba.dubbo.remoting.ChannelHandler arg1) throws com.alibaba.dubbo.common.URL {
if (arg0 == null) throw new IllegalArgumentException("url == null");
com.alibaba.dubbo.common.URL url = arg0;
String extName = url.getParameter("client", url.getParameter("transporter", "netty"));
if(extName == null) throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.remoting.Transporter) name from url(" + url.toString() + ") use keys([client, transporter])");
com.alibaba.dubbo.remoting.Transporter extension = (com.alibaba.dubbo.remoting.Transporter)ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.remoting.Transporter.class).getExtension(extName);
return extension.connect(arg0, arg1);
}
public com.alibaba.dubbo.remoting.Server bind(com.alibaba.dubbo.common.URL arg0, com.alibaba.dubbo.remoting.ChannelHandler arg1) throws com.alibaba.dubbo.common.URL {
if (arg0 == null) throw new IllegalArgumentException("url == null");
com.alibaba.dubbo.common.URL url = arg0;
String extName = url.getParameter("server", url.getParameter("transporter", "netty"));
if(extName == null) throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.remoting.Transporter) name from url(" + url.toString() + ") use keys([server, transporter])");
com.alibaba.dubbo.remoting.Transporter extension = (com.alibaba.dubbo.remoting.Transporter)ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.remoting.Transporter.class).getExtension(extName);
return extension.bind(arg0, arg1);
}
}
這樣一來,除了參數獲取方式不一樣。其實和自己定義的Adaptive實現沒什麼區別了。 **拿到Adaptive(其實這就是一個代理工廠),並在代理工廠內屏蔽實現的差異(獲取到準確的擴展實現,然後調用方法,最後返回)** 擴展靈活配置,還體現在另一個註解上`@Activate`。這個註解使用在接口的實現類上,用來標註使用這個實現的前提條件。 比如:`ValidationFilter`被標註爲:
@Activate(group = {Constants.CONSUMER, Constants.PROVIDER}, value = Constants.VALIDATION_KEY, order = 10000)
public class ValidationFilter implements Filter {...}
表明這個Filter實現,在做Validation的時候,可以在客戶端和服務端生效; value表示另外一個激活條件,注意上面截圖的方法,active extension相關的方法,需要提供一個URL參數, 那麼這個value配置的值,表示URL中必須要有指定的參數才能激活這個擴展。 order值越大越靠前。 `這裏指的是對 框架本身對接口的實現 的排序,用戶擴展的實現,將放置在後面。` Dubbo中對該註解用得最多的地方是`Filter`的實現。Dubbo的調用經常會使用過濾器鏈的形式,哪些實現以及實現的順序,是由`@Activate`註解來控制的。
  • 自定義擴展的例子
    目標:引入Dubbo框架,自定義擴展,用ExtensionLoader檢驗Dubbo框架是否能正確解析配置,並且返回正確結果。
    項目結構搭建省略。
    1、定義擴展接口:
@SPI("default")
public interface MyExtension {
    @Adaptive
    String sayHello(String name, String extensionType);
}

它的方法沒有提供URL,所以,只能顯式定義一個Adaptive類。

2、定義兩個實現:

public class DefaultExtension implements MyExtension {
    @Override
    public String sayHello(String name, String extensionType) {
        return "This is DEFAULT implementation, and hello - "+name;
    }
}
public class SWExtension implements MyExtension {
    @Override
    public String sayHello(String name, String extensionType) {
        return "This is SW implementation, and hello - "+name;
    }
}

3、定義Adaptive類,根據類型獲取實現。

@Adaptive
public class AdaptiveExtension implements MyExtension {
    public String sayHello(String name, String extensionType) {
        ExtensionLoader<MyExtension> loader = ExtensionLoader.getExtensionLoader(MyExtension.class);
        MyExtension extension = loader.getDefaultExtension();
        switch (extensionType){
            case "default":
                extension = loader.getExtension("default");
                break;
            case "sw":
                extension = loader.getExtension("sw");
                break;
            default:
                break;
        }
        return extension.sayHello(name,extensionType);
    }
}

4、配置META-INF/dubbo/cn.irving.extension.MyExtension

default=cn.irving.extension.DefaultExtension
sw=cn.irving.extension.SWExtension
adaptive=cn.irving.extension.AdaptiveExtension

5、定義測試類

public class ExtensionTest {
    public static void main(String[] args) {
        MyExtension extension = ExtensionLoader.getExtensionLoader(MyExtension.class).getAdaptiveExtension();
        System.out.println(extension.sayHello("Irving","sw"));
// ExtensionLoader.getExtensionLoader(Transporter.class).getAdaptiveExtension();
    }
}

運行結果:

This is SW implementation, and hello - Irving

參考資料:
http://dubbo.io/books/dubbo-dev-book/SPI.html
https://my.oschina.net/bieber/blog/418949

-EOF-

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章