歡迎光臨
每天分享高質量文章

Dubbo原始碼解析 —— 服務取用原理

友情提示:歡迎關註公眾號【芋道原始碼】。?關註後,拉你進【原始碼圈】微信群和【芋艿】搞基嗨皮。
友情提示:歡迎關註公眾號【芋道原始碼】。?關註後,拉你進【原始碼圈】微信群和【芋艿】搞基嗨皮。
友情提示:歡迎關註公眾號【芋道原始碼】。?關註後,拉你進【原始碼圈】微信群和【芋艿】搞基嗨皮。

前言

經過上一篇dubbo原始碼解析-簡單原理、與spring融合的鋪墊,我們已經能簡單的實現了dubbo的服務取用.其實上一篇中的程式碼,很多都是從dubbo原始碼中複製出來,甚至有些類名,變數名都沒改.那請問,我為什麼要這麼做?

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

  • 掌握基本使用

  • 看過原始碼,知道其中原理

  • 臨摹原始碼,自己仿寫一個簡易的框架

其實大家都清楚,程式設計這東西,最關鍵是多動手.也就是,第三步才是最關鍵的.但是現實也是非常殘酷的,絕大多數人都停留在第一步.光是第二步,都有些讓人產生的心裡恐懼.所以在寫服務取用的時候,我就想到了小時候看紀曉嵐的一個片段.當時紅樓夢是禁書,紀曉嵐為了讓太后看紅樓夢,就把紅樓夢這個名字換成了石頭記.這樣太后自然就沒有心裡負擔.我覺得用一個圖來描述可能更貼切

當然臨摹原始碼的這個過程,依肥朝拙見,也需要分為三個過程,分別是入門版(用最簡單的程式碼表達出框架原理)、進階版(加入設計樣式等思想,在入門版的基礎上最佳化程式碼)、高階版(和框架程式碼基本一致).

當然上一篇的入門版只是拋磚引玉,等整個dubbo原始碼解析系列完結之後,和大家一起臨摹dubbo原始碼也在計劃當中.當然更多後續進展關註肥朝即可.

插播面試題

  • 描述一下dubbo服務取用的過程,原理

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

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

原諒他

看原始碼對於大多數人來說,最難的一點莫過於”從原始碼的哪個地方開始看”.雖然我之前數十篇dubbo原始碼解析都在回答這個問題,但是每發出一篇,都還是有小夥伴私信問我同樣的問題.對此,我當然是選擇”原諒他”.因此,本篇我又再次粗暴式的點題,”怎麼看原始碼”.就把本篇來說,這個服務取用的原理,我們要從哪裡開始看呢?我們一起看一下官方檔案

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

  • 將spring的schemas標簽資訊轉換bean,然後透過這個bean的資訊,連線、訂閱zookeeper節點資訊建立一個invoker

  • invoker的資訊建立一個動態代理物件

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

時序圖

直入主題

有部分朋友反饋說程式碼貼圖手機閱讀不友好,但是如果不貼圖的話,很多朋友看完文章自己debug的時候找相應的類和方法又要花費大量時間,所以折中一下,貼圖和貼程式碼結合

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究竟用到了哪種方式實現代理呢?我們往下看

序號5的結束本篇也接近了尾聲.本篇綜合性較強,其中涉及到之前的內容本篇將不再重覆提及,可根據註釋中的標記自行檢視.

寫在最後

2017即將結束,這一年來,給我的一些感悟就是,任何事情無關大小,只要加上”堅持”二字,都會變得格外的不易.大家都知道,健身房主要賺的就是那些堅持不下去人的錢,我就有一個愛好健身的朋友,他能堅持每天健身,我也問過他”秘訣”.他是這樣說的

別人都是為了做別的事輕易就放棄健身,而我最常和別人說的話是,我要去健身了,不能聚會了

這個真實的例子,總結起來也就一句話時間在哪,成就就在哪.其實所謂門檻,能力夠了就是門,能力不足就是坎.

期待下週的dubbo原始碼解析繼續與你相遇.鑒於本人才疏學淺,不對的地方還望斧正,也歡迎關註我的簡書,名稱為肥朝

贊(0)

分享創造快樂