dubbo源碼分析 SPI機制、provider、consume啓動與consume調用provider流程

1.dubbo spi機制

以dubbo源碼的dubbo-demo-provider爲例說明

dubbo-demo-provider.xml內容如下

<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"
       xmlns="http://www.springframework.org/schema/beans"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
       http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd">
    <dubbo:application name="demo-provider"/>
    <dubbo:registry address="zookeeper://127.0.0.1:2181" />
    <dubbo:protocol name="dubbo" port="20880" server="netty4" transporter="netty4" />
    <bean id="demoService" class="com.alibaba.dubbo.demo.provider.DemoServiceImpl"/>
    <dubbo:service interface="com.alibaba.dubbo.demo.DemoService"  ref="demoService"/>
</beans>

spring容器啓動,會去解析自定義的標籤,dubbo的標籤定義都在DubboNamespaceHandler內,具體解析就不說了,這個看過spring源碼的都知道,不知道也沒關係,不影響理解dubbo,下面說下每個dubbo標籤的功能

<dubbo:application> 創建ApplicationConfig,保存配置上的值到ApplicationConfig屬性上。

<dubbo: registry> 創建RegistryConfig,保存配置上的值到RegistryConfig屬性上。

<dubbo:protocol> 創建ProtocolConfig,保存配置上的值到ProtocolConfig屬性上

<dubbo:service> 創建ServiceBean,保存配置上的值到ServiceBean屬性上,該類和spring緊密聯繫,它實現了spring bean的初始化接口InitializingBean,銷燬接口DisposableBean,感知接口ApplicationContextAware、BeanNameAware,監聽器接口ApplicationListener,這裏不講spring的啓動流程,看我其它spring啓動流程圖,就可以看出這些spring接口的功能。

這些都是bean,都是單例對象存在於spring IOC容器內,那麼既然是bean,就有bean的初始化、創建了。

對於ServiceBean,由於實現了InitializingBean、ApplicationContextAware、BeanNameAware、ApplicationListener,那麼在spring對該bean啓動過程會有一些特殊操作,按照該bean的創建執行順序分別是:

step1:ServiceBean.setBeanName(String) 設置bean名稱,實現了BeanNameAware可以自定義名稱,這裏名稱就是interface屬性值,在DubboBeanDefinitionParser解析的時候獲取的。
step2:ServiceBean.setApplicationContext(ApplicationContext),吧IOC容器對象保存到ServiceBean屬性內,同時該方法也把監聽器ServiceBean添加到IOC容器的監聽器集合中。
step3:ServiceBean.afterPropertiesSet()初始化步驟,服務暴露在該方法內執行。
step4:ServiceBean.onApplicationEvent(ContextRefreshedEvent),spring IOC啓動後執行監聽器調用這裏,如果dubbo服務沒有被暴露,則暴露服務。

加載ServiceBean肯定是在創建ServiceBean之前進行的,該類有2個重要屬性,分別是父類ServiceConfig中的

private static final Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
private static final ProxyFactory proxyFactory = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();

搞清楚這兩個屬性,那麼對於dubbo的啓動就比較容易明白了。

先看private static final Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();屬性在加載的時候賦值過程,這個執行還有點繞,當時看的時候不下畫圖了,結果被繞暈了,貼個該屬性創建的流程圖,跟着圖看一圈代碼,就很容易明白:

題外話,這個屬性跟cglib的Enhancer.KEY_FACTORY屬性很類似,都是這種寫法,創建都是執行了一大堆複雜東西。

核心是一個創建自適應類,在com.alibaba.dubbo.common.extension.ExtensionLoader.createAdaptiveExtensionClassCode()內,

幾個註解說明下

@SPI,用於註解在類、接口、枚舉類上,作用是標記該接口是一個dubbo spi接口,即一個擴展點,運行時需要通過配置找到具體的實現類。比如Protocol就是個dubbo spi擴展點,用@SPI註解。

@Adaptive,自適應註解,標記在類、接口、枚舉類、方法上,標記在類上,說明該類是個自適應類,標記在方法上,說明可以通過該方法,動態的從參數(比如URL)中來確定要使用哪個方法,這個是策略模式的升級版。比如Protocol的export、refer方法就被@Adaptive註解,生成的自適應類Protocol$Adaptive,根據url的protocol不同,使用不同的協議。

