淺入淺出Dubbo

前言

接下來一段時間敖丙將帶你們開啓緊張刺激的 Dubbo 之旅!是的要開始寫 Dubbo 系列的文章了,以前我已經寫過一篇架構演進的文章,也說明了微服務的普及化以及重要性,服務化場景下隨之而來的就是服務之間的通訊問題,那服務間的通訊腦海中想到的就是 RPC,說到 RPC 就離不開我們的 Dubbo。面試

這篇文章敖丙先帶着你們來總覽全局,通常而言熟悉一個框架你要先知道這玩意是作什麼的,能解決什麼痛點,核心的模塊是什麼,大體運轉流程是怎樣的。編程

你要一來就扎入細節之中沒法自拔,一波 DFS 直接被勸退的可能性高達99.99%,因此本暖男敖丙將帶你們先過一遍 Dubbo 的簡介、整體分層、核心組件以及大體調用流程緩存

不只如此我還會帶着你們過一遍若是要讓你設計一個 RPC 框架你看看都須要什麼功能?這波操做以後你會發現嘿嘿 Dubbo 怎麼設計的和我想的同樣呢?真是英雄所見略同啊!網絡

並且我還會寫一個簡單版 RPC 框架實現,讓你們明白 RPC 究竟是如何工做的。架構

若是看了這篇文章你要仍是不知道 Dubbo 是啥,我能夠要勸退了。負載均衡

咱們先來談一談什麼叫 RPC ,我發現有不少同窗不太瞭解這個概念,還有人把 RPC 和 HTTP 來進行對比。因此我們先來講說什麼是 RPC。框架

什麼是 RPC

RPC,Remote Procedure Call 即遠程過程調用,遠程過程調用其實對標的是本地過程調用,本地過程調用你熟悉吧?運維

想一想那青蔥歲月,你在大學趕着期末大做業,正在攻克圖書管理系統,你奮筆疾書瘋狂地敲擊鍵盤,實現了圖書借閱、圖書歸還等等模塊,你實現的一個個方法之間的調用就叫本地過程調用。異步

你要是和我說你實現圖書館裏系統已經用了服務化,搞了遠程調用了,我只能和你說你有點東西。socket

簡單的說本機上內部的方法調用均可以稱爲本地過程調用,而遠程過程調用實際上就指的是你本地調用了遠程機子上的某個方法,這就是遠程過程調用。

因此說 RPC 對標的是本地過程調用,至於 RPC 要如何調用遠程的方法能夠走 HTTP、也能夠是基於 TCP 自定義協議。

因此說你討論 RPC 和 HTTP 就不是一個層級的東西。

RPC 框架就是要實現像那小助手同樣的東西,目的就是讓咱們使用遠程調用像本地調用同樣簡單方便,而且解決一些遠程調用會發生的一些問題,使用戶用的無感知、舒心、放心、順心,它好我也好,快樂沒煩惱。

如何設計一個 RPC 框架

在明確了什麼是 RPC,以及 RPC 框架的目的以後,我們想一想若是讓你作一款 RPC 框架你該如何設計?

服務消費者

咱們先從消費者方(也就是調用方)來看須要些什麼,首先消費者面向接口編程,因此須要得知有哪些接口能夠調用,能夠經過公用 jar 包的方式來維護接口。

如今知道有哪些接口能夠調用了,可是隻有接口啊,具體的實現怎麼來?這事必須框架給處理了!因此還須要來個代理類,讓消費者只管調,啥事都別管了,我代理幫你搞定

對了,還須要告訴代理,你調用的是哪一個方法,而且參數的值是什麼。

雖然說代理幫你搞定可是代理也須要知道它到底要調哪一個機子上的遠程方法,因此須要有個註冊中心,這樣調用方從註冊中心能夠知曉能夠調用哪些服務提供方,通常而言提供方不止一個,畢竟只有一個掛了那不就沒了。

因此提供方通常都是集羣部署,那調用方須要經過負載均衡來選擇一個調用,能夠經過某些策略例如同機房優先調用啊啥的。

固然還須要有容錯機制,畢竟這是遠程調用,網絡是不可靠的,因此可能須要重試什麼的。

