導航:首頁 > 源碼編譯 > dubbo源碼設置

dubbo源碼設置

發布時間:2022-11-20 05:24:27

A. 如何更好地學習bbo源代碼

1、Dubbo與Spring的整合 Dubbo在使用上可以做到非常簡單,不管是Provider還是Consumer都可以通過Spring的配置文件進行配置,配置完之後,就可以像使用 spring bean一樣進行服務暴露和調用了,完全看不到bbo api的存在。這是因為bbo使用了spring提供的可擴展Schema自定義配置支持。在spring配置文件中,可以像、這樣進行配置。 META-INF下的spring.handlers文件中指定了bbo的xml解析類:DubboNamespaceHandler。像前面的被解 析成ServiceConfig,被解析成ReferenceConfig等等。 2、jdk spi擴展 由於Dubbo是開源框架,必須要提供很多的可擴展點。Dubbo是通過擴展jdk spi機制來實現可擴展的。具體來說,就是在META-INF目錄下,放置文件名為介面全稱,文件中為key、value鍵值對,value為具體實現類 的全類名,key為標志值。由於bbo使用了url匯流排的設計,即很多參數通過URL對象來傳遞,在實際中,具體要用到哪個值,可以通過url中的參 數值來指定。 Dubbo對spi的擴展是通過ExtensionLoader來實現的,查看ExtensionLoader的源碼,可以看到Dubbo對jdk spi做了三個方面的擴展:
(1)jdk spi僅僅通過介面類名獲取所有實現,而ExtensionLoader則通過介面類名和key值獲取一個實現;
(2)Adaptive實現,就是生成一個代理類,這樣就可以根據實際調用時的一些參數動態決定要調用的類了。
(3)自動包裝實現,這種實現的類一般是自動激活的,常用於包裝類,比如Protocol的兩個實現類:ProtocolFilterWrapper、ProtocolListenerWrapper。 3、url匯流排設計 Dubbo為了使得各層解耦,採用了url匯流排的設計。我們通常的設計會把層與層之間的交互參數做成Model,這樣層與層之間溝通成本比較大,擴展起來也比較麻煩。因此,Dubbo把各層之間的通信都採用url的形式。比如,注冊中心啟動時,參數的url為: registry://0.0.0.0:9090?codec=registry&transporter=netty 這就表示當前是注冊中心,綁定到所有ip,埠是9090,解析器類型是registry,使用的底層網路通信框架是netty。
二、Dubbo啟動過程
Dubbo分為注冊中心、服務提供者(provider)、服務消費者(consumer)三個部分。 1、注冊中心啟動過程 注冊中心的啟動過程,主要看兩個類:RegistrySynchronizer、RegistryReceiver,兩個類的初始化方法都是start。 RegistrySynchronizer的start方法:
(1)把所有配置信息load到內存;
(2)把當前注冊中心信息保存到資料庫;
(3)啟動5個定時器。 5個定時器的功能是: (1)AutoRedirectTask,自動重定向定時器。默認1小時運行1次。如果當前注冊中心的連接數高於平均值的1.2倍,則將多出來的連接數重定向到其他注冊中心上,以達到注冊中心集群的連接數均衡。 (2)DirtyCheckTask,臟數據檢查定時器。作用是:分別檢查緩存provider、資料庫provider、緩存consumer、資料庫 consumer的數據,清除臟數據;清理不存活的provider和consumer數據;對於緩存中的存在的provider或consumer而數 據庫不存在,重新注冊和訂閱。 (3)ChangedClearTask,changes變更表的定時清理任務。作用是讀取changes表,清除過期數據。 (4)AlivedCheckTask,注冊中心存活狀態定時檢查,會定時更新registries表的expire欄位,用以判斷注冊中心的存活狀態。如果有新的注冊中心,發送同步消息,將當前所有注冊中心的地址通知到所有客戶端。 (5)ChangedCheckTask,變更檢查定時器。檢查changes表的變更,檢查類型包括:參數覆蓋變更、路由變更、服務消費者變更、權重變更、負載均衡變更。

B. bbo之ProtocolFilterWrapper

ProtocolFilterWrapper是bbo-rpc模塊中,bbo-rpc-api的一個核心類,其中核心方法buildInvokerChain,顧名思義構建invoker鏈。bbo源碼看到這塊時,理解起來有點費勁兒,特意做記錄,方便日後查看。

1、首先,我們先看一下方法中的3個核心變數,invoker、filter、next

2、可以清晰看到源碼中,invoker採用了匿名類 ProtocolFilterWrapper$1 實現,我們來看一下生成的匿名內部類結構

重點關注紅框內的構造方法,以及invoke方法的實現。

