Dubbo服務暴露分析

来源:https://www.cnblogs.com/maratong/archive/2020/02/19/12333658.html
-Advertisement-
Play Games

Dubbo的服務暴露是一個重要的特性,瞭解其機制很重要。之前有很多人寫了有關的源代碼分析,在本文中不再重新分析。官方文檔中的一篇寫的就很好,本文主要是有關內容進行補充與總結。 傳送門: "服務導出" 為什麼要服務暴露 服務暴露分為遠程暴露和本地暴露。在遠程服務暴露中會將服務信息上傳到註冊中心。這時客 ...


Dubbo的服務暴露是一個重要的特性,瞭解其機制很重要。之前有很多人寫了有關的源代碼分析,在本文中不再重新分析。官方文檔中的一篇寫的就很好,本文主要是有關內容進行補充與總結。
傳送門:服務導出

為什麼要服務暴露

服務暴露分為遠程暴露和本地暴露。在遠程服務暴露中會將服務信息上傳到註冊中心。這時客戶端要調用某個服務時會從註冊中心找到該服務的遠程地址等信息。然後客戶端根據這個地址進行遠程調用。服務端接收到遠程調用請求後會最終調用getInvoker()方法進行查找對用的invoker。在getInvoker()方法中會從一個HashMap中進行查找,如果在這個Map中查找不到就會拋出異常。在遠程服務暴露中,會按照規則將實例Invoker存儲在HashMap中,其中Key名包含埠、介面名、介面版本和介面分組。所以進行服務暴露很重要。
本地服務暴露是暴露在JVM中,不需要遠程通信。Dubbo會預設把遠程服務用injvm協議再暴露一份。
為什麼會有本地服務暴露
在Dubbo中,一個服務可以即是provider,又是Consumer,因此就存在它自己調用自己服務的時候,如果再通過網路去訪問,那麼就是捨近求遠,因此有了本地暴露服務這個設計。消費者可以直接消費同一個JVM內部的服務,避免了跨網路進行遠程通信。

服務暴露起點

我們會通過XML或註解的方式來指定要暴露的服務。l例子如下:

<bean id=“xxxService” class=“com.xxx.XxxServiceImpl” /> 
<!-- 增加暴露遠程服務配置 -->
<dubbo:service interface=“com.xxx.XxxService” ref=“xxxService” /> 

這時會創建出一個ServiceBean對象。

public class ServiceBean<T> extends ServiceConfig<T> implements InitializingBean, DisposableBean,
        ApplicationContextAware, BeanNameAware,
        ApplicationEventPublisherAware 
public class ServiceConfig<T> extends ServiceConfigBase<T>
public abstract class ServiceConfigBase<T> extends AbstractServiceConfig {
    private static final long serialVersionUID = 3033787999037024738L;
    protected String interfaceName;
    //要暴露服務類的介面類
    protected Class<?> interfaceClass;
    //實現類引用
    protected T ref;
    //服務名 
    protected String path;
    protected ProviderConfig provider;
    protected String providerIds;
    protected volatile String generic;
    protected ServiceMetadata serviceMetadata;

ServiceBean和Spring有關,它繼承了InitializingBean和ApplicationEvent。在Bean初始化完成後會調用InitializingBean.afterPropertiesSet方法來執行服務暴露的準備工作。在Spring的context完成初始化後,會觸發ApplicationEventListener事件進行服務暴露,會執行onApplicationEvent方法。這時服務服務暴露就開始了。

public void onApplicationEvent(ContextRefreshedEvent event) {
    // 是否有延遲導出 && 是否已導出 && 是不是已被取消導出
    if (isDelay() && !isExported() && !isUnexported()) {
        // 導出服務
        export();
    }
}

整體流程

Dubbo真正的服務暴露入口是ServiceConfig#doExport()方法。首先ServiceConfig類拿到對外提供服務的實際類ref,然後通過ProxyFactory類的getInvoker方法使用ref生成一個AbstractProxyInvoker實例,到這一步就完成具體服務到Invoker的轉化。然後就是把Invoker通過具體的協議(比如Bubbo)轉化成Exporter
整體流程
參考了簡書肥朝的一篇文章: dubbo源碼解析-服務暴露原理

