Dubbo源碼解析 — 服務引用原理

前言
通過上一篇dubbo源碼解析-簡單原理、與spring融合的鋪墊,咱們已經能簡單的實現了dubbo的服務引用.其實上一篇中的代碼,不少都是從dubbo源碼中複製出來,甚至有些類名,變量名都沒改.那請問,我爲何要這麼作?java

我認爲學習一個框架,無非就三個步驟.面試

掌握基本使用spring

看過源碼,知道其中原理編程

臨摹源碼,本身仿寫一個簡易的框架設計模式

其實你們都清楚,編程這東西,最關鍵是多動手.也就是,第三步纔是最關鍵的.可是現實也是很是殘酷的,絕大多數人都停留在第一步.光是第二步,都有些讓人產生的內心恐懼.因此在寫服務引用的時候,我就想到了小時候看紀曉嵐的一個片斷.當時紅樓夢是禁書,紀曉嵐爲了讓太后看紅樓夢,就把紅樓夢這個名字換成了石頭記.這樣太后天然就沒有內心負擔.我以爲用一個圖來描述可能更貼切框架

固然臨摹源碼的這個過程,依肥朝拙見,也須要分爲三個過程,分別是入門版(用最簡單的代碼表達出框架原理)、進階版(加入設計模式等思想,在入門版的基礎上優化代碼)、高級版(和框架代碼基本一致).ide

固然上一篇的入門版只是拋磚引玉,等整個dubbo源碼解析系列完結以後,和你們一塊兒臨摹dubbo源碼也在計劃當中.固然更多後續進展關注肥朝便可.學習

插播面試題
描述一下dubbo服務引用的過程,原理優化

既然你提到了dubbo的服務引用中封裝通訊細節是用到了動態代理,那請問建立動態代理經常使用的方式有哪些,他們又有什麼區別?dubbo中用的是哪種?(高頻題)url

除了JDK動態代理和CGLIB動態代理外,還知不知道其餘實現代理的方式?(區分度高)

原諒他
看源碼對於大多數人來講,最難的一點莫過於"從源碼的哪一個地方開始看".雖然我以前數十篇dubbo源碼解析都在回答這個問題,可是每發出一篇,都仍是有小夥伴私信問我一樣的問題.對此,我固然是選擇"原諒他".所以,本篇我又再次粗暴式的點題,"怎麼看源碼".就把本篇來講,這個服務引用的原理,咱們要從哪裏開始看呢?咱們一塊兒看一下官方文檔
Dubbo源碼解析 — 服務引用原理

若是你在上一篇中把我貼出來的demo都實現過一遍,再看到這個圖,就不難總結出服務引用無非就是作了兩件事

將spring的schemas標籤信息轉換bean,而後經過這個bean的信息,鏈接、訂閱zookeeper節點信息建立一個invoker

將invoker的信息建立一個動態代理對象

舒適提示:除了看官方文檔入手,在dubbo源碼解析-服務暴露原理中我還提到了從輸出日誌入手.固然,我這裏列舉了兩種方式只是給你提供參考,並非說一共就只有這兩種方式,也不是說,這兩種就是最優的.

時序圖
Dubbo源碼解析 — 服務引用原理
直入主題
有部分朋友反饋說代碼貼圖手機閱讀不友好,可是若是不貼圖的話,不少朋友看完文章本身debug的時候找相應的類和方法又要花費大量時間,因此折中一下,貼圖和貼代碼結合
Dubbo源碼解析 — 服務引用原理

public  Invoker refer(Class type, URL url) throws RpcException {
    url = url.setProtocol(url.getParameter(Constants.REGISTRY_KEY, Constants.DEFAULT_REGISTRY)).removeParameter(Constants.REGISTRY_KEY);
    //序號2,這裏的邏輯和以前分享的'zookeeper鏈接'基本一致,不熟悉的能夠回去看看
    Registry registry = registryFactory.getRegistry(url);
    if (RegistryService.class.equals(type)) {
        return proxyFactory.getInvoker((T) registry, type, url);
    }
    // group="a,b" or group="*"
    Map qs = StringUtils.parseQueryString(url.getParameterAndDecoded(Constants.REFER_KEY));
    String group = qs.get(Constants.GROUP_KEY);
    if (group != null && group.length() > 0 ) {
        if ( ( Constants.COMMA_SPLIT_PATTERN.split( group ) ).length > 1
                || "*".equals( group ) ) {
            return doRefer( getMergeableCluster(), registry, type, url );
        }
    }
    return doRefer(cluster, registry, type, url);
}
private  Invoker doRefer(Cluster cluster, Registry registry, Class type, URL url) {
    RegistryDirectory directory = new RegistryDirectory(type, url);
    directory.setRegistry(registry);
    directory.setProtocol(protocol);
    URL subscribeUrl = new URL(Constants.CONSUMER_PROTOCOL, NetUtils.getLocalHost(), 0, type.getName(), directory.getUrl().getParameters());
    if (! Constants.ANY_VALUE.equals(url.getServiceInterface())
            && url.getParameter(Constants.REGISTER_KEY, true)) {
        registry.register(subscribeUrl.addParameters(Constants.CATEGORY_KEY, Constants.CONSUMERS_CATEGORY,
                Constants.CHECK_KEY, String.valueOf(false)));
    }
    //序號3,這裏的邏輯和以前分享的'zookeeper訂閱'基本一致,不熟悉的能夠回去看看
    directory.subscribe(subscribeUrl.addParameter(Constants.CATEGORY_KEY, 
            Constants.PROVIDERS_CATEGORY 
            + "," + Constants.CONFIGURATORS_CATEGORY 
            + "," + Constants.ROUTERS_CATEGORY));
    //序號4,cluster關鍵字在集羣容錯系列也提到過,不熟悉的能夠回去看看
    return cluster.join(directory);
}

上面的這4步,就完成了schemas標籤信息到invoker的轉換,那麼下面就是建立代理對象了(序號5)

private T createProxy(Map map) {
    //......(省略部分代碼)
    // 建立服務代理
    return (T) proxyFactory.getProxy(invoker);
}

咱們知道,要封裝這個通訊細節,讓用戶像以本地調用方式調用遠程服務,就必須使用代理,而後說到動態代理,通常咱們就想到兩種,一種是JDK的動態代理,一種是CGLIB的動態代理,那咱們看看二者有什麼特色.

JDK的動態代理代理的對象必需要實現一個接口,而針對於沒有接口的類,則可用CGLIB.要明白二者區別必需要了解原理,以前反覆強調,明白了原理天然一通百通.CGLIB其原理也很簡單,對指定的目標類生成一個子類,並覆蓋其中方法實現加強,但因爲採用的是繼承,因此不能對final修飾的類進行代理.

除了以上兩種你們都很熟悉的方式外,其實還有一種方式,就是javassist生成字節碼來實現代理(後面會詳細講,dubbo多處用到了javassist).那dubbo究竟用到了哪一種方式實現代理呢?咱們往下看

Dubbo源碼解析 — 服務引用原理
Dubbo源碼解析 — 服務引用原理

序號5的結束本篇也接近了尾聲.本篇綜合性較強,其中涉及到以前的內容本篇將再也不重複說起,可根據註釋中的標記自行查看.

相關文章
相關標籤/搜索