@Activate,自動激活註解,標記在類、接口、枚舉類、方法上,使用在有多個擴展點實現,需要根據不同條件被激活的場景中,比如filter需要同時被激活多個。

ExtensionLoader類屬性我加了註釋,幫助閱讀源碼理解。

public class ExtensionLoader<T> {

    private static final Logger logger = LoggerFactory.getLogger(ExtensionLoader.class);

    private static final String SERVICES_DIRECTORY = "META-INF/services/";

    private static final String DUBBO_DIRECTORY = "META-INF/dubbo/";

    private static final String DUBBO_INTERNAL_DIRECTORY = DUBBO_DIRECTORY + "internal/";

    private static final Pattern NAME_SEPARATOR = Pattern.compile("\\s*[,]+\\s*");

    //全局變量,緩存已經創建的ExtensionLoader[XXXX]
    private static final ConcurrentMap<Class<?>, ExtensionLoader<?>> EXTENSION_LOADERS = new ConcurrentHashMap<Class<?>, ExtensionLoader<?>>();//{interface com.alibaba.dubbo.common.extension.ExtensionFactory=com.alibaba.dubbo.common.extension.ExtensionLoader[com.alibaba.dubbo.common.extension.ExtensionFactory], interface com.alibaba.dubbo.rpc.Protocol=com.alibaba.dubbo.common.extension.ExtensionLoader[com.alibaba.dubbo.rpc.Protocol]}

    //全局變量,緩存創建的ExtensionLoader[XXXX].type文件內的clazz實例對象
    private static final ConcurrentMap<Class<?>, Object> EXTENSION_INSTANCES = new ConcurrentHashMap<Class<?>, Object>();//{class com.alibaba.dubbo.config.spring.extension.SpringExtensionFactory=com.alibaba.dubbo.config.spring.extension.SpringExtensionFactory@916eb0, class com.alibaba.dubbo.common.extension.factory.SpiExtensionFactory=com.alibaba.dubbo.common.extension.factory.SpiExtensionFactory@10dbc7a}

    // ==============================

    /*
     * 構造器賦值,以該clazz作爲名稱的文件是要被加載的
     */
    private final Class<?> type;

    /*
     * 構造器賦值
     * 除了ExtensionLoader[ExtensionFactory]該屬性是null,其他ExtensionLoader[XXXX]該屬性是AdaptiveExtensionFactory
     * 該屬性的功能是用來在injectExtension(T instance)進行ioc注入(通過setter注入)
     */
    private final ExtensionFactory objectFactory;//AdaptiveExtensionFactory,該屬性就是用於給創建的XXX$Adaptive注入屬性
    
    /*
     * 緩存加載/META-INF/dubbo/internal/、/META-INF/dubbo/、/META-INF/services/目錄下的ExtensionLoader.type文件內
     * 未被@Adaptive註解且構造器的參數非ExtensionFactory.type,則把clazz=>name保存該屬性。其中clazz和name就是ExtensionLoader.type文件內的配置,
     * 比如class SpringExtensionFactory=spring
     * 緩存普通擴展類clazz
     */
    private final ConcurrentMap<Class<?>, String> cachedNames = new ConcurrentHashMap<Class<?>, String>();

    /*
     * 緩存加載/META-INF/dubbo/internal/、/META-INF/dubbo/、/META-INF/services/目錄下的ExtensionLoader.type文件內的
     * 未被@Adaptive註解且構造器的參數非ExtensionFactory.type的clazz集合
     * 緩存普通擴展類clazz和被@Active註解的擴展類,不包括自適應類和warpper類
     */
    private final Holder<Map<String, Class<?>>> cachedClasses = new Holder<Map<String, Class<?>>>();//{registry=class com.alibaba.dubbo.registry.integration.RegistryProtocol, injvm=class com.alibaba.dubbo.rpc.protocol.injvm.InjvmProtocol, dubbo=class com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol, mock=class com.alibaba.dubbo.rpc.support.MockProtocol}