  • 調用ServiceConfig中的export方法,對配置進行檢查與更新,例如provider是否為空,註冊中心是否為空,protocols是否為空。然後檢測是否應該暴露,如果不應該暴露,則直接結束;然後檢測是否配置了延遲載入,如果是,則使用定時器來實現延遲載入的目的。
  • 調用expoer方法裡面的doExport()方法。通過exported變數判斷是否暴露,如果為true,直接返回;否則先設置為true,然後執行doExportUrls()方法。
  • 調用doExportUrls()方法,先通過loadRegistries載入註冊中心鏈接,然後遍歷ProtocolConfig集合,它是用戶指定的協議集合(比如Dubbo、REST),在裡面執行doExportUrlsFor1Protocol(protocolConfig, registryURLs)方法。
  • 執行doExportUrlsFor1Protocol(protocolConfig, registryURLs)方法,剛開始是組裝URL。(1)它把metrices、application、module、provider、protocol等所有配置都放入到map中,(2) 通過反射獲取interfaceClass的方法列表,先做簽名校驗,判斷該服務是否有配置的方法存在,然後該方法簽名是否有這個參數存在,都核對成功才將method的配置加入到map中;(3)將範化調用、版本號、method或者methods、token等信息加入到map;(4)獲取服務暴露地址和埠號,利用map內數據組裝成URL。
  • 然後根據url中的scope參數決定服務導出方式。scope=none,不導出服務;scope!=remote,導出到本地;scope!=local,導出到遠程。
  • 執行proxyFactory.getInvoker(ref, (Class)interfaceClass, url)方法,來獲取invoker,Dubbo預設的ProxyFactory實現類是JavasistProxyFacoty方法。

這時,第一大步服務轉化成Invoker已經完成,然後執行第二大步Invoker轉化成Exporter
先介紹本地服務的暴露機制

  • 調用exportLocal(url)。首先判斷URL的協議頭如果等於injvm,說明已經導出到本地了,無需再次導出。如果不是,則創建一個新的URL並將協議頭設置為injvm,並另外設置主機名和埠。然後創建invoker,並調用InjvmProtocalexport()方法暴露服務。
  • InjvmProtocolexport方法僅創建一個InjvmExporter,無其他邏輯。
public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
    return new InjvmExporter<T>(invoker, invoker.getUrl().getServiceKey(), exporterMap);
}
InjvmExporter(Invoker<T> invoker, String key, Map<String, Exporter<?>> exporterMap) {
    super(invoker);
    this.key = key;
    this.exporterMap = exporterMap;
    exporterMap.put(key, this);
}

然後介紹遠程服務暴露
如果有註冊中心,服務暴露後需要向註冊中心註冊服務信息
如果沒有註冊中心,直接暴露服務。

有註冊中心的暴露

  1. 調用doLocalExport()方法暴露服務。
  2. 向註冊中心註冊服務
  3. 向註冊中心進行訂閱ovrride數據
  4. 創建並返回DestroyableExporter
    調用doLocalExport()方法暴露服務
  • 首先根據invoker得到key,從bounds緩存變數中嘗試獲取exporter,如果獲取不到,則調用Protocal的export方法,預設的協議是dubbo,所以調用的DubboProtocol的export方法。
  • 調用DubboProtocol的export方法,首先從invoker中獲取對用的URL,然後根據服務組名、服務名、服務版本號和埠組成key,然後創建一個DubboExporter。
// demoGroup/com.alibaba.dubbo.demo.DemoService:1.0.1:20880
String key = serviceKey(url);
// 創建 DubboExporter
DubboExporter<T> exporter = new DubboExporter<T>(invoker, key, exporterMap);
// 將 <key, exporter> 鍵值對放入緩存中
exporterMap.put(key, exporter);
  • 調用openServer(url)方法。首先根據url獲取host:port,用於標識當前的伺服器實例。在同一臺機器上,同一個埠僅允許啟動一個伺服器實例。若某個埠上已有伺服器實例,此時調用reset方法重置一些伺服器的配置;如果沒有則調用createServer(url)方法創建一個伺服器實例。
  • 調用createServer(url)方法,有三個核心的邏輯,首先檢測是否存在server參數所代表的Transporter拓展,即網路傳輸方式(如Netty, Mina),如果沒有,則拋出異常;然後通過Exchangers.bind(url, requestHandler)方法創建伺服器實例;最後是檢測是否支持client參數所表示的Transporter拓展,不存在也是拋出異常。
  • 調用Exchangers.bind(url, requestHandler)方法,裡面又調用了下麵幾個方法。只保留了主要邏輯部分。
getExchanger(url).bind(url, handler);
   
