天天看點

Dubbo的SPI機制詳解執行個體源碼擴充Dubbo元件

Dubbo 沒使用 Java SPI,而重新實作了一套功能更強的 SPI。

Dubbo SPI 邏輯封裝在 ExtensionLoader 類,通過 ExtensionLoader,可加載指定實作類。Dubbo SPI 所需配置檔案需放置在

META-

Dubbo的SPI機制詳解執行個體源碼擴充Dubbo元件

INF/dubbo

 路徑

配置内容如下:

Protocol protocol = ExtensionLoader
                    .getExtensionLoader(Protocol.class)
                    .getAdaptiveExtension();
      

Dubbo要判斷系統運作時,應該選用該Protocol接口的哪個實作類。

它會去找一個你配置的Protocol,将你配置的Protocol實作類,加載進JVM,将其執行個體化。

微核心,可插拔,大量的元件,Protocol負責RPC調用的東西,你可以實作自己的RPC調用元件,實作Protocol接口,給自己的一個實作類即可。

這行代碼就是Dubbo裡大量使用的,對很多元件都保留一個接口和多個實作,然後在系統運作的時候動态根據配置去找到對應實作類。

若你沒配置,那就走預設實作。

執行個體

Dubbo的SPI機制詳解執行個體源碼擴充Dubbo元件

在Dubbo自己的jar裡

/META_INF/dubbo/internal/com.alibaba.dubbo.rpc.Protocol

檔案中:

Dubbo的SPI機制詳解執行個體源碼擴充Dubbo元件
Dubbo的SPI機制詳解執行個體源碼擴充Dubbo元件

即可看到Dubbo的SPI機制預設流程,就是Protocol接口

  • @SPI(“dubbo”)

通過SPI機制提供實作類,實作類是通過将dubbo作為預設key去配置檔案裡找到的,配置檔案名稱為接口全限定名,通過dubbo作為key可以找到預設的實作類org.apache.dubbo.rpc.protocol.dubbo.DubboProtocol

Dubbo的預設網絡通信協定,就是dubbo協定,用的DubboProtocol

在 Java 的 SPI 配置檔案裡每一行隻有一個實作類的全限定名,在 Dubbo的 SPI配置檔案中是 key=value 的形式,我們隻需要對應的 key 就能加載對應的實作。

源碼

/**
  * 傳回指定名字的擴充。如果指定名字的擴充不存在,則抛異常 {@link IllegalStateException}.
  */
@SuppressWarnings("unchecked")
public T getExtension(String name) {
    if (name == null || name.length() == 0)
        throw new IllegalArgumentException("Extension name == null");
    if ("true".equals(name)) {
        return getDefaultExtension();
    }
    Holder<Object> holder = cachedInstances.get(name);
    if (holder == null) {
        cachedInstances.putIfAbsent(name, new Holder<Object>());
        holder = cachedInstances.get(name);
    }
    //  DCL(double check lock)
    Object instance = holder.get();
    if (instance == null) {
        synchronized (holder) {
            instance = holder.get();
            if (instance == null) {
                instance = createExtension(name);
                holder.set(instance);
            }
        }
    }
    return (T) instance;
}
      

不用像 Java 原生的 SPI 那樣去周遊加載對應的服務類,隻需要通過 key 去尋找,并且尋找的時候會先從緩存的對象裡去取。

private T createExtension(String name) {
    Class<?> clazz = getExtensionClasses().get(name);
    if (clazz == null) {
        throw findException(name);
    }
    try {
        T instance = (T) EXTENSION_INSTANCES.get(clazz);
        if (instance == null) {
            EXTENSION_INSTANCES.putIfAbsent(clazz, clazz.newInstance());
            instance = (T) EXTENSION_INSTANCES.get(clazz);
        }
        injectExtension(instance);
        Set<Class<?>> wrapperClasses = cachedWrapperClasses;
        if (CollectionUtils.isNotEmpty(wrapperClasses)) {
            for (Class<?> wrapperClass : wrapperClasses) {
                instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
            }
        }
        initExtension(instance);
        return instance;
    } catch (Throwable t) {
        throw new IllegalStateException("Extension instance (name: " + name + ", class: " +
                type + ") couldn't be instantiated: " + t.getMessage(), t);
    }
}
      

若想動态替換預設實作類,需使用@Adaptive接口。Protocol接口中,有兩個方法添加了@Adaptive注解,就是說那倆接口會被代理實作。

比如這個Protocol接口搞了倆@Adaptive注解了方法,在運作時會針對Protocol生成代理類,該代理類的那倆方法中會有代理代碼,代理代碼會在運作時動态根據url中的protocol來擷取key(預設是dubbo),也可以自己指定,如果指定了别的key,那麼就會擷取别的實作類的執行個體。通過這個url中的參數不同,就可以控制動态使用不同的元件實作類

擴充Dubbo元件

自己寫個工程,可以打成jar包的那種哦

  • 裡面的

    src/main/resources

    目錄下
  • 搞一個

    META-INF/services

  • 裡面放個檔案叫:

    com.alibaba.dubbo.rpc.Protocol

  • 檔案裡搞一個

    my=com.javaedge.MyProtocol

  • 自己把jar弄到nexus私服
  • 然後自己搞一個dubbo provider工程,在這個工程裡面依賴你自己搞的那個jar
  • 然後在spring配置檔案裡給個配置:
<dubbo:protocol name=”my” port=”20000” />
      

這個時候provider啟動的時候,就會加載到我們jar包裡的my=com.javaedge.MyProtocol這行配置,接着會根據你的配置使用你定義好的MyProtocol了,這個就是簡單說明一下,你通過上述方式,可以替換掉大量的dubbo内部的元件,就是扔個你自己的jar包,然後配置一下即可~

Dubbo的SPI原理圖

Dubbo的SPI機制詳解執行個體源碼擴充Dubbo元件

Dubbo中提供了大量的類似上面的擴充點。

你要擴充一個東西,隻需自己寫個jar,讓你的consumer或者是provider工程,依賴它,在你的jar裡指定目錄下配置好接口名稱對應的檔案,裡面通過key=實作類然後對對應的元件,用類似<dubbo:protocol>用你的哪個key對應的實作類來實作某個接口,你可以自己去擴充dubbo的各種功能,提供你自己的實作!