    /*
     * 緩存加載/META-INF/dubbo/internal/、/META-INF/dubbo/、/META-INF/services/目錄下的ExtensionLoader.type文件內
     * 未被@Adaptive註解且clazz的構造器參數非ExtensionLoader.type的clazz(dubbo稱爲wrapper類)且clazz被@Activate註解的name和Activate.value
     */
    private final Map<String, Activate> cachedActivates = new ConcurrentHashMap<String, Activate>();//如果被加載的類被@Activate註解,則存放其name和Activate.value
    /*
     * 保存的是ExtensionLoader.type文件內的name和clazz實例對象,在createExtension()內創建
     */
    private final ConcurrentMap<String, Holder<Object>> cachedInstances = new ConcurrentHashMap<String, Holder<Object>>();//{spring=com.alibaba.dubbo.common.utils.Holder@1282ed8, spi=com.alibaba.dubbo.common.utils.Holder@f57048}
    
    /*
     * 自適應對象,
     * 如果ExtensionLoader.type文件內的類有被@Adaptive註解,則保存的是該類,否則通常由createAdaptiveExtension()創建
     * 比如ExtensionLoader[ExtensionFactory]該值是AdaptiveExtensionFactory
     * 比如Protocol是Protocol$Adaptive,是由createAdaptiveExtension()創建
     * 緩存實例化後的自適應擴展類,只能有一個
     */
    private final Holder<Object> cachedAdaptiveInstance = new Holder<Object>();//value=AdaptiveExtensionFactory
    
    /*
     * 緩存/META-INF/dubbo/internal/、/META-INF/dubbo/、/META-INF/services/目錄下的ExtensionLoader.type文件內被@Adaptive註解的clazz,
     * ExtensionLoader.type文件內最多只能有一個被@Adaptive註解的clazz,超過一個則拋異常
     * 如果沒有,則執行createAdaptiveExtensionClass()創建
     */
    private volatile Class<?> cachedAdaptiveClass = null;//如果要加載的類被Adaptive註解,則賦值爲該class。對於ExtensionFactory來說,其三個子類AdaptiveExtensionFactory、SpiExtensionFactory、SpringExtensionFactory只有AdaptiveExtensionFactory被註解了,因此就是AdaptiveExtensionFactory
    /*
     * 存放的是@SPI註解的value值,比如com.alibaba.dubbo.rpc.Protocol被@SPI("dubbo")註解,則該值就是dubbo
     * 在ExtensionLoader.loadExtensionClasses()賦值
     */
    private String cachedDefaultName;
    private volatile Throwable createAdaptiveInstanceError;

    /*
     * 緩存/META-INF/dubbo/internal/、/META-INF/dubbo/、/META-INF/services/目錄下的ExtensionLoader.type文件內
     * 未被@Adaptive註解且clazz的構造器參數是ExtensionLoader.type的clazz,就是包裝類
     * 緩存wrapper類
     */
    private Set<Class<?>> cachedWrapperClasses;
}

核心方法有四個,下面一個個說明

1.1.ExtensionLoader.getExtensionLoader(Class<T>)

創建接口clazz對應的ExtensionLoader對象。該方法功能就是從全局緩存EXTENSION_LOADERS獲取一個ExtensionLoader[type]對象,不存在則創建ExtensionLoader[type]對象放入全局緩存EXTENSION_LOADERS,最後返回ExtensionLoader[type]對象。

/*
     * 傳入參數是clazz,即該clazz是個文件存放在META-INF/dubbo/internal/、META-INF/dubbo/、META-INF/services/,跟jdk spi是類似的
     * 從全局緩存EXTENSION_LOADERS獲取type對應的ExtensionLoader[type]對象,不存在則創建
     */
    @SuppressWarnings("unchecked")
    public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) {
        if (type == null)
            throw new IllegalArgumentException("Extension type == null");
        if (!type.isInterface()) {//非接口,拋出異常
            throw new IllegalArgumentException("Extension type(" + type + ") is not interface!");
        }
        if (!withExtensionAnnotation(type)) {//class未被@SPI註解,拋出異常,只有被@SPI註解說明纔是一個dubbo擴展接口
            throw new IllegalArgumentException("Extension type(" + type +
                    ") is not extension, because WITHOUT @" + SPI.class.getSimpleName() + " Annotation!");
        }

        ExtensionLoader<T> loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);//從全局緩存獲取
        if (loader == null) {//不存在則創建
            EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader<T>(type));//創建ExtensionLoader[type]對象,屬性type存放的就是擴展接口clazz
            loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
        }
        return loader;
    }