還要和服務提供方約定一個協議,例如咱們就用 HTTP 來通訊就好啦,也就是你們要講同樣的話,否則可能聽不懂了。

固然序列化必不可少,畢竟咱們本地的結構是「立體」的,須要序列化以後才能傳輸,所以還須要約定序列化格式

而且這過程當中間可能還須要摻入一些 Filter,來做一波統一的處理,例如調用計數啊等等。

這些都是框架須要作的,讓消費者像在調用本地方法同樣,無感知。

服務提供者

服務提供者確定要實現對應的接口這是毋庸置疑的。

而後須要把本身的接口暴露出去,向註冊中心註冊本身,暴露本身所能提供的服務。

而後有消費者請求過來須要處理,提供者須要用和消費者協商好的協議來處理這個請求,而後作反序列化

序列化完的請求應該扔到線程池裏面作處理,某個線程接受到這個請求以後找到對應的實現調用,而後再將結果原路返回

註冊中心

上面其實咱們都提到了註冊中心,這東西就至關於一個平臺,你們在上面暴露本身的服務,也在上面得知本身能調用哪些服務。

固然還能作配置中心,將配置集中化處理,動態變動通知訂閱者。

監控運維

面對衆多的服務,精細化的監控和方便的運維必不可少。

這點不少開發者在開發的時候察覺不到,到你真正上線開始運行維護的時候,若是沒有良好的監控措施,快速的運維手段,到時候就是睜眼瞎!手足無措,等着挨批把!

那種痛苦不要問我爲何知道,我就是知道!

小結一下

讓咱們小結一下,大體上一個 RPC 框架須要作的就是約定要通訊協議,序列化的格式、一些容錯機制、負載均衡策略、監控運維和一個註冊中心!

簡單實現一個 RPC 框架

沒錯就是簡單的實現,上面咱們在思考如何設計一個 RPC 框架的時候想了不少,那算是生產環境使用級別的功能需求了,咱們這是 Demo,目的是突出 RPC框架重點功能 - 實現遠程調用

因此啥七七八八的都沒,而且我用僞代碼來展現,其實也就是刪除了一些保護性和約束性的代碼,由於看起來太多了不太直觀,須要一堆 try-catch 啥的,所以我刪減了一些,直擊重點。

Let's Do It!

首先咱們定義一個接口和一個簡單實現。

`public interface AobingService {  
    String hello(String name);  

public class AobingServiceImpl implements AobingService {  
    public String hello(String name) {  
        return "Yo man Hello,I am" + name;  
    }  
}`

而後咱們再來實現服務提供者暴露服務的功能。

`public class AobingRpcFramework { 
     public static void export(Object service, int port) throws Exception { 
          ServerSocket server = new ServerSocket(port);
          while(true) {
              Socket socket = server.accept();
              new Thread(new Runnable() {
                  //反序列化
                  ObjectInputStream input = new ObjectInputStream(socket.getInputStream()); 
                  String methodName = input.read(); //讀取方法名
                  Class<?>[] parameterTypes = (Class<?>[]) input.readObject(); //參數類型
                  Object[] arguments = (Object[]) input.readObject(); //參數
                  Method method = service.getClass().getMethod(methodName, parameterTypes);  //找到方法
                  Object result = method.invoke(service, arguments); //調用方法
                  // 返回結果
                  ObjectOutputStream output = new ObjectOutputStream(socket.getOutputStream());
                  output.writeObject(result);
              }).start();
          }
     }
    public static <T> T refer (Class<T> interfaceClass, String host, int port) throws Exception {
       return  (T) Proxy.newProxyInstance(interfaceClass.getClassLoader(), new Class<?>[] {interfaceClass}, 
            new InvocationHandler() {  
                public Object invoke(Object proxy, Method method, Object[] arguments) throws Throwable {  
                    Socket socket = new Socket(host, port);  //指定 provider 的 ip 和端口
                    ObjectOutputStream output = new ObjectOutputStream(socket.getOutputStream()); 
                    output.write(method.getName());  //傳方法名
                    output.writeObject(method.getParameterTypes());  //傳參數類型
                    output.writeObject(arguments);  //傳參數值
                    ObjectInputStream input = new ObjectInputStream(socket.getInputStream());  
                    Object result = input.readObject();  //讀取結果
                    return result;  
               }
        });  
    }  
}
`

