① 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 方法,其他的拓展框架流程也會明白。只不過關聯的知識點太多了,還是需要時間的積累才能一點一點的搞懂。
如果本篇有描述不清,或者描述有誤的地方,還望在下方留言,大家一起交流,一起學習,一起進步~
② Dubbo簡介
Dubbo是Alibaba開源的分布式服務框架,它按照分層的方式來架構,使用這種方式可以使各層解耦。
Dubbo在調用遠程的服務的時候再本地有一個介面,就想調用本地方法一樣去調用,底層實現好參數傳輸和遠程服務運行結果傳回之後的返回。
Dubbo的特點:
(1)它主要使用高效的網路框架和序列化框架,讓分布式服務之間調用效率更高。
(2)採用注冊中心管理眾多的服務介面地址,當你想調用服務的時候只需要跟注冊中心詢問即可,不像使用WebService一樣每個服務都得記錄好介面調用方式。
(3)監控中心時實現服務方和調用方之間運行狀態的監控,還能控制服務的優先順序、許可權、權重、上下線等,讓整個龐大的分布式服務系統的維護和治理比較方便。
(4)高可用,如果有服務掛了,注冊中心就會從服務列表去掉該節點,客戶端會像注冊中心請求另一台可用的服務節點重新調用。同時注冊中心也能實現高可用(ZooKeeper)。
(5)負載均衡,採用軟負載均衡演算法實現對多個相同服務的節點的請求負載均衡。
Dubbo需要四大基本組件:Rigistry,Monitor,Provider,Consumer。
1、監控中心的配置文件-bbo.properties文件
(1)容器,監控中心是在jetty和spring環境下運行,依賴於注冊中心,日誌系統是log4j
bbo.container = log4j,spring,registry,jetty
(2)監控服務的名稱,監控系統對整個Dubbo服務系統來說也是一個服務
bbo.application.name = simple-monitor
(3)服務的所有者,這是Dubbbo的服務的功能,可以指定服務的負責人
bbo.application.owner = coselding
(4)注冊中心的地址,配置後監控中心就能通過注冊中心獲取當前可用的服務列表及其狀態,在頁面向你匯報Dubbo中的服務運行情況。
bbo.registr.address = multicast://{ip}:{port} //廣播
bbo.registr.address = zookeeper://{ip}:{port} //zookeper
bbo.registr.address = redis://{ip}:{port} //redis
bbo.registr.address = bbo://{ip}:{port} //bbo
(5)bbo協議埠號
bbo.protocol.port = 7070
(6)jetty工作埠號
bbo.jetty.port = 8082
(7)工作目錄,用於存放監控中心的數據
bbo.jetty.directory = ${user.home}/monitor
(8)監控中心報表存放目錄
bbo.charts.directory=${bbo.jetty.directory}/charts
(9)監控中心數據資料目錄
bbo.statistics.directory=${user.home}/monitor/statistics
(10)監控中心日誌文件路徑
bbo.log4j.file=logs/bbo-monitor-simple.log
(11)監控中心日誌記錄級別
bbo.log4j.level=WARN
2、Dubbo提供負載均衡方式
(1)Random,隨機,按權重配置隨機概率,調用量越大分布越均勻,默認方式。
(2)RounRobin,輪詢,按權重設置輪詢比例,如果存在比較慢的機器容易在這台機器上請求阻塞較多。
(3)LeastActive,最少活躍調用數,不支持權重,只能根據自動識別的活躍數分配,不能靈活調配。
(4)ConsistenHash,一致性hash,對相同參數的請求路由到一個服務提供者上,如果有類似灰度發布需求可採用。
3、Dubbo過濾器
Dubbo初始化過程載入ClassPath下的META-INF/bbo/internal/,META-INF/bbo/,META-INF/services/三個路徑下的com.alibaba.bbo.rpc.Filter文件。文件內容:
Name = FullClassName,這些類必須實現Filter介面。
自定義Filter類:
配置文件在配置過濾器,consumer.xml中:
Dubbo對過濾器的載入過程:
先載入三個路徑下的com.alibaba.bbo.rpc.Filter文件裡面的鍵值對,key為過濾器名稱,value為過濾器的類的全限定名(這個類必須實現Dubbo中的Filter介面)。
自定義的類中@Active註解是過濾器設定的全局基本屬性。
Spring在載入consumer.xml文件時,通過 <bbo:consumer filter="xxx" id = "xxx" retrries = "0">這個配置指定消費者端要載入的過濾器,通過filter屬性指定過濾器名稱。
@Activate註解-自動激活,group屬性是表示匹配了對應的角色才被載入,value表示表明過濾條件,不寫則表示所有條件都會被載入,寫了則只有bbo URL中包含該參數名且參數值不為空才被載入,這個參數會以bbo協議的一個參數K-V對傳到Provider。
4、Dubbo的Provider配置
5、Dubbo的Consumer配置
1、Dubbo是什麼?
Dubbo是阿里巴巴開源的基於Java的高性能RPC分布式框架。
2、為什麼使用Dubbo?
很多公司都在使用,經過很多線上的考驗,內部使用了Netty,Zookeeper,保證了高性能可用性。
使用Dubbo可以將核心業務抽取出來,作為獨立的服務,逐漸形成穩定的服務中心,可以提高業務復用靈活性擴展,使前端應用能快速的響應對邊的市場需求。分布式架構可以承受更大規模的並發流量。
Dubbo的服務治理圖:
3、Dubbo和Spring Cloud的區別
兩個沒有關聯,但是非要說區別,有如下幾點:
(1)通信方式不同,Dubbo使用RPC通信,Spring Cloud使用HTTP Restful方式
(2)組成部分不同
4、Dubbo支持的協議
bbo:// (推薦);rmi:// ;hessian:// ;http:// ;webservice:// ;thrift:// ;memcached:// ;redis:// ;rest:// 。
5、Dubbo需要容器嗎?
不需要,如果硬要容器的話,會增加復雜性,同時也浪費資源。
6、Dubbo內置的服務容器
Spring Container;Jetty Container;Log4j Container。
7、Dubbo中節點角色
Register,Monitor,Provider,Consumer,Container(服務運行的容器)。
8、Dubbo的服務注冊和發現的流程圖
9、Dubbo的注冊中心
默認使用Zookeper作為注冊中心,還有Redis,Multicast,bbo注冊中心。
10、Dubbo的配置方式
Spring配置方式和Java API配置方式
11、Dubbo的核心配置
(1)bbo:service 服務配置
(2)bbo:referece 引用配置
(3)bbo:protocol 協議配置
(4)bbo:application 應用配置
(5)bbo:registry 注冊中心配置
(6)bbo:monitor 監控中心配置
(7)bbo:provider 提供方配置
(8)bbo:consumer 消費方配置
(9)bbo:method 方法配置
(10)bbo:argument 參數配置
12、在Provider 節點上可以配置Consumer端的屬性有哪些?
(1)timeout:方法調用超時
(2)retries:失敗重試次數,默認是2次
(3)loadbalance:負載均衡演算法,默認隨機
(4)actives消費者端,最大並發調用控制
13、Dubbo啟動時如果依賴的服務不可用會怎樣
Dubbo預設會在啟動時檢查依賴的服務是否可用,不可用時會拋出異常,阻止Spring初始化完成。默認check ="true"。
14、Dubbo序列化框架
推薦使用Hessian序列化,還有Dubbo,Fastjson,Java自帶序列化。
15、Dubbo的通信框架
默認使用Netty框架,另外也提供了Mina,Grizzly。
16、Dubbo集群容錯方案
(1)Failover Cluster,失敗自動切換,自動重試其他伺服器。
(2)Failfast Cluster,快速失敗,立即報錯,只發起一次調用。
(3)Failsafe Cluster,失敗安全,出現異常時,直接忽略。
(4)Failback Cluster,失敗自動恢復,記錄失敗請求,定時重發。
(5)Forking Cluster,並行調用多個伺服器,只要一個返回成功即可。
(6)Broadcast Cluster,廣播逐個調用所有提供者,任意一個報錯則報錯。
17、Dubbo的負載均衡策略
(1)Random LoadBalance,隨機,按權重設置隨機概率,默認。
(2)RoundRobin LoadBalace,輪詢,按公約後的權重設置輪訓比例。
(3)LeastActive LoadBalace,最少活躍調用數,相同活躍數的隨機。
(4)ConsistenHash LoadBalance,一致性hash,相同參數的請求總是發到用一個伺服器。
18、指定某一個服務
可以配置環境點對點直連,繞過注冊中心,將以服務介面為單位,忽略注冊中心的提供者列表。
<bbo:reference interface="com.weidian.bbo.IMyDemo" version="1.0" id="myDemo" url="bbo://127.0.0.1:20880/"></bbo:reference>
19、Dubbo多協議
Dubbo允許配置多協議,在不同伺服器上支持不同協議,或者同一服務支持多種協議。
20、當一個服務有多種實現時怎麼做?
當一個介面有多種是現實,可以用group屬性來分組,服務提供方和消費方都指定同一個group即可。
21、兼容舊版本
使用版本號過度,多個不同版本的服務注冊到注冊中心,版本號不同的服務相互間不引用。
22、Dubbo可以緩存嗎?
Dubbo提供聲明式緩存,用於加速熱門數據的訪問速度,以減少用戶加緩存的工作量。
23、Dubbo服務之間的調用時阻塞的嗎?
默認是同步等待結果阻塞的,支持非同步調用。Dubbo是基於NIO的非阻塞實現並行調用的,客戶端不需要啟動多線程即可完成並行調用多個遠程服務,相對多線程開銷較小,非同步調用會返回一個Future對象。
24、Dubbo不支持分布式事務
25、Dubbo必須依賴的包
Dubbo必須依賴JDK,其他為可選。
26、Dubbo使用過程中的問題
Dubbo的設計目的是為了滿足高並發小數據量的rpc請求,在大數據量下性能表現不是很好,建議使用rmi或http協議。
27、Dubbo的管理控制台的作用
路由規則,動態配置,服務降級,訪問控制,權重調整,負載均衡。
28、Spring boot整合Dubbo
(1)添加依賴
<!-- https://mvnrepository.com/artifact/com.alibaba.boot/bbo-spring-boot-project -->
<dependency>
<groupId>com.alibaba.boot</groupId>
<artifactId>bbo-spring-boot-starter</artifactId>
<version>0.1.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.101tec/zkclient -->
<dependency>
<groupId>com.101tec</groupId>
<artifactId>zkclient</artifactId>
<version>0.10</version>
</dependency>
(2)配置bbo
## Dubbo 服務提供者配置
spring.bbo.application.name=provider
spring.bbo.registry.address=zookeeper://127.0.0.1:2181
spring.bbo.protocol.name=bbo
spring.bbo.protocol.port=20880
spring.bbo.scan=org.spring.springboot.bbo
## Dubbo 服務消費者配置
spring.bbo.application.name=consumer
spring.bbo.registry.address=zookeeper://127.0.0.1:2181
spring.bbo.scan=org.spring.springboot.bbo
③ bbo version: 2.6.0, current host: 192.168.245.1
您可以通過修改橡大緩Dubbo配置文件來設置Dubbo應用的IP地址。Dubbo應用的IP地址可以通過bbo.registry.address配置項來指定。例如,您可以在Dubbo配置文件中添加以下配置項:
bbo.registry.address = zookeeper://192.168.245.1:2181
這里的IP地址為您希望Dubbo應用使用的IP地址。這個配置仿困項指定了Dubbo應用使用Zookeeper作為注冊中心,並將Zookeeper的地址設置為192.168.245.1:2181。您也可以使用其他類型的注冊中心,例如Redis、Consul等,具體配置方梁模式可以參考Dubbo文檔。
④ 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 兩個實現,它們是在條件路由的基礎上添加了動態變更路由規則的能力,同時區分了服務級別和服務實例級別的配置。