1.2.ExtensionLoader.getAdaptiveExtension()

功能:獲取ExtensionLoader.type接口對應的自適應對象,不存在則創建

//獲取接口clazz(即ExtensionLoader.type)的自適應擴展類對象,如果不存在,則創建
@SuppressWarnings("unchecked")
public T getAdaptiveExtension() {
    Object instance = cachedAdaptiveInstance.get();//從當前對象查找
    if (instance == null) {
        if (createAdaptiveInstanceError == null) {
            synchronized (cachedAdaptiveInstance) {
                instance = cachedAdaptiveInstance.get();
                if (instance == null) {
                    try {
                        instance = createAdaptiveExtension();//創建自適應對象
                        cachedAdaptiveInstance.set(instance);//保存到當前對象
                    } catch (Throwable t) {
                        createAdaptiveInstanceError = t;
                        throw new IllegalStateException("fail to create adaptive instance: " + t.toString(), t);
                    }
                }
            }
        } else {
            throw new IllegalStateException("fail to create adaptive instance: " + createAdaptiveInstanceError.toString(), createAdaptiveInstanceError);
        }
    }

    return (T) instance;//返回自適應對象
}

/*
 * 創建自適應擴展並注入屬性
 */
@SuppressWarnings("unchecked")
private T createAdaptiveExtension() {
    try {
    	/*
    	 * step1 getAdaptiveExtensionClass 獲取自適應對象的clazz
    	 * step2 通過clazz.newInstance()反射創建自適應對象
    	 * step3 進行setter注入屬性,這個是dubbo spi ioc功能
    	 */
        return injectExtension((T) getAdaptiveExtensionClass().newInstance());
    } catch (Exception e) {
        throw new IllegalStateException("Can not create adaptive extension " + type + ", cause: " + e.getMessage(), e);
    }
}
/*
 * 獲取自適應clazz,形式是XXX$Adaptive
 */
private Class<?> getAdaptiveExtensionClass() {
    getExtensionClasses();//如果緩存不存在則先加載
    if (cachedAdaptiveClass != null) {//說明ExtensionFactory.type文件內的clazz有被@Adaptive註解的類
        return cachedAdaptiveClass;
    }
    return cachedAdaptiveClass = createAdaptiveExtensionClass();//創建自適應對象保存到this.cachedAdaptiveClass
}
/*
 * 生成自適應類的java代碼,然後使用javaassit編譯自適應類java代碼爲class
 */
private Class<?> createAdaptiveExtensionClass() {
    String code = createAdaptiveExtensionClassCode();//生成的是XXX$Adaptive這個java文件內容
    //如果需要查看生成的代碼,則把變量code保存到文件內即可
    ClassLoader classLoader = findClassLoader();
    com.alibaba.dubbo.common.compiler.Compiler compiler = ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.common.compiler.Compiler.class).getAdaptiveExtension();//AdaptiveCompiler
    return compiler.compile(code, classLoader);//默認使用JavassistCompiler編譯java代碼
}
/*
 * 生成ExtensionLoader.type接口的實現類,並對接口內被@Adaptive註解的方法生成具體實現,
 * 未被@Adaptive註解的方法拋出UnsupportedOperationException,
 * 生成的類名是類名是ExtensionLoader.type$Adaptive
 */
private String createAdaptiveExtensionClassCode() {
	//代碼較多,省略。
	//功能就是生成ExtensionLoader.type接口實現類的java代碼,最後返的是個java代碼對象
}

上步代碼展示省略了編譯java代碼的步驟,這個編譯我也沒完全看懂,通常也不需要看懂的。

1.3.ExtensionLoader.getExtension(String)

獲取name對應的擴展類,比如對於com.alibaba.dubbo.rpc.Protocol,那麼name=dubbo的獲取的是DubboProtocol,但是由於有wrapper類,因此獲取到的對象是ProtocolFilterWrapper,結構是

爲什麼要使用wrapper呢?wrapper是對目標對象進行了一層裝飾,爲了增強目標對象,但是又不對目標對象增加額外功能,保持目標對象的功能共用性和單一性。