OK,下面我們對buildInvokerChain的具體邏輯做分析;我們先對方法邏輯做一個抽象,首先是原始方法

藉助前面我們提到的匿名類,我們做一下抽象,下面是抽象後的方法:

這樣看起來就簡單多了,實際上這塊邏輯就是把url里拿到的filter包裝成Invoker,串起來;下面我們了解一下bbo是如何把Invoker 串起來的,為了方便理解,這里做圖解。

假設現在有A、B、C、D、E 5個filter,初始Invoker順序如下:

最後 return last 5,這樣就把所有filter串起來了,最終的Invoker chain順序是 last 5 -> last 4 -> last 3 -> last 2 -> last 1(即 invoker 本身)。

C. Dubbo——路由機制(下)

在 Dubbo——路由機制(上) ,介紹了 Router 介面的基本功能以及 RouterChain 載入多個 Router 的實現,之後介紹了 ConditionRouter 這個類對條件路由規則的處理邏輯以及 ScriptRouter 這個類對腳本路由規則的處理邏輯。本文繼續介紹剩餘的三個 Router 介面實現類。

FileRouterFactory 是 ScriptRouterFactory 的裝飾器,其擴展名為 file,FileRouterFactory 在 ScriptRouterFactory 基礎上增加了讀取文件的能力。可以將 ScriptRouter 使用的路由規則保存到文件中,然後在 URL 中指定文件路徑,FileRouterFactory 從中解析到該腳本文件的路徑並進行讀取,調用 ScriptRouterFactory 去創建相應的 ScriptRouter 對象。

下面來看 FileRouterFactory 對 getRouter() 方法的具體實現,其中完成了 file 協議的 URL 到 script 協議 URL 的轉換,如下是一個轉換示例,首先會將 file:// 協議轉換成 script:// 協議,然後會添加 type 參數和 rule 參數,其中 type 參數值根據文件後綴名確定,該示例為 js,rule 參數值為文件內容。

可以再結合接下來這個示例分析 getRouter() 方法的具體實現:

TagRouterFactory 作為 RouterFactory 介面的擴展實現,其擴展名為 tag。但是需要注意的是,TagRouterFactory 與之前介紹的 ConditionRouterFactory、ScriptRouterFactory 的不同之處在於,它是通過繼承 CacheableRouterFactory 這個抽象類,間接實現了 RouterFactory 介面。

CacheableRouterFactory 抽象類中維護了一個 ConcurrentMap 集合(routerMap 欄位)用來緩存 Router,其中的 Key 是 ServiceKey。在 CacheableRouterFactory 的 getRouter() 方法中,會優先根據 URL 的 ServiceKey 查詢 routerMap 集合,查詢失敗之後會調用 createRouter() 抽象方法來創建相應的 Router 對象。在 TagRouterFactory.createRouter() 方法中,創建的自然就是 TagRouter 對象了。

通過 TagRouter,可以將某一個或多個 Provider 劃分到同一分組,約束流量只在指定分組中流轉,這樣就可以輕松達到流量隔離的目的,從而支持灰度發布等場景。

目前,Dubbo 提供了動態和靜態兩種方式給 Provider 打標簽,其中動態方式就是通過服務治理平台動態下發標簽,靜態方式就是在 XML 等靜態配置中打標簽。Consumer 端可以在 RpcContext 的 attachment 中添加 request.tag 附加屬性,注意保存在 attachment 中的值將會在一次完整的遠程調用中持續傳遞,我們只需要在起始調用時進行設置,就可以達到標簽的持續傳遞。

了解了 Tag 的基本概念和功能之後,再簡單介紹一個 Tag 的使用示例。

在實際的開發測試中,一個完整的請求會涉及非常多的 Provider,分屬不同團隊進行維護,這些團隊每天都會處理不同的需求,並在其負責的 Provider 服務中進行修改,如果所有團隊都使用一套測試環境,那麼測試環境就會變得很不穩定。如下圖所示,4 個 Provider 分屬不同的團隊管理,Provider 2 和 Provider 4 在測試環境測試,部署了有 Bug 的版本,這樣就會導致整個測試環境無法正常處理請求,在這樣一個不穩定的測試環境中排查 Bug 是非常困難的,因為可能排查到最後,發現是別人的 Bug。

為了解決上述問題,我們可以針對每個需求分別獨立出一套測試環境,但是這個方案會佔用大量機器,前期的搭建成本以及後續的維護成本也都非常高。

下面是一個通過 Tag 方式實現環境隔離的架構圖,其中,需求 1 對 Provider 2 的請求會全部落到有需求 1 標簽的 Provider 上,其他 Provider 使用穩定測試環境中的 Provider;需求 2 對 Provider 4 的請求會全部落到有需求 2 標簽的 Provider 4 上,其他 Provider 使用穩定測試環境中的 Provider。