好了,這個 RPC 框架就這樣好了,是否是很簡單?就是調用者傳遞了方法名、參數類型和參數值,提供者接收到這樣參數以後調用對於的方法返回結果就行了!這就是遠程過程調用。

咱們來看看如何使用

`//服務提供者只須要暴露出接口
       AobingService service = new AobingServiceImpl ();  
       AobingRpcFramework.export(service, 2333);  

       //服務調用者只須要設置依賴
       AobingService service = AobingRpcFramework.refer(AobingService.class, "127.0.0.1", 2333);  
       service.hello();`

看起來好像好不錯喲,不過這非常簡陋,用做 demo 有助理解仍是極好的!

接下來就來看看 Dubbo 吧!上正菜!

Dubbo 簡介

Dubbo 是阿里巴巴 2011年開源的一個基於 Java 的 RPC 框架,中間沉寂了一段時間,不過其餘一些企業還在用 Dubbo 並本身作了擴展,好比噹噹網的 Dubbox,還有網易考拉的 Dubbok。

可是在 2017 年阿里巴巴又重啓了對 Dubbo 維護。在 2017 年榮獲了開源中國 2017 最受歡迎的中國開源軟件 Top 3。

在 2018 年和 Dubbox 進行了合併,而且進入 Apache 孵化器,在 2019 年畢業正式成爲 Apache 頂級項目。

目前 Dubbo 社區主力維護的是 2.6.x 和 2.7.x 兩大版本,2.6.x 版本主要是 bug 修復和少許功能加強爲準,是穩定版本。

而 2.7.x 是主要開發版本,更新和新增新的 feature 和優化,而且 2.7.5 版本的發佈被 Dubbo 認爲是里程碑式的版本發佈,以後咱們再作分析。

它實現了面向接口的代理 RPC 調用,而且能夠配合 ZooKeeper 等組件實現服務註冊和發現功能,而且擁有負載均衡、容錯機制等。

Dubbo 整體架構

咱們先來看下官網的一張圖。

本丙再暖心的給上圖內每一個節點的角色說明一下。

節點

角色說明

Consumer

須要調用遠程服務的服務消費方

Registry

註冊中心

Provider

服務提供方

Container

服務運行的容器

Monitor

監控中心

我再來大體說一下總體的流程,首先服務提供者 Provider 啓動而後向註冊中心註冊本身所能提供的服務。

服務消費者 Consumer 啓動向註冊中心訂閱本身所需的服務。而後註冊中心將提供者元信息通知給 Consumer, 以後 Consumer 由於已經從註冊中心獲取提供者的地址,所以能夠經過負載均衡選擇一個 Provider 直接調用

以後服務提供方元數據變動的話註冊中心會把變動推送給服務消費者

服務提供者和消費者都會在內存中記錄着調用的次數和時間,而後定時的發送統計數據到監控中心

一些注意點

首先註冊中心和監控中心是可選的,你能夠不要監控,也不要註冊中心,直接在配置文件裏面寫而後提供方和消費方直連。

而後註冊中心、提供方和消費方之間都是長鏈接,和監控方不是長鏈接,而且消費方是直接調用提供方,不通過註冊中心

就算註冊中心和監控中心宕機了也不會影響到已經正常運行的提供者和消費者,由於消費者有本地緩存提供者的信息。

Dubbo 分層架構

總的而言 Dubbo 分爲三層,若是每一層再細分下去,一共有十層。別怕也就十層,本丙帶你們過一遍,你們先有個大體的印象,以後的文章丙會帶着你們再深刻。

大的三層分別爲 Business(業務層)、RPC 層、Remoting,而且還分爲 API 層和 SPI 層。

分爲大三層其實就是和咱們知道的網絡分層同樣的意思,只有井井有條,職責邊界清晰才能更好的擴展

而分 API 層和 SPI 層這是 Dubbo 成功的一點,採用微內核設計+SPI擴展,使得有特殊需求的接入方能夠自定義擴展,作定製的二次開發。