/*
 * 功能:獲取普通擴展類對象XXX$Adaptive
 * 在ExtensionLoader[XXXX].getExtension(String name)獲取ExtensionLoader.type文件內name對應的clazz實例(即自適應類)
 * 參數name是url上的屬性值(或者是dubbo默認值)
 */
@SuppressWarnings("unchecked")
public T getExtension(String name) {//該方法功能就是獲取ExtensionLoader.type文件內名稱是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);//從緩存獲取對應的clazz
    if (holder == null) {
        cachedInstances.putIfAbsent(name, new Holder<Object>());
        holder = cachedInstances.get(name);
    }
    Object instance = holder.get();
    if (instance == null) {
        synchronized (holder) {
            instance = holder.get();
            if (instance == null) {
                instance = createExtension(name);
                holder.set(instance);
            }
        }
    }
    return (T) instance;
}
/*
 * 創建ExtensionLoader.type文件內name對應的clazz實例
 * dubbo擴展類的4個特性:自動包裝,自動加載,自適應,自動激活
 * 這裏是自動包裝,就是目標擴展類自動會用wrapper類包裝下
 */
@SuppressWarnings("unchecked")
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());//創建clzz實例
            instance = (T) EXTENSION_INSTANCES.get(clazz);
        }
        injectExtension(instance);//setter注入
        Set<Class<?>> wrapperClasses = cachedWrapperClasses;
        //spi的wrapper對象就是該類只有一個構造器且構造器的參數是ExtensionLoader.type,比如ProtocolFilterWrapper、ProtocolListenerWrapper就是DubboProtocol的wrapper類
        if (wrapperClasses != null && !wrapperClasses.isEmpty()) {//使用wrapper類裝飾,這樣作用是爲了增強功能,而且是類功能分開
            for (Class<?> wrapperClass : wrapperClasses) {
            	/*
            	 * step1: wrapper類使用instance實例化
            	 * step2: wrapper類屬性注入
            	 */
                instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));//wrapper實例化並注入
            }
        }
        return instance;
    } catch (Throwable t) {
        throw new IllegalStateException("Extension instance(name: " + name + ", class: " +
                type + ")  could not be instantiated: " + t.getMessage(), t);
    }
}

1.4.ExtensionLoader.getActivateExtension(URL, String[], String)

/*
     * 參數values是url上屬性值集合,比如filter屬性,配置了多個filter,那麼就是filter1、filter2、filter3
     * 參數group是url上屬性group的value值
     * 功能:獲取所有自動激活擴展點
     */
    public List<T> getActivateExtension(URL url, String[] values, String group) {//遍歷cachedActivates集合,返回@Activate.value匹配group名稱的實例集合
        List<T> exts = new ArrayList<T>();
        List<String> names = values == null ? new ArrayList<String>(0) : Arrays.asList(values);
        //step0:加入默認的激活
        if (!names.contains(Constants.REMOVE_VALUE_PREFIX + Constants.DEFAULT_KEY)) {//-default表示禁用所有默認
            //step1:檢查緩存,如緩存沒有,則加載到緩存
        	getExtensionClasses();
        	/*
        	 * step2:遍歷ExtensionLoader.type文件內所有被@Activate註解的類,比如com.alibaba.dubbo.rpc.Filter文件內所有被@Activate註解的類
        	 * 根據傳入的url匹配條件(匹配group name),得到所有符合激活條件的擴展類實現。然後根據@Activate中配置的before after order排序
        	 */
            for (Map.Entry<String, Activate> entry : cachedActivates.entrySet()) {//
                String name = entry.getKey();//獲取名稱,比如com.alibaba.dubbo.rpc.Filter 文件內future
                Activate activate = entry.getValue();//獲取類上的Activate註解
                if (isMatchGroup(group, activate.group())) {//group匹配,參數group爲null 或者 被@Activate.group值集合包含 則匹配
                    T ext = getExtension(name);//獲取名稱爲name對應的擴展點
                    if (!names.contains(name)//默認激活是不在url配置
                            && !names.contains(Constants.REMOVE_VALUE_PREFIX + name)//-表示禁止
                            && isActive(activate, url)) {//處於激活狀態
                        exts.add(ext);
                    }
                }
            }
            Collections.sort(exts, ActivateComparator.COMPARATOR);//按照@Activate中配置的before after order排序
        }
        /*
         * step3:遍歷所有用戶自定義擴展類名稱,根據用戶url配置的順序,調整擴展點激活順序(按照用戶在url中配置的順序)
         */
        List<T> usrs = new ArrayList<T>();
        for (int i = 0; i < names.size(); i++) {
            String name = names.get(i);
            if (!name.startsWith(Constants.REMOVE_VALUE_PREFIX)//-表示禁用
                    && !names.contains(Constants.REMOVE_VALUE_PREFIX + name)) {
            	//這個理解稍微麻煩些,比如test://localhost/text?ext=order1,default,order2,那麼擴展點ext的激活順序是按照order1->default->order2,這個default是表示前面的默認激活擴展點
                if (Constants.DEFAULT_KEY.equals(name)) {
                    if (!usrs.isEmpty()) {
                        exts.addAll(0, usrs);//把usrs添加到exts的頭部,即default擴展點的前面
                        usrs.clear();
                    }
                } else {
                    T ext = getExtension(name);//獲取名稱name對應的擴展
                    usrs.add(ext);
                }
            }
        }
        if (!usrs.isEmpty()) {
            exts.addAll(usrs);
        }
        return exts;//最後返回的激活擴展點集合是順序先默認激活+用戶自定義激活
    }