在一些特殊場景中,會有 Tag 降級的場景,比如找不到對應 Tag 的 Provider,會按照一定的規則進行降級。如果在 Provider 集群中不存在與請求 Tag 對應的 Provider 節點,則默認將降級請求 Tag 為空的 Provider;如果希望在找不到匹配 Tag 的 Provider 節點時拋出異常的話,我們需設置 request.tag.force = true。

如果請求中的 request.tag 未設置,只會匹配 Tag 為空的 Provider,也就是說即使集群中存在可用的服務,若 Tag 不匹配也就無法調用。一句話總結,攜帶 Tag 的請求可以降級訪問到無 Tag 的 Provider,但不攜帶 Tag 的請求永遠無法訪問到帶有 Tag 的 Provider。

下面再來看 TagRouter 的具體實現。在 TagRouter 中持有一個 TagRouterRule 對象的引用,在 TagRouterRule 中維護了一個 Tag 集合,而在每個 Tag 對象中又都維護了一個 Tag 的名稱,以及 Tag 綁定的網路地址集合,如下圖所示:

另外,在 TagRouterRule 中還維護了 addressToTagnames、tagnameToAddresses 兩個集合(都是 Map<String, List<String>> 類型),分別記錄了 Tag 名稱到各個 address 的映射以及 address 到 Tag 名稱的映射。在 TagRouterRule 的 init() 方法中,會根據 tags 集合初始化這兩個集合。

了解了 TagRouterRule 的基本構造之後,我們繼續來看 TagRouter 構造 TagRouterRule 的過程。TagRouter 除了實現了 Router 介面之外,還實現了 ConfigurationListener 介面,如下圖所示:

ConfigurationListener 用於監聽配置的變化,其中就包括 TagRouterRule 配置的變更。當我們通過動態更新 TagRouterRule 配置的時候,就會觸發 ConfigurationListener 介面的 process() 方法,TagRouter 對 process() 方法的實現如下:

我們可以看到,如果是刪除配置的操作,則直接將 tagRouterRule 設置為 null,如果是修改或新增配置,則通過 TagRuleParser 解析傳入的配置,得到對應的 TagRouterRule 對象。TagRuleParser 可以解析 yaml 格式的 TagRouterRule 配置,下面是一個配置示例:

經過 TagRuleParser 解析得到的 TagRouterRule 結構,如下所示:

除了上圖展示的幾個集合欄位,TagRouterRule 還從 AbstractRouterRule 抽象類繼承了一些控制欄位,後面介紹的 ConditionRouterRule 也繼承了 AbstractRouterRule。

AbstractRouterRule 中核心欄位的具體含義大致可總結為如下:

我們可以看到,AbstractRouterRule 中的核心欄位與前面的示例配置是一一對應的。

我們知道,Router 最終目的是要過濾符合條件的 Invoker 對象,下面我們一起來看 TagRouter 是如何使用 TagRouterRule 路由邏輯進行 Invoker 過濾的,大致步驟如下:

上述流程的具體實現是在 TagRouter.route() 方法中,如下所示:

除了之前介紹的 TagRouterFactory 繼承了 CacheableRouterFactory 之外,ServiceRouterFactory 也繼承 CachabelRouterFactory,具有了緩存的能力,具體繼承關系如下圖所示:

ServiceRouterFactory 創建的 Router 實現是 ServiceRouter,與 ServiceRouter 類似的是 AppRouter,兩者都繼承了 ListenableRouter 抽象類(雖然 ListenableRouter 是個抽象類,但是沒有抽象方法留給子類實現),繼承關系如下圖所示:

ListenableRouter 在 ConditionRouter 基礎上添加了動態配置的能力,ListenableRouter 的 process() 方法與 TagRouter 中的 process() 方法類似,對於 ConfigChangedEvent.DELETE 事件,直接清空 ListenableRouter 中維護的 ConditionRouterRule 和 ConditionRouter 集合的引用;對於 ADDED、UPDATED 事件,則通過 ConditionRuleParser 解析事件內容,得到相應的 ConditionRouterRule 對象和 ConditionRouter 集合。這里的 ConditionRuleParser 同樣是以 yaml 文件的格式解析 ConditionRouterRule 的相關配置。ConditionRouterRule 中維護了一個 conditions 集合(List<String> 類型),記錄了多個 Condition 路由規則,對應生成多個 ConditionRouter 對象。

整個解析 ConditionRouterRule 的過程,與前文介紹的解析 TagRouterRule 的流程類似。

在 ListenableRouter 的 route() 方法中,會遍歷全部 ConditionRouter 過濾出符合全部路由條件的 Invoker 集合,具體實現如下:

ServiceRouter 和 AppRouter 都是簡單地繼承了 ListenableRouter 抽象類,且沒有覆蓋 ListenableRouter 的任何方法,兩者只有以下兩點區別。

本文我們是緊接 Dubbo——路由機制(上) 的內容,繼續介紹了剩餘 Router 介面實現的內容。

我們介紹了基於文件的 FileRouter 實現,其底層會依賴之前介紹的 ScriptRouter;接下來又講解了基於 Tag 的測試環境隔離方案,以及如何基於 TagRouter 實現該方案,同時深入分析了 TagRouter 的核心實現;最後我們還介紹了 ListenableRouter 抽象類以及 ServerRouter 和 AppRouter 兩個實現,它們是在條件路由的基礎上添加了動態變更路由規則的能力,同時區分了服務級別和服務實例級別的配置。

D. Dubbo(一)——Dubbo 集成於 Spring 的原理

最近一直在看bbo的源碼部分。在閱讀的時候,需要有一個入手點,才能一點一點的進行下去。自己在研究的時候,發現思緒比較亂,於是就以 芋道源碼 為基礎,一點一點的啃食。芋道源碼是直接從bbo的配置和一些核心的API開始講起,是從bbo已經啟動的過程作為開始節點,而這些核心 API 與 Spring 的之間的關系被省略了,這些東西對我來說屬於前置的知識點,所以花了比較長的時間又從 Dubbo 的核心 API 倒著往前看。

在閱讀 Dubbo 時,發現前置知識越來越多,如:Spring 的 refresh 中的一些核心點,Spring 中 bean 的生命周期,BeanFactory 與 FactoryBean 的區別等。所以這些前置知識花了特別多的時間去補。所幸,雖然補前置知識雖然時間長,但是性價比還是可以的。Dubbo 是依賴於Spring 的上下文環境的框架,其他依賴於 Spring 的框架也是相同的道理。Spring 的一些對外的擴展點,讀過之後也會心中有數。

1、本篇主要是描述了 Dubbo 在 Spring 創建上下文的時候,是如何從創建,到能完整提供一個RPC調用能力的一些相關點。
2、由於源碼比較多,直接貼斷點也太過臃腫,所以僅僅貼一些關鍵點來概括整個流程。
3、本文是依賴於前面的 bbo 項目進行斷點分析,項目結構可以參照這里。項目中 bbo 的配置方式是 xml 文件,所以本篇主要說 xml 配置方式。其他方式道理相同,並不是問題的關鍵點。

4、項目啟動的是 bbo-user 服務,所以 UserService 為 bbo:service,OrderService 為 bbo:reference。

下圖為Spring 啟動時是如何載入 Dubbo 的,其中省略了大量過程,只保留了一些關鍵節點,省略的部分可以略微腦補一下。

整個流程的入口是 Spring 的 refresh 方法。每個方法都有比較深的調用棧。與 Dubbo 有關的入口是 refresh 中的 方法

這個方法是執行 beanFactory 的一些後處理操作,其核心流程為在Spring容器中找出實現了BeanFactoryPostProcessor介面的processor並執行。Spring容器會委託給的方法執行。
是比較核心的類,在這里我們關注一下這個類。它的作用是對項目中配置的類進行處理。具體處理可以分為幾步:

在載入類信息時,spring 會去用各種方式掃到注冊的 bean 信息。我們在 spring 中注冊的 bean,逃不出這個方法的掃描方式。 核心方法是:

掃描之後,會將掃描到的 bean 注冊到 beanDefinitionMap 中

首先是此處 org.springframework.beans.factory.xml.#parseBeanDefinitions,可以看出方法會以配置文件根節點起,遍歷所有子節點。

其次是這里 org.springframework.beans.factory.xml.BeanDefinitionParserDelegate#parseCustomElement(org.w3c.dom.Element, org.springframework.beans.factory.config.BeanDefinition), 此方法會通過解析出來的節點,獲取對應的 Spring 的 namespaceUri ,進而獲取對應的配置文件處理器。
此處 ele 參數實際值為 <bbo:service ... />,namespaceUri 為 http://code.alibabatech.com/schema/bbo

我們看一下 resolve 方法中的細節。因為這個方法內部才是 Dubbo 依賴於 Spring 的關鍵點。

此處的 NamespaceHandler 為 DubboNamespaceHandler,再創建結束之後,進行 init 初始化。

可以看到,DubboNamespaceHandler 在初始化的時候,會創建所有 bbo 標簽對應的Config 類的 DubboBeanDefinitionParser。並將 DubboBeanDefinitionParser 和 對應的 bbo 標簽類注冊到 NamespaceHandlerSupport 的 parsers 中。