public static Exchanger getExchanger(URL url) {
        String type = url.getParameter(Constants.EXCHANGER_KEY, Constants.DEFAULT_EXCHANGER);
        return getExchanger(type);
    }
public static Exchanger getExchanger(String type) {
        return ExtensionLoader.getExtensionLoader(Exchanger.class).getExtension(type);
    }

Exchange層是為了封裝請求/響應模式,例如:把同步請求轉化為非同步請求。預設的擴展點實現類是HeaderExchanger

  • 調用HeaderExchanger的bind方法
public static Server bind(URL url, ChannelHandler... handlers) throws RemotingException {
    if (url == null) {
        throw new IllegalArgumentException("url == null");
    }
    if (handlers == null || handlers.length == 0) {
        throw new IllegalArgumentException("handlers == null");
    }
    ChannelHandler handler;
    if (handlers.length == 1) {
        handler = handlers[0];
    } else {
        // 如果 handlers 元素數量大於1,則創建 ChannelHandler 分發器
        handler = new ChannelHandlerDispatcher(handlers);
    }
    // 獲取自適應 Transporter 實例,並調用實例方法
    return getTransporter().bind(url, handler);
}
  • 根據自適應擴展機制動態創建一個Transporter,Dubbo預設是NettyTransporter。
  • 調用NettyTransporter.bind(URL, ChannelHandler)方法。創建一個NettyServer實例。
  • 調用NettyServer.doOPen()方法,伺服器被開啟,服務也被暴露出來了。

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 在上一個版本中利用netty實現了簡單的一對一的RPC,需要手動設置服務地址,限制性較大。 在本文中,利用zookeeper作為服務註冊中心,在服務端啟動時將本地的服務信息註冊到zookeeper中,當客戶端發起遠程服務調用時,先從zookeeper中獲取該服務的地址,然後根據獲得的這個地址來利用n ...
  • 什麼是RPC RPC (Remote Procedure Call Protocol), 遠程過程調用,通俗的解釋就是:客戶端在不知道調用細節的情況下,調用存在於遠程電腦上的某個對象,就像調用本地應用程式中的對象一樣,不需要瞭解底層網路技術的協議。 簡單的整體工作流程 請求端發送一個調用的數據包, ...
  • 在dubbo中,關於註冊中心Registry的有關實現封裝在了dubbo registry模塊中。提供者(Provider)個消費者(Consumer)都是通過註冊中心進行資源的調度。當服務啟動時,provider會調用註冊中心的register方法將自己的服務通過url的方式發佈到註冊中心,而co ...
  • 在網路傳輸中,怎麼確保通道連接的可用性是一個很重要的問題,簡單的說,在網路通信中有客戶端和服務端,一個負責發送請求,一個負責接收請求,在保證連接有效性的背景下,這兩個物體扮演了什麼角色,心跳機制能有效的保證連接的可用性,那它的機制是什麼,下文中將會詳細講解。 網路層的可用性 首先講一下TCP,在du ...
  • 在上文中介紹了基礎類AbstractRegistry類的解釋,在本篇中將繼續介紹該包下的其他類。 FailbackRegistry 該類繼承了AbstractRegistry,AbstractRegistry中的註冊訂閱等方法,實際上就是一些記憶體緩存的變化,而真正的註冊訂閱的實現邏輯在Failbac ...
  • [TOC] python是數據分析的主要工具,它包含的數據結構和數據處理工具的設計讓python在數據分析領域變得十分快捷。它以NumPy為基礎,並對於需要類似 for迴圈 的大量數據處理的問題有非常快捷的數組處理函數。 但是pandas最擅長的領域還是在處理表格型二維以上不同數據類型數據。 基本導 ...
  • 從今天開始,將會逐步介紹關於DUbbo的有關知識。首先先簡單介紹一下DUbbo的整體概述。 概述 Dubbo是SOA(面向服務架構)服務治理方案的核心框架。用於分散式調用,其重點在於分散式的治理。 簡單的來說,可以把它分為四個角色。服務提供方(Provider)、服務消費方(Consumer)、註冊 ...
  • 在上一篇文章 Dubbo之服務暴露分析 中介紹了當遠程暴露時,如果有註冊中心,需要在服務暴露後再將服務註冊到註冊中心。該篇將介紹該功能的有關步驟。 註冊的起點 在 方法包含了服務導出,註冊,以及數據訂閱等邏輯。其中服務註冊先調用 方法。 可以看出,服務註冊主要包括兩部分, 獲取註冊中心實例 和 向註 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...