接下來我們再來看看每一層都是幹嗎的。

  • Service,業務層,就是我們開發的業務邏輯層。
  • Config,配置層,主要圍繞 ServiceConfig 和 ReferenceConfig,初始化配置信息。
  • Proxy,代理層,服務提供者仍是消費者都會生成一個代理類,使得服務接口透明化,代理層作遠程調用和返回結果。
  • Register,註冊層,封裝了服務註冊和發現。
  • Cluster,路由和集羣容錯層,負責選取具體調用的節點,處理特殊的調用要求和負責遠程調用失敗的容錯措施。
  • Monitor,監控層,負責監控統計調用時間和次數。
  • Portocol,遠程調用層,主要是封裝 RPC 調用,主要負責管理 Invoker,Invoker表明一個抽象封裝了的執行體,以後再作詳解。
  • Exchange,信息交換層,用來封裝請求響應模型,同步轉異步。
  • Transport,網絡傳輸層,抽象了網絡傳輸的統一接口,這樣用戶想用 Netty 就用 Netty,想用 Mina 就用 Mina。
  • Serialize,序列化層,將數據序列化成二進制流,固然也作反序列化。

SPI

我再稍微提一下 SPI(Service Provider Interface),是 JDK 內置的一個服務發現機制,它使得接口和具體實現徹底解耦。咱們只聲明接口,具體的實現類在配置中選擇。

具體的就是你定義了一個接口,而後在META-INF/services目錄下放置一個與接口同名的文本文件,文件的內容爲接口的實現類,多個實現類用換行符分隔。

這樣就經過配置來決定具體用哪一個實現!

而 Dubbo SPI 還作了一些改進,篇幅有限留在以後再談。

Dubbo 調用過程

上面我已經介紹了每一個層究竟是幹嗎的,咱們如今再來串起來走一遍調用的過程,加深你對 Dubbo 的理解,讓知識點串起來,由點及面來一波連連看。

咱們先從服務提供者開始,看看它是如何工做的。

服務暴露過程

首先 Provider 啓動,經過 Proxy 組件根據具體的協議 Protocol 將須要暴露出去的接口封裝成 Invoker,Invoker 是 Dubbo 一個很核心的組件,表明一個可執行體。

而後再經過 Exporter 包裝一下,這是爲了在註冊中心暴露本身套的一層,而後將 Exporter 經過 Registry 註冊到註冊中心。 這就是總體服務暴露過程。

消費過程

接着咱們來看消費者調用流程(把服務者暴露的過程也在圖裏展現出來了,這個圖其實算一個挺完整的流程圖了)。

首先消費者啓動會向註冊中心拉取服務提供者的元信息,而後調用流程也是從 Proxy 開始,畢竟都須要代理才能無感知。

Proxy 持有一個 Invoker 對象,調用 invoke 以後須要經過 Cluster 先從 Directory 獲取全部可調用的遠程服務的 Invoker 列表,若是配置了某些路由規則,好比某個接口只能調用某個節點的那就再過濾一遍 Invoker 列表。

剩下的 Invoker 再經過 LoadBalance 作負載均衡選取一個。而後再通過 Filter 作一些統計什麼的,再經過 Client 作數據傳輸,好比用 Netty 來傳輸。

傳輸須要通過 Codec 接口作協議構造,再序列化。最終發往對應的服務提供者。

服務提供者接收到以後也會進行 Codec 協議處理,而後反序列化後將請求扔到線程池處理。某個線程會根據請求找到對應的 Exporter ,而找到 Exporter 其實就是找到了 Invoker,可是還會有一層層 Filter,通過一層層過濾鏈以後最終調用實現類而後原路返回結果。

完成整個調用過程!

總結

此次敖丙帶着你們先了解了下什麼是 RPC,而後規劃了一波 RPC 框架須要哪些組件,而後再用代碼實現了一個簡單的 RPC 框架。

而後帶着你們瞭解了下 Dubbo 的發展歷史、整體架構、分層設計架構以及每一個組件是幹嗎的,再帶着大夥走了一遍總體調用過程。

我真的是太暖了啊!

dubbo近期我會安排幾個章節繼續展開,最後會出一個面試版本的dubbo,咱們拭目以待吧。

我是敖丙,你知道的越多,你不知道的越多,咱們下期見!

人才們的 【三連】 就是敖丙創做的最大動力,若是本篇博客有任何錯誤和建議,歡迎人才們留言!

相關文章
相關標籤/搜索