最後,會在 com.alibaba.bbo.config.spring.schema.DubboBeanDefinitionParser#parse(org.w3c.dom.Element, org.springframework.beans.factory.xml.ParserContext, java.lang.Class<?>, boolean) 方法中進行處理

Dubbo 服務比較特殊,beanDefinition 跟普通的 bean 不太一樣。在向 beanDefinitionMap 注冊時,普通的 beanDefinition 的 beanName 與 beanClass 是對應的;而 bbo 服務的 beanDefinition 的 beanName 是bbo 服務的名稱,beanClass 為 bbo 對應的 Bean。

普通的 beanDefinition:

bbo 引用的服務的 beanDefinition:

這一步的核心流程是從 beanFactory 中獲取所有的 ApplicationListener,然後注冊到監聽器集合中。它的關鍵點其實是 ServiceBean。因為 ServiceBean 是 ApplicationListener 的實現。

所以 beanFactory 中 ServiceBean 也會被注冊到監聽器集合中。項目中的 ServiceBean 的 beanClass 實際是 UserService。

這一步的核心點,主要是創建剩餘的各類對象,並將其保存到 singletonObjects 中。其中關聯的前置知識為 Spring 中 bean 的生命周期 。它的核心方法是:
org.springframework.beans.factory.support.#doCreateBean

它的具體流程為:

ps:此處並不是只有這一步才會跟 bean 生命周期相關,bean 生命周期貫穿在 refresh 的很多流程中,只要執行doGetBean 方法,都會走這個流程。此處僅僅借樓關聯一下。

這一步的核心點,是通知所有的監聽器上下文刷新結束的事件。在這一步執行時,會通知到 ServiceBean。

此處暴露的是 UserService。

Dubbo 的啟動條件是完全依賴於 Spring 的啟動流程,Spring 的啟動流程中核心的點是 refresh 方法。所以只要搞懂 refresh 方法,其他的拓展框架流程也會明白。只不過關聯的知識點太多了,還是需要時間的積累才能一點一點的搞懂。
如果本篇有描述不清,或者描述有誤的地方,還望在下方留言,大家一起交流,一起學習,一起進步~

E. Dubbo之SPI實現原理詳解

 SPI全稱為Service Provider Interface,是一種服務提供機制,比如在現實中我們經常會有這種場景,就是對於一個規范定義方而言(可以理解為一個或多個介面),具體的服務實現方是不可知的(可以理解為對這些介面的實現類),那麼在定義這些規范的時候,就需要規范定義方能夠通過一定的方式來獲取到這些服務提供方具體提供的是哪些服務,而SPI就是進行這種定義的。

說明:



Dubbo 的擴展點載入是基於JDK 標準的 SPI 擴展點發現機制增強而來的,Dubbo 改進了 JDK 標準的 SPI 的以下問題:


bbo對於SPI的實現主要是在ExtensionLoader這個類中,這個類主要有三個方法:


如下是getExtension()方法的源碼:



createExtension()方法的源碼:

在createExtension()方法中,其主要做了三件事:

關於wrapper對象,這里需要說明的是,其主要作用是為目標對象實現AOP。wrapper對象有兩個特點:



getExtensionClasses()方法的源碼



loadDirectory()方法的源碼:



loadClass()方法的源碼

loadClass()方法主要作用是對子類進行劃分,這里主要劃分成了三部分:

總結而言,getExtension()方法主要是獲取指定名稱對應的子類。在獲取過程中,首先會從緩存中獲取是否已經載入過該子類,如果沒載入過則通過定義文件載入,並且使用獲取到的wrapper對象封裝目標對象返回。




getAdaptiveExtension()方法源碼



F. Dubbo之服務導出源碼分析

要了解服務導出做了什麼,需要了解導出的目的是什麼?
bbo是一款面向介面代理的高性能RPC調用,說白了就是提供遠程服務。

服務導出需要做的:簡單說就是根據服務參數、服務協議構建服務URL,注冊到注冊中心,並啟動Server。其中服務參數可以動態配置也需要監聽。

1、onApplicationEvent
org.apache.bbo.config.spring.ServiceBean#onApplicationEvent
發布ContextRefreshedEvent,進行導出服務

2、export
org.apache.bbo.config.spring.ServiceBean#export
調用父類ServiceConfig導出,之後發布一個ServiceBeanExportedEvent

②、shouldExport檢查服務是否需要導出
org.apache.bbo.config.ServiceConfig#shouldExport

org.apache.bbo.config.ServiceConfig#checkAndUpdateSubConfigs

