2、dubbo的架構思路
2.1 dubbo框架設計
dubbo官網的架構設計 提供了一張總體的框架圖,10個層級看起來挺嚇人的。可是其核心總結起來就是:Microkernel + Plugin(微內核+插件) 。
官網介紹的架構設計思想是兩點:
採用 URL 做爲配置信息的統一格式,全部擴展點都經過傳遞 URL 攜帶配置信息;
採用 Microkernel + Plugin 模式,Microkernel 只負責組裝 Plugin,Dubbo 自身的功能也是經過擴展點實現的,也就是 Dubbo 的全部功能點均可被用戶自定義擴展所替換。
對於第一點比較容易理解,由於是分佈式環境,各系統之間的參數傳遞基於URL來攜帶配置信息,全部的參數都封裝成 Dubbo 自定義的 URL 對象進行傳遞。URL 對象主要包括如下屬性:
String protocol
String host
int port
String path
Map<String, String> parameters
第二點:系統裏抽象的各個模塊,每每有不少不一樣的實現方案,好的設計來講:模塊之間基於接口編程 ,模塊之間不對實現類進行硬編碼。一旦代碼裏涉及具體的實現類,就違反了可拔插的原則,若是須要替換一種實現,就須要修改代碼,例如:
if(參數=="dubbo"){ return new DubboProtocol(); } else if(參數 == "rmi"){ return new RMIProtocol(); }
SPI的解決方案就呼之欲出了,一個接口對應有多個實現類的時候該怎樣指定麼?若是採用上述設計是很糟糕的,用if else
來寫死本身的服務發現,若是新增一種協議則還須要去修改代碼,針對此類問題Java自己提供了spi機制,能夠作到服務發現和動態擴展,可是弊端就是一初始化就把全部實現類給加載進去,dubbo改進了spi並從新命名爲ExtensionLoader(擴展點機制),按照用戶配置來指定加載模塊,只須要約定一下路徑便可:
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/";
這部分源碼能夠考察知識點很是多,對使用者來講是透明的,而精華卻良多,尤爲結合java-spi,jvm以及spring等多方面對比、借鑑,所以理論上能夠好好掌握,固然最好的學習方式就是按照極簡的思路來實現一個簡版RPC工具。
2.2dubbo原理、與Spring融合
dubbo是一個分佈式服務框架,致力於提供高性能和透明化的RPC遠程服務調用方案,以及SOA服務治理方案。既然是分佈式 那就意味着:一個業務分拆多個子業務,部署在不一樣的服務器上,既然各服務是部署在不一樣的服務器上,那服務間的調用就是要經過網絡通訊。既然涉及到了網絡通訊,那麼服務消費者調用服務以前,都要寫各類網絡請求,編解碼之類的相關代碼,明顯是很不友好的.dubbo所說的透明,就是指,讓調用者對網絡請求,編解碼之類的細節透明,讓咱們像調用本地服務同樣調用遠程服務,甚至感受不到本身在調用遠程服務。
public class ProxyFactory implements InvocationHandler { private Class interfaceClass; public ProxyFactory(Class interfaceClass) { this.interfaceClass = interfaceClass; }
項目引入dubbo的方法推薦用XML配置的方式引入,即使是老項目拆分改造,只要是Spring工程,這個都是比較好作的,能夠想象本身若是開發一箇中間件服務,若是把服務嵌入spring容器當中呢?做爲高級開發人員這個也是一個進階的既能項。XML 配置 方式是基於 Spring 的 Schema 和 XML 擴展機制實現的。經過該機制,咱們能夠編寫本身的 Schema,並根據自定義的 Schema 自定義標籤來配置 Bean。
使用 Spring 的 XML 擴展機制有如下幾個步驟:
定義 Schema(編寫 .xsd 文件)
定義 JavaBean
編寫 NamespaceHandler 和 BeanDefinitionParser 完成 Schema 解析
編寫 spring.handlers 和 spring.schemas 文件串聯解析部件
在 XML 文件中應用配置
最好的學習效果是能夠本身按照模板來同樣畫瓢來創做一個相似的xml配置。可參考《dubbo源碼解析-簡單原理、與spring融合》
2.3 服務發佈
服務的發佈總共作了如下幾件事,這個也能夠從日誌log上看出來:
暴露本地服務
暴露遠程服務
啓動netty
鏈接zookeeper
到zookeeper註冊
監聽zookeeper
貼出一張官方文檔的服務發佈圖
首先 ServiceConfig
類拿到對外提供服務的實際類 ref(如:HelloWorldImpl
),而後經過 ProxyFactory
類的 getInvoker
方法使用 ref 生成一個 AbstractProxyInvoker
實例,到這一步就完成具體服務到 Invoker
的轉化。接下來就是 Invoker
轉換到 Exporter
的過程。Dubbo 處理服務暴露的關鍵就在 Invoker
轉換到 Exporter
的過程,上圖中的紅色部分。 Dubbo 的實現 Dubbo 協議的 Invoker
轉爲 Exporter
發生在 DubboProtocol
類的 export
方法,它主要是打開 socket
偵聽服務,並接收客戶端發來的各類請求,通信細節由 Dubbo 本身實現。
上面摘抄了官方文檔(具體連接請戳 ),可能仍是有點抽象,實際上從代碼層面進行分析: 此處就是將本地的須要暴漏的方法以url形式做爲參數傳入 exportLocal()
方法,url以前已經提到過包含了ip地址、端口、接口以及配置信息等。
這時會執行到一個接口方法getInvoker()
,這是一個註解了@Adaptive的方法,該方法的具體實現類是運行中生成動態編譯的Adaptive類,把java編譯出來的動態類貼出來debug以下,恍然大悟,原來他就是幾個if判斷,來告訴程序我這個url參數配置的是哪一種協議,我如今就動態的去調用這個擴展點服務(dubbo-spi),動態編譯的好處就是不用將代碼寫死,在協議會擴展的狀況下,我根據你配置的協議來動態的生成個人extensionLoader,再來加載我所須要的Invoker。
上圖引用的是本地服務的暴露執行,如果遠程服務的暴露,arg2參數的開頭則會是registry://192.168.0.1:2181/com.alibaba.dubbo.** / **。從exporter對象裏包含的invoker屬性能夠看出,invoker包含的攜帶ip、端口、接口以及配置信息的url。
如今開始進入到遠程服務暴露的過程,通常來講這部分是應用和考察最多的點,經過配置的協議將服務暴露給外部調用。dubbo所支持的協議有多重,默認推薦dubbo協議,因而在動態代理的時候會生成Protocol$Adpative代理類,該代理類實現了RPC 協議接口 ,再經過擴展機制將服務加載進來。
關鍵步驟4-Protocol$Adpative代理類
加載了實現類後方法會順着調用鏈路進入到dubbo協議中的export()方法中來,能夠再DubboProtocol類中設置斷點觀察方法執行,此處完成了一個綁定,將暴露的接口+DubboExporter進行關聯放入map中緩存。
後面的步驟再也不一一展開來說,愈來愈貼近底層和網絡通訊,咱們在調用dubbo接口的時候dubbo都爲了咱們作了這樣的工做,可是對開發人員來講都是透明無感知的:
exchange 信息交換層。封裝請求響應模式,同步轉異步,以 Request, Response 爲中心。
transport 網絡傳輸層:抽象 mina 和 netty 爲統一接口,以 Message 爲中心。
serialize 數據序列化層:可複用的一些工具,擴展接口爲 Serialization, ObjectInput, ObjectOutput, ThreadPool
這裏引用一張肥朝博客 的總結圖,來總結服務暴露所幹的事情: 首先是經過動態代理店的方式將暴露的接口組裝成url形式的invoker,而後再根據url的配置信息來指定傳輸協議、交換方式、序列化方式等等,因爲dubbo採用了自定義的SPI擴展,各層之間都是相互獨立的,只有在調用的時候才知道所調用的具體擴展實現,這裏仍是以jdk或者javasisit的方式來動態代理實現。
2.4 服務引用
首先 ReferenceConfig
類的init
方法調用 Protocol 的 refer
方法生成 Invoker 實例(如上圖中的紅色部分),這是服務消費的關鍵。接下來把 Invoker 轉換爲客戶端須要的接口(如:HelloWorld)。關於每種協議如 RMI/Dubbo/Web service
等它們在調用 refer
方法生成Invoker
實例的細節和上一章節所描述的相似。
上述圖和文字是摘自官方文檔的原話(地址在這裏 ),總結來講就是幹了兩件事情:一、將spring的schemas標籤信息轉換bean,而後經過這個bean的信息,鏈接、訂閱zookeeper節點信息建立一個invoker。二、將invoker的信息建立一個動態代理對象。貼一張服務應用的時序圖:
這裏又一次出現了Invoker
,這個抽象的概念真是無處不在呀,dubbo中最重要的兩種 Invoker
:服務提供 Invoker
和服務消費 Invoker
。Invoker從類的設計信息上是封裝了 Provider和Consumer地址及 Service 接口信息 ,咱們在本身的子系統調用遠程接口的時候,會像調用本身的方法同樣,好比在消費端這裏用註解@Autowirted
自動注入一個遠程接口進來,這個遠程接口就是上圖中服務消費端的 proxy
,可是遠程接口是須要網絡通訊、編碼解碼等等一系列工做的,要封裝這個通訊細節,讓用戶像以本地調用方式調用遠程服務,就必須使用代理,而後說到動態代理,用戶代碼經過這個 proxy
調用其對應的 Invoker
,而該 Invoker
實現了真正的遠程服務調用。
3、Dubbo實戰應用
實戰應用主要是從應用層面講引入dubbo框架後如何作一些關鍵配置
3.1 Dubbo 支持四種配置方式:
XML 配置:基於 Spring 的 Schema 和 XML 擴展機制實現(推薦) 屬性配置:加載 classpath 根目錄下的 dubbo.properties API 配置:經過硬編碼方式配置(不推薦使用,可學習加深源碼理解) 註解配置:經過註解方式配置(Dubbo-2.5.7及以上版本支持,不推薦使用)
3.2 集羣容錯
在集羣調用失敗時,Dubbo 提供了多種容錯方案,缺省爲 failover 重試。
Invoker 是 Provider 的一個可調用 Service 的抽象,Invoker 封裝了 Provider 地址及 Service 接口信息
Directory 表明多個 Invoker,能夠把它當作 List<Invoker> ,但與 List 不一樣的是,它的值多是動態變化的,好比註冊中心推送變動
Cluster 將 Directory 中的多個 Invoker 假裝成一個 Invoker,對上層透明,假裝過程包含了容錯邏輯,調用失敗後,重試另外一個
Router 負責從多個 Invoker 中按路由規則選出子集,好比讀寫分離,應用隔離等
LoadBalance 負責從多個 Invoker 中選出具體的一個用於本次調用,選的過程包含了負載均衡算法,調用失敗後,須要重選。
集羣調用的配置可從以下列表中選擇:
<dubbo:service cluster="failsafe" />
集羣模式
說明
Failfast Cluster
快速失敗,只發起一次調用,失敗當即報錯。一般用於非冪等性的寫操做,好比新增記錄。
Failsafe Cluster
失敗安全,出現異常時,直接忽略。一般用於寫入審計日誌等操做。
Failback Cluster
失敗自動恢復,後臺記錄失敗請求,定時重發。一般用於消息通知操做。
Forking Cluster
並行調用多個服務器,只要一個成功即返回。一般用於實時性要求較高的讀操做,但須要浪費更多服務資源。可經過 forks="2" 來設置最大並行數。
Broadcast Cluster
廣播調用全部提供者,逐個調用,任意一臺報錯則報錯 [2]。一般用於通知全部提供者更新緩存或日誌等本地資源信息。
3.3 負載均衡
Random LoadBalance
隨機 ,按權重設置隨機機率。
在一個截面上碰撞的機率高,但調用量越大分佈越均勻,並且按機率使用權重後也比較均勻,有利於動態調整提供者權重。
RoundRobin LoadBalance
輪詢 ,按公約後的權重設置輪詢比率。
存在慢的提供者累積請求的問題,好比:第二臺機器很慢,但沒掛,當請求調到第二臺時就卡在那,長此以往,全部請求都卡在調到第二臺上。
LeastActive LoadBalance
最少活躍調用數 ,相同活躍數的隨機,活躍數指調用先後計數差。
使慢的提供者收到更少請求,由於越慢的提供者的調用先後計數差會越大。
ConsistentHash LoadBalance
一致性 Hash ,相同參數的請求老是發到同一提供者。
當某一臺提供者掛時,本來發往該提供者的請求,基於虛擬節點,平攤到其它提供者,不會引發劇烈變更。
算法參見:http://en.wikipedia.org/wiki/Consistent_hashing
缺省只對第一個參數 Hash,若是要修改,請配置 <dubbo:parameter key="hash.arguments" value="0,1" />
缺省用 160 份虛擬節點,若是要修改,請配置 <dubbo:parameter key="hash.nodes" value="320" />
SPI
一、你是否瞭解SPI,講一講什麼是SPI,爲何要使用SPI? SPI具體約定: 當服務的提供者,提供了服務接口的一種實現以後,在jar包的META-INF/services/
目錄裏同時建立一個以服務接口命名的文件。該文件裏就是實現該服務接口的具體實現類。而當外部程序裝配這個模塊的時候,就能經過該jar包META-INF/services/
裏的配置文件找到具體的實現類名,並裝載實例化,完成模塊的注入(從使用層面來講,就是運行時,動態給接口添加實現類 )。 基於這樣一個約定就能很好的找到服務接口的實現類,而不須要再代碼裏制定(不須要在代碼裏寫死)。
這樣作的好處: java設計出SPI目的是爲了實如今模塊裝配的時候能不在程序裏動態指明,這就須要一種服務發現機制。這樣程序運行的時候,該機制就會爲某個接口尋找服務的實現,有點相似IOC的思想,就是將裝配的控制權移到程序以外,在模塊化設計中這個機制尤爲重要。例如,JDBC驅動,能夠加載MySQL、Oracle、或者SQL Server等,目前有很多框架用它來作服務的擴張發現。回答這個問題能夠延伸一下和API的對比,API是將方法封裝起來給調用者使用的,SPI是給擴展者使用的。
二、對類加載機制瞭解嗎,說一下什麼是雙親委託模式,他有什麼弊端,這個弊端有沒有什麼咱們熟悉的案例,解決這個弊端的原理又是怎麼樣的? 擴展延生的一道題。
三、Dubbo的SPI和JDK的SPI有區別嗎?有的話,究竟有什麼區別? Dubbo 的擴展點加載是基於JDK 標準的 SPI 擴展點發現機制加強而來的,Dubbo 改進了 JDK 標準的 SPI 的如下問題:
JDK 標準的 SPI 會一次性實例化擴展點全部實現,若是有擴展實現初始化很耗時,但若是沒用上也加載,會很浪費資源。
增長了對擴展點 IoC 和 AOP 的支持,一個擴展點能夠直接 setter 注入其它擴展點。
上文已提供。另外在博客中也單獨對此寫了一篇《Dubbo內核之SPI機制》 、《跟我學Dubbo系列之Java SPI機制簡介》
四、Dubbo中SPI也增長了IoC,先講講Spring的IoC,而後再講講Dubbo裏面又是怎麼作的 五、Dubbo中SPI也增長了AOP,那你講講這用到了什麼設計模式,Dubbo又是如何作的.
Dubbo原理
一、Dubbo角色和設計是怎麼樣的,原理是怎麼樣的?請簡單談談?
二、有沒有考慮過本身實現一個相似dubbo的RPC框架,若是有,請問你會若是着手實現?(面試高頻題,區分度高) 可從兩個方面去入手,考慮接口擴展性,改造JDK的SPI機制來實現本身的擴展SPI機制 。另外就是從動態代理入手,從網絡通訊、編碼解碼這些步驟以動態代理的方式植入遠程調用方法中,實現透明化的調用。
三、用過mybatis是否知道Mapper接口的原理嗎?(若是回答得不錯,而且提到動態代理這個關鍵詞會繼續往下問,那這個動態代理又是如何經過依賴注入到Mapper接口的呢?)
四、服務發佈過程當中作了哪些事? 暴露本地服務、暴露遠程服務、啓動netty、鏈接zookeeper、到zookeeper註冊、監聽zookeeper
五、dubbo都有哪些協議,他們之間有什麼特色,缺省值是什麼? dubbo支持多種協議,默認使用的是dubbo
協議,具體介紹官方文檔寫得很清楚,傳送地址:相關協議介紹 ,重點是掌握好推薦dubbo協議。Dubbo 缺省協議採用單一長鏈接和 NIO 異步通信,適合於小數據量大併發的服務調用,以及服務消費者機器數遠大於服務提供者機器數的狀況。
六、什麼是本地暴露和遠程暴露,他們的區別? 在dubbo中咱們一個服務可能既是Provider,又是Consumer,所以就存在他本身調用本身服務的狀況,若是再經過網絡去訪問,那天然是捨近求遠,所以他是有本地暴露服務的這個設計.從這裏咱們就知道這個二者的區別
本地暴露是暴露在JVM中,不須要網絡通訊.
遠程暴露是將ip,端口等信息暴露給遠程客戶端,調用時須要網絡通訊.
七、服務暴露中遠程暴露的整體過程,畫圖和文字方式說明 詳見上述說明
zookeeper
一、通常選擇什麼註冊中心,還有別的選擇嗎? zk爲默認推薦,其他還有Multicast、redis、Simple等註冊中心。
二、dubbo中zookeeper作註冊中心,若是註冊中心集羣都掛掉,那發佈者和訂閱者還能通訊嗎?(面試高頻題) zookeeper的信息會緩存到服務器本地做爲一個cache緩存文件,而且轉換成properties對象方便使用,每次調用時,按照本地存儲的地址進行調用,可是沒法從註冊中心去同步最新的服務列表,短時間的註冊中心掛掉是沒關係的,但必定要儘快修復。因此掛掉是沒關係的,但前提是你沒有增長新的服務,若是你要調用新的服務,則是不能辦到的。
三、項目中有使用過多線程嗎?有的話講講你在哪裏用到了多線程?(面試高頻題) 以dubbo爲例,這裏的作法是:創建線程池,定時的檢測並鏈接註冊中心,若是失敗了就重連,其實也就是一個定時任務執行器 。可能作了兩三年java還沒真正在項目中開啓過線程,問到這個問題時菊花一緊,可是定時任務執行器這種需求在項目中仍是很常見的,好比失敗重連、輪詢執行任務等等,能夠參考這個例子,把大家的定時任務場景和這裏的多線程用法套在一塊兒。
四、zookeeper的java客戶端你使用過哪些? zookeeper是支持ZkClient和Curator兩種,關於zk的使用場景,除了以dubbo做爲註冊中心之外,zk在分佈式環境做爲協調服務器有許多應用場景,能夠嘗試用java來調用zk服務作一些協調服務,如負載均衡、數據訂閱與發佈等等。SnailClimb寫了一篇優秀的博客《多是全網把ZK概念講的最清楚的一篇文章》
五、服務提供者能實現失效踢出是什麼原理(高頻題) 在分佈式系統中,咱們經常須要知道某個機器是否可用,傳統的開發中,能夠經過Ping某個主機來實現,Ping得通說明對方是可用的,相反是不可用的,ZK 中咱們讓全部的機器都註冊一個臨時節點,咱們判斷一個機器是否可用,咱們只須要判斷這個節點在ZK中是否存在就能夠了,不須要直接去鏈接須要檢查的機器,下降系統的複雜度。
六、zookeeper的有哪些節點,他們有什麼區別?講一下應用場景 zookeeper中節點是有生命週期的.具體的生命週期取決於節點的類型.節點主要分爲持久節點(Persistent)和臨時節點(Ephemeral),可是更詳細的話還能夠加上時序節點(Sequential),建立節點中每每組合使用,所以也就是4種:持久節點、持久順序節點、臨時節點、臨時順序節點。
所謂持久節點,是指在節點建立後,就一直存在,直到有刪除操做來主動清除這個節點,也就是說不會由於建立該節點的客戶端會話失效而消失。
臨時節點的生命週期和客戶端會話綁定,也就是說,若是客戶端會話失效,那麼這個節點就會自動被清除掉。
七、在dubbo中,何時更新本地的zookeeper信息緩存文件?訂閱zookeeper信息的總體過程是怎麼樣的? dubbo向zk發送了訂閱請求之後,會去監聽zk的回調,(若是zk有回調就回去調用notify方法),接着會去建立接口配置信息的持久化節點,同時dubbo也設置了對該節點的監聽,zk節點若是發生了變化那麼會觸發回調方法,去更新zk信息的緩存文件,同時註冊服務在調用的時候會去對比最新的配置信息節點,有差異的話會以最新信息爲準從新暴露。《dubbo源碼解析-zookeeper訂閱》
服務引用 一、描述一下dubbo服務引用的過程,原理 上文已提供。
二、既然你提到了dubbo的服務引用中封裝通訊細節是用到了動態代理,那請問建立動態代理經常使用的方式有哪些,他們又有什麼區別?dubbo中用的是哪種?(高頻題) jdk、cglib還有javasisit,JDK的動態代理代理的對象必需要實現一個接口,而針對於沒有接口的類,則可用CGLIB。要明白二者區別必需要了解原理,明白了原理天然一通百通,CGLIB其原理也很簡單,對指定的目標類生成一個子類,並覆蓋其中方法實現加強,但因爲採用的是繼承,因此不能對final修飾的類進行代理。除了以上兩種你們都很熟悉的方式外,其實還有一種方式,就是javassist生成字節碼來實現代理(dubbo多處用到了javassist)。
集羣容錯 一、dubbo提供了集中集羣容錯模式? 二、談談dubbo中的負載均衡算法及特色?最小活躍數算法中是如何統計活躍數的?簡單談談一致性哈希算法 這部分能夠多結合官方文檔進行學習,並且涉及到了負載均衡的多個重要算法,也是高頻的考察熱點。
三、怎麼經過dubbo實現服務降級的,降級的方式有哪些,又有什麼區別? 當網站處於高峯期時,併發量大,服務能力有限,那麼咱們只能暫時屏蔽邊緣業務,這裏面就要採用服務降級策略了。首先dubbo中的服務降級分紅兩個:屏蔽(mock=force)、容錯(mock=fail)。
mock=force:return+null
表示消費方對該服務的方法調用都直接返回 null 值,不發起遠程調用。用來屏蔽不重要服務不可用時對調用方的影響。
mock=fail:return+null
表示消費方對該服務的方法調用在失敗後,再返回 null 值,不拋異常。用來容忍不重要服務不穩定時對調用方的影響。
要生效須要在dubbo後臺進行配置的修改:
四、dubbo監控平臺可以動態改變接口的一些設置,其原理是怎樣的? 改變註冊在zookeeper上的節點信息,從而zookeeper通知從新生成invoker(這些具體細節在zookeeper建立節點,zookeeper鏈接,zookeeper訂閱中都詳細講了,這裏再也不重複)。