那麼判斷是處於激活狀態呢?如何判斷的在isActive方法內

/*
     * 分2種情況
     * case1:被@Activate註解的擴展點的value集合是空,返回true,說明處於自動激活狀態
     * case2:@Activate註解的擴展點的value集合和url上配置的激活key每個比較,如果相同 或者 url上的k是以Activate上的.key結尾 且url上的k的value非空 說明匹配
     */
    private boolean isActive(Activate activate, URL url) {
        String[] keys = activate.value();
        if (keys.length == 0) {
            return true;
        }
        //雙層for循環,如果url上的k==Activate上的key || url上的k是以Activate上的.key結尾 且url上的k的value非空 說明匹配
        for (String key : keys) {
            for (Map.Entry<String, String> entry : url.getParameters().entrySet()) {//遍歷url上的參數,
                String k = entry.getKey();
                String v = entry.getValue();
                if ((k.equals(key) || k.endsWith("." + key))//k==key || k是以.key結尾,說明匹配
                        && ConfigUtils.isNotEmpty(v)) {
                    return true;
                }
            }
        }
        return false;
    }

下面給出個自己分析的時候,生成的自適應類,供參考

鏈接:https://share.weiyun.com/5qDR7qt

 

2.provider啓動流程

按照自己習慣,先貼總體流程圖

總結如下:

invoker對象本質就是對目標對象的代理,在dubbo中對通過代理對目標對象增強了功能,增加了filter,那麼最終暴露的是exporter對象,需要把invoker對象包裝爲Exporter對象,Exporter對象也通過了裝飾增加了功能,最終暴露到zk的服務對象的結構是這樣的:

                                                                                圖一,服務暴露對象的數據結構

總結dubbo啓動流程大體是這樣的,生成registry協議的url,生成dubbo協議的url,把dubbo url作爲屬性保存到registry協議url上,屬性的export,接着生成目標對象的代理對象invoker,給該invoker增強(增加filter處理鏈),把invoker包裝爲protocol協議的export(默認是DubboExporter),啓動netty監聽端口,增加netty channelHandler處理鏈,在zk上創建服務節點,接着訂閱本節點,zk創建configurators節點,zk watch機制監聽該節點變化,有變化,則重新暴露服務,最後把export對象暴露(緩存起來),exporter對象也是經過了多層裝飾。

啓動過程大量用到了spi機制,根據url上的參數配置spi找到對應的具體實現對象,完全進行了解耦,理解了這個,很容易對自己的項目進行特定擴展,比如最常見增加filter。

3.consumer啓動流程

consumer的配置如下

<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"
       xmlns="http://www.springframework.org/schema/beans"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
       http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd">

    <!-- consumer's application name, used for tracing dependency relationship (not a matching criterion),
    don't set it same as provider -->
    <dubbo:application name="demo-consumer"/>
	
    <!-- use multicast registry center to discover service -->
    <!-- <dubbo:registry address="multicast://224.5.6.7:1234"/> -->
    <dubbo:registry address="zookeeper://47.98.54.117:2181" />
	
    <!-- generate proxy for the remote service, then demoService can be used in the same way as the
    local regular interface -->
    <dubbo:reference id="demoService" connections="1" client="netty4" check="false" interface="com.alibaba.dubbo.demo.DemoService"/>

</beans>

<dubbo:reference>對應的是ReferenceBean,同ServiceBean一樣,也是實現了spring提供的擴展接口,核心入口是ReferenceConfig.createProxy(Map<String,String>),看流程圖:

消費端消費provider服務,但是實際都是存在多個provider的,那就爲每個provider生成一個對應的invoker對象(默認dubboinvoker),但是最終得表現爲一個invoker,那就包裝下,把invoker集合包裝爲一個對象就是RegistryDirectory(實際保存在urlInvokerMap屬性上),接着又包裝爲FailoverClusterInvoker(集羣容錯)、MockClusterInvoker,最終表現爲對外的invoker對象就是MockClusterInvoker,那麼該invoker對象結構就是:

MockClusterInvoker
    FailoverClusterInvoker
    RegistryDirectory.methodInvokerMap集合內存放是proviver的Invoker,表示provider集羣
        RegistryDirectory$InvokerDelegate<T>  (id=277)    
            ProtocolFilterWrapper$1[filterchain: ConsumerContextFilter -> FutureFilter -> MonitorFilter]
                DubboInvoker

包裝爲了invoker,但是類型不是所需要的目標類型com.alibaba.dubbo.demo.DemoService,那就對invoker生成代理類,爲代理類增加目標接口,這樣生成的proxy類實現了目標接口又包裝了invoker對象,然後調用的時候proxy類直接調用invoker對象,這個和jdk的動態代理模式思想是一樣的,只是使用的javaassit生成的代理類。

爲什麼要生成代理類呢?因爲要遠程調用,要連接服務端,涉及到編解碼,這些步驟都是公共部分,那麼就包裝起來,因此invoker類就是包裝了公共部分功能。

消費端訂閱了zk上的providers節點、configurators、routers節點,在這些節點發發生變化的時候,zk向消費端發送通知,然後消費端從zk拉取到節點變化內容,執行對應的變化,比如重新生成invoker對象。 這裏的執行流程是

 

4.dubbo調用流程

以dubbo源碼的例子來說,String hello = demoService.sayHello("world");,該步驟是實際的調用,表面看就是IOC容器內的bean執行方法,但是方法的具體實現是在provider的,使用的是netty,下圖是dubbo consume調provider總體流程圖

 

大體功能是:consume發起請求,把要執行的目標方法、參數包裝爲RpcInvocation,根據集羣策略查找到consume啓動的時候保存的所有該方法對應的invoker集合,然後經過router篩選後,返回符合的invoker集合,再通過負載均衡策略獲取一個invoker,然後執行該invoker的調用,invoker是個裝飾對象,執行其內部被裝飾的一個個invoker,那麼經過invoker和filter處理,最終調用到netty client發送數據,創建com.alibaba.dubbo.remoting.exchange.Request,該對象包裝了RpcInvocation,通過Netty進行網絡發送,經過netty ChannelHandler處理鏈的編碼寫入到緩衝區,刷新緩衝區,從而把數據發送到provider發送出去,因爲netty是異步非阻塞的(發送線程和業務線程不是同一個),如果是同步發送方式,ResponseFuture進行同步等待結果,異步的話不需要等待結果。

provider端netty io線程不停輪詢selector,如果有可讀事件,則觸發ChannelHandler鏈的channelRead()操作,那麼由netty讀取到consume請求,進行解碼,接收處理,根據url獲取到provider暴露的服務,從暴露的服務獲取到對應的invoker對象,那麼執行invoker對象,invoker對象是個裝飾鏈對象,一層層執行內部Invoker,再經過filter鏈處理,最終執行到目標方法。然後把目標方法進行編碼通過netty傳輸返回到consume。

FIXME 這個圖應該用管道圖更加清晰,後續改爲管道圖。

 

 

 

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