1、completeCompoundConfigs
ServiceConfig中的某些屬性如果是空的,那麼就從ProviderConfig、MoleConfig、ApplicationConfig中獲取
①、如果配置了ProviderConfig provider,如果application、mole、registries、monitor、protocols、configCenter這些屬性為空的情況下,可以從provider中獲取信息並賦值

③、如果ApplicationConfig application不為空,可以為registries、monitor為空的賦值
2、startConfigCenter
從配置中心獲取配置,包括應用配置和全局配置

SystemConfiguration:是系統環境變數,可以在服務啟動時通過-D指定參數
AbstractConfig:是通過@Service註解配置的參數
PropertiesConfiguration:是bbo.properties文件配置的
AppExternalConfiguration和ExternalConfiguration:是在配置中心Dubbo-Admin中配置的,AppXxx是應用級別
配置優先順序是:SystemConfiguration -> AppExternalConfiguration -> ExternalConfiguration -> AbstractConfig -> PropertiesConfiguration
如果放到第二個位置優先順序是:SystemConfiguration -->AbstractConfig -> AppExternalConfiguration -> ExternalConfiguration -> -> PropertiesConfiguration
c、根據setXX()方法和setParameters()方法進行參數值覆蓋

②、prepareEnvironment
管理台上的動態配置中心,如果是zookeeper,獲取的就是/bbo/config/bbo/bbo.properties節點中的內容。如果是應用級別的則獲取的就是/bbo/config/bbo-demo-consumer-application/bbo.properties節點中的內容(bbo-demo-consumer-application應用名,這里是以bbo-demo為例)

4、checkProtocol 從配置中心設置Protocol配置

10、Local、Stub、Mock
local和stub一樣,不建議使用了。如果Local存根為true,則存根類為interfaceName + "Local"。如果Stub本地存根為true,則存根類為interfaceName + "Stub"

org.apache.bbo.config.ServiceConfig#doExport

unexported表示:當前服務已經被取消了,就不需要再導出了
exported表示:已經導出了,就不再重復導出了
1、doExportUrls
org.apache.bbo.config.ServiceConfig#doExportUrls

②、遍歷protocols協議
pathKey = group/應用名/path服務名:version
例如:mygroup/bbo-demo/org.apache.bbo.demo.DemoService:1.0.1
ProviderMethodModel表示某一個方法、方法名所屬的服務的,包含實現類,介面,以及介面中的各個方法

ApplicationModel表示應用中有哪些服務提供者和引用了哪些服務
每種協議都會導出一個單獨的服務,並注冊到各個注冊中心
2、doExportUrlsFor1Protocol
org.apache.bbo.config.ServiceConfig#doExportUrlsFor1Protocol

③、methods方法參數處理

⑦、scope導出方式(scope=null進行遠程導出)

生成的注冊服務URL是在registryURL和export參數是服務url的拼裝
b、也表示服務提供者,包括了Invoker和服務的配置。是對Invoker的包裹
c、protocol.export(wrapperInvoker)這是導出的核心,使用了SPI。
使用特定的協議來對服務進行導出,這里的協議是registry,導出成功後得到一個Exporter
由於注冊地址也是服務,所以會先使用RegistryProtocol進行服務注冊(服務導出),然後注冊(服務導出)完了之後,再使用DubboProtocol進行導出
自適應SPI會獲取url
org.apache.bbo.config.invoker.#getUrl

⑩、當存在注冊中心時,是先使用Registy協議注冊服務,然後在使用Http協議導出服務。而沒有注冊中心時,是直接使用Http協議導出服務。根據服務url,講服務的元信息存入元數據中心MetadataReportService

RegistryProtocol被ProtocolFilterWrapper包裹,ProtocolFilterWrapper被ProtocolListenerWrapper包裹(SPI原理)

1、export
org.apache.bbo.rpc.protocol.ProtocolListenerWrapper#export
如果協議是REGISTRY_PROTOCOL(registry)則會直接走到下一個protocol實現類ProtocolFilterWrapper。
如果不是則會使用ListenerExporterWrapper處理

②、獲取providerUrl 服務提供者url

③、生成overrideSubscribeUrl,與監聽有關
在服務提供者url的基礎上,生成一個overrideSubscribeUrl,協議為provider://,增加參數category=configurators&check=false
overrideSubscribeUrl是用來對動態配置監聽的,需要監聽的服務和監聽的類型(configurators, 老版本)
一個overrideSubscribeUrl對應一個OverrideListener,用來監聽overrideSubscribeUrl變化事件

④、doLocalExport導出
此時服務協議是bbo,DubboProtocol被ProtocolFilterWrapper包裹,ProtocolFilterWrapper被ProtocolListenerWrapper包裹

1、export
org.apache.bbo.rpc.protocol.ProtocolListenerWrapper#export
返回結果被ListenerExporterWrapper包裹

2、export
org.apache.bbo.rpc.protocol.ProtocolFilterWrapper#export

EchoFilter、ClassLoaderFilter、GenericFilter、ContextFilter、TraceFilter、TimeoutFilter、MonitorFilter、ExceptionFilter
返回包裹了Filter的CallbackRegistrationInvoker
3、export
org.apache.bbo.rpc.protocol.bbo.DubboProtocol#export

②、register
org.apache.bbo.registry.support.FailbackRegistry#register

在服務導出過程中,需要對動態配置中心的數據進行監聽。如果發生更改,可以及時做出反映。
這里用到了Zookeeper的Watcher機制。
1、配置中心的路徑與動態配置路徑是不相同的,這里需要區分開來
配置中心的路徑是:
①、應用:/bbo/config/bbo/org.apache.bbo.demo.DemoService/bbo.properties節點的內容
②、全局:/bbo/config/bbo/bbo.properties節點的內容
2、對於動態配置的監聽有版本的差別
在2.7之前,只可以對單個服務進行監聽,不可以對應用監聽
/bbo/org.apache.bbo.demo.DemoService/configurators/* 只對路徑名字監聽,不監聽內容
在2.7之後,服務和應用都可以監聽
服務: /bbo/config/bbo/org.apache.bbo.demo.DemoService.configurators 節點的內容
應用: /bbo/config/bbo/bbo-demo-provider-application.configurators 節點的內容
3、監聽時機RegistryProtocol#export
org.apache.bbo.registry.integration.RegistryProtocol#export

G. 4. Dubbo-executes設置解析

限制某個介面、方法使用的業務線程數
源碼參見ExecuteLimitFilter, 利用信號量實現

參考:
https://blog.csdn.net/manzhen/article/details/75727860 : 文章中說的問題在2.6.1版本已修復

有個地方沒搞明白:
對介面設置 executes = "10", 是整個介面只能有10個線程? 還是每個方法最多10個,如果這樣,假設介面有5個方法,豈不是該介面最多50個線程?

H. bbo 中使用 ThreadLocal 隱患

在 Dubbo 中使用 ThreadLocal ,如果採用默認的設置,每次 Dubbo 調用結束,Dubbo 處理響應線程並不會被銷毀, 而是歸還到線程池中。而從 ThreadLocal 源碼可以看出,每次我們設置的值其實會存在位於 Thread 中 ThreadLocalMap 變數中。

這就導致,下次如果 Dubbo 處理響應恰好繼續使用到這個線程,該線程就能調用到上次響應中設置在 ThreadLocal 設置的值。這就引起內存泄露,可能還會導致業務上異常。其實並不止在 Dubbo 中,該案例還會發生在 web項目中,只要相關使用線程池的,都有可能發生。

bbo 默認採用單一長連接加線程池方式處理調用。

默認採取 Dispatcher=all 的分發策略,所有消息都派發到線程池,包括請求,響應,連接事件,斷開事件,心跳等。

線程池在預設配置為固定大小線程池,啟動時建立線程,不關閉,一直持有。

使用 Threadlocal,我們需要注意幾點:

調整線程池大小配置是

bbo.protocol.threads = 5000
調整線程池類型配置是
bbo.protocol.threadpool = cached
調整事件處理方式配置是
bbo.protocol.dispatcher = message

或者
<bbo:protocol name="bbo" dispatcher="all" threadpool="fixed" threads="100" />

I. 14. bbo源碼-集群容錯之MergeableCluster

在bbo官方的用戶手冊中,提到了使用 MergeableCluster 的場景--分組聚合:

功能示意圖如下:

定義菜單介面方式:

Provider暴露服務--一個服務屬於 group-hot ,一個服務屬於 group-cold

筆者測試時啟動了兩個Provider,所以總計有四個服務,bbo-monitor監控顯示如下:

Consumer調用服務:

幾個重要的配置說明:

com.alibaba.bbo.rpc.cluster.Merger 文件內容如下:

核心源碼在 MergeableClusterInvoker.java 中,源碼如下所示:

在條件分支 if ( merger.startsWith(".") ) {} 中,有一段邏輯: method = returnType.getMethod( merger, returnType ); ,即從bbo服務介面方法返回類型即 java.util.List 中查找merger配置的方法,例如 .addAll ,我們先看一下debug過程各變數的值:

bbo源碼中 method = returnType.getMethod( merger, returnType ); 調用 Method method = getMethod0(name, parameterTypes, true); ,再調用 Method res = privateGetMethodRecursive(name, parameterTypes, includeStaticMethods, interfaceCandidates); ,最後調用 searchMethods(privateGetDeclaredMethods(true), name, parameterTypes)) ,得到最後方法匹配的核心邏輯如下:

從searchMethods()源碼可知,方法匹配需要滿足幾個條件:

由上面的分析可知,如果要merger=".addAll"能夠正常工作,那麼只需要將bbo服務的返回類型改成 Collection 即可,例如:

如果 com.alibaba.bbo.rpc.cluster.Merger 文件集中方法無法滿足需求,需要自定義實現,那麼還是和bbo其他擴展實現一樣,依賴SPI。只需要一下幾步實現即可:

J. 【bbo源碼】5.配置信息解析-註解版

用於把bbo.properties讀到spring的environment中,

這個工作是由Spring的類來完成的.檢測到某個需要注冊的Bean上有@PropertySource註解,就會讀該文件的配置信息,弄到environment對象的MutablePropertySources對象中。

後期會把配置信息弄到bbo 配置類中.

該註解上還有@DubboComponentScan,@EnableDubboConfig,這兩個註解是bbo用註解與spring集成的核心了

該註解用@import導入了這個類

實現了ImportBeanDefinitionRegistrar介面,那麼spring在實例化的時候會調用重寫ImportBeanDefinitionRegistrar介面的registerBeanDefinitions方法,並且將用@Import導入的類的元數據包裝成importingClassMetadata對象。

其實就是為了獲取入口類AnnoBean上的@EnableDubboConfig註解里的multiple屬性配置的值,默認是true.

然後注冊了兩個DubboConfigConfiguration的內部類

通過讀Class對象注冊到ioc容器

類上有@EnableDubboConfigBindings,值為@EnableDubboConfigBinding數組
通過綁定,將有對應前綴的配置信息賦值到對應的配置類中

又用@Import導入DubboConfigBindingsRegistrar類,DubboConfigBindingsRegistrar這個類又實現了ImportBeanDefinitionRegistrar,EnvironmentAware介面

實現ImportBeanDefinitionRegistrar肯定是為了另外導入一些類,並且拿到導入的源類,獲取源類上配置的信息

實現EnvironmentAware是為了拿到spring的environment對象,因為 bbo.properties 已經被@PropertySource註解機制載入到了environment.MutablePropertySources中,在這里只對beanName的創建有作用。

registrar.registerBeanDefinitions :

注冊的過程中,需要從environment對象中拿bbo相關的配置,比如ApplicationConfig只拿
bbo.application.*相關的配置,然後創建ApplicationConfig的BeanDefinition.

如果 @EnableDubboConfigBinding配置的multiple為true(默認為false),並且在配置文件中配置了同樣前綴的屬性,如:

這樣會為同一種配置類型,生成兩個BD.beanName不同的配置Bean,名稱規則如下所示, #0表示的是'.'在配置的key中出現的位置

之後還會注冊一個BeanPostProcessor類型的類的beanDefinition,BeanPostProcessor類型 會在每一個Bean實例化的過程中,根據配置的前綴,從environment拿出所需的配置,根據beanName來處理beanName相同的這一個配置Bean,把配置信息綁定到配置類的屬性中。

.

利用 bboConfigBinder 對象來綁定前綴為bbo.application的配置信息到配置Bean中

這里bboConfigBinder對象是中的一個屬性,是在因為這個類實現了InitializingBean這個介面的afterPropertiesSet方法,bboConfigBinder對象就是在這里初始化的

最後用的DataBinder的api把一個MutablePropertyValues綁定到Bean的屬性

@import進來了DubboComponentScanRegistrar類

DubboComponentScanRegistrar又實現了ImportBeanDefinitionRegistrar介面,實現registerBeanDefinitions方法.

跟xml的邏輯一樣,同樣是

閱讀全文

與dubbo源碼設置相關的資料

熱點內容
北京文件夾加密多少錢 瀏覽:669
什麼是車鑒定app 瀏覽:64
戰地一私人伺服器怎麼買 瀏覽:497
陳天程序員 瀏覽:833
編譯原理如何運用到編程中 瀏覽:17
linux選擇資料庫 瀏覽:376
php兩個數組差集 瀏覽:978
迷你pdf閱讀器下載 瀏覽:433
做一個python小程序 瀏覽:655
pythonossystem和 瀏覽:645
win2008如何搭建ftp伺服器 瀏覽:53
安卓手機為什麼不翻牌 瀏覽:546
刪除pkpm及相關文件夾 瀏覽:481
房貸解壓銀行內部流程 瀏覽:734
安卓手機如何更改語音 瀏覽:601
android紅包實現 瀏覽:734
蘋果的nvme為什麼安卓不用 瀏覽:32
python輸入單詞統計個數 瀏覽:998
腳本軟體提取源碼 瀏覽:281
程序員能給自己的微信錢包刷錢么 瀏覽:73