大家好,我是小羽。
Dubbo 起源於阿里巴巴,對於我們做電商開發的人來說,基本是首選的技術,那么為何一個區區 soa 服務治理框架,會受到這么多人的青睞呢?
今天就跟着小羽一起看看這個微服務框架之一的 Dubbo 的詳細解讀吧。
前言
互聯網的不斷發展,網站應用的規模不斷擴大,常規的垂直應用架構已無法應對。
服務化的進一步發展,服務越來越多,服務之間的調用和依賴關系也越來越復雜,誕生了面向服務的架構體系(SOA),
也因此衍生出了一系列相應的技術,如對服務提供、服務調用、連接處理、通信協議、序列化方式、服務發現、服務路由、日志輸出等行為進行封裝的服務框架。
就這樣分布式系統的服務治理框架就出現了,Dubbo也就這樣產生了。
概念
Dubbo 是一款高性能、輕量級的開源 RPC
框架、提供服務自動注冊、自動發現等高效治理方案,可以和 Spring
框架無縫集成。
簡單的說,dubbo就是個分布式服務框架,在有分布式需要的時候可以使用 dubbo 的框架,使用 dubbo 的好處:
_1、_透明化的遠程方法調用
_2、_軟負載均衡及容錯機制
_3、_服務自動注冊與發現
_4、_提供了完善的服務接口管理與監控功能
架構圖
RPC
簡介
RPC 全稱為 remote procedure call,即遠程過程調用。比如兩台服務器 A 和 B,A 服務器上部署一個應用,B 服務器上部署一個應用,A 服務器上的應用想調用 B 服務器上的應用提供的方法,由於兩個應用不在一個內存空間,不能直接調用,所以需要通過網絡來表達調用的語義和傳達調用的數據。
RPC 並不是一個具體的技術,而是指整個網絡遠程調用過程。
RPC 是一個泛化的概念,嚴格來說一切遠程過程調用手段都屬於 RP C范疇。各種開發語言都有自己的 RPC 框架。Java 中的 RPC 框架比較多,廣泛使用的有 RMI、Hessian、Dubbo
等。
原理
服務消費方(client)調用以本地調用方式調用服務。客戶端存根(client stub)接收到調用后負責將方法、參數等編碼成能在網絡中傳輸的消息體。然后,客戶端存根找到服務地址后,將消息發送給服務端。
服務提供方(server)收到序列化后的消息,就按照解碼該消息。然后,根據解碼結果調用本地服務,執行完畢后,將結果打包發送給消費方。
服務消費方收到執行結果后,也是進行解碼后得到結果。
原理
使用場景
RPC 分布式服務,拆分應用進行服務化,提高開發效率,調優性能,節省競爭資源
_配置管理,_解決服務的地址信息劇增,配置困難的問題
_服務依賴,_解決服務間依賴關系錯蹤復雜的問題
_服務擴容,_解決隨着訪問量的不斷增大,動態擴展服務提供方的機器的問題
核心功能
Remoting
:遠程通訊,提供對多種 NIO 框架抽象封裝,包括“同步轉異步”和“請求-響應”模式的信息交換方式。
Cluster
:服務框架,提供基於接口方法的透明遠程過程調用,包括多協議支持,以及軟負載均衡,失敗容錯,地址路由,動態配置等集群支持。
Registry
:服務注冊中心,服務自動發現: 基於注冊中心目錄服務,使服務消費方能動態的查找服務提供方,使地址透明,使服務提供方可以平滑增加或減少機器。
核心組件
_Provider:_服務的提供方
_Consumer:_調用遠程服務的服務消費方
_Registry:_服務注冊和發現的注冊中心
_Monitor:_統計服務調用次數和調用時間的監控中心
_Container:_服務運行容器
組件
服務注冊與發現
流程如下:
_1、_Provider(提供者)綁定指定端口並啟動服務
_2、_供者連接注冊中心,並發本機 IP、端口、應用信息和提供服務信息發送至注冊中心存儲
_3、_Consumer(消費者),連接注冊中心 ,並發送應用信息、所求服務信息至注冊中心
_4、_注冊中心根據消費者所求服務信息匹配對應的提供者列表發送至Consumer 應用緩存。
_5、_Consumer 在發起遠程調用時基於緩存的消費者列表擇其一發起調用。
_6、_Provider 狀態變更會實時通知注冊中心、在由注冊中心實時推送至Consumer設計的原因:
Consumer 與 Provider 解偶,雙方都可以橫向增減節點數。注冊中心對本身可做對等集群,可動態增減節點,並且任意一台宕掉后,將自動切換到另一台
_7、_去中心化,雙方不直接依懶注冊中心,即使注冊中心全部宕機短時間內也不會影響服務的調用
_8、_服務提供者無狀態,任意一台宕掉后,不影響使用
流程
服務治理
治理原因
Dubbo的服務治理主要原因:
_1、_過多的服務 URL 配置困難。
_2、_負載均衡分配節點壓力過大的情況下也需要部署集群。
_3、_服務依賴混亂,啟動順序不清晰。
_4、_過多服務導致性能指標分析難度較大,需要監控。
主要特性
_透明遠程調用:_就像調用本地方法一樣調用遠程方法;只需簡單配置,沒有任何 API 侵入
_負載均衡機制:_Client 端 LB,可在內網替代 F5 等硬件負載均衡器
_容錯重試機制:_服務 Mock 數據,重試次數、超時機制等
_自動注冊發現:_注冊中心基於接口名查詢服務提 供者的 IP 地址,並且能夠平滑添加或刪除服務提供者
_性能日志監控:_Monitor 統計服務的調用次調和調用時間的監控中心
_服務治理中心:_路由規則,動態配置,服務降級,訪問控制,權重調整,負載均衡,等手動配置
_自動治理中心:_無,比如:熔斷限流機制、自動權重調整等(因此可以搭配SpringCloud的熔斷機制等進行開發)
服務治理
架構設計
整體架構
先看下 Dubbo 的整體架構圖:
圖例說明:
整體架構
圖中左邊淡藍背景的為服務消費方使用的接口,右邊淡綠色背景的為服務提供方使用的接口,位於中軸線上的為雙方都用到的接口。
圖中從下至上分為十層,各層均為單向依賴,右邊的黑色箭頭代表層之間的依賴關系,每一層都可以剝離上層被復用,其中,Service
和 Config
層為 API,其它各層均為 SPI。
圖中綠色小塊的為擴展接口,藍色小塊為實現類,圖中只顯示用於關聯各層的實現類。
圖中藍色虛線為初始化過程,即啟動時組裝鏈,紅色實線為方法調用過程,即運行時調時鏈,紫色三角箭頭為繼承,可以把子類看作父類的同一個節點,線上的文字為調用的方法。
各層說明
_config 配置層:_對外配置接口,以 ServiceConfig, ReferenceConfig 為中心,可以直接初始化配置類,也可以通過 spring 解析配置生成配置類
_proxy 服務代理層:_服務接口透明代理,生成服務的客戶端 Stub 和服務器端 Skeleton,以ServiceProxy 為中心,擴展接口為 ProxyFactory
_registry 注冊中心層:_封裝服務地址的注冊與發現,以服務 URL 為中心,擴展接口為RegistryFactory, Registry, RegistryService
_cluster 路由層:_封裝多個提供者的路由及負載均衡,並橋接注冊中心,以 Invoker 為中心,擴展接口為 Cluster, Directory, Router, LoadBalance
_monitor 監控層:_RPC 調用次數和調用時間監控,以 Statistics 為中心,擴展接口為MonitorFactory, Monitor, MonitorService
_protocol 遠程調用層:_封裝 RPC 調用,以 Invocation, Result 為中心,擴展接口為 Protocol, Invoker, Exporter
_exchange 信息交換層:_封裝請求響應模式,同步轉異步,以 Request, Response 為中心,擴展接口為 Exchanger, ExchangeChannel, ExchangeClient, ExchangeServer
_transport 網絡傳輸層:_抽象 mina 和 netty 為統一接口,以 Message 為中心,擴展接口為 Channel, Transporter, Client, Server, Codec
_serialize 數據序列化層:_可復用的一些工具,擴展接口為 Serialization, ObjectInput, ObjectOutput, ThreadPool
主要模塊
dubbo-common
公共邏輯模塊,包括 Util
類和通用模型。
dubbo-remoting
遠程通訊模塊,相當於 Dubbo 協議的實現,如果 RPC 用 RMI 協議則不需要使用此包。
dubbo-rpc
遠程調用模塊,抽象各種協議,以及動態代理,只包含一對一的調用,不關心集群的管理。
dubbo-cluster
集群模塊,將多個服務提供方偽裝為一個提供方,包括:負載均衡、容錯、路由等,集群的地址列表可以是靜態配置的,也可以是由注冊中心下發。
dubbo-registry
注冊中心模塊,基於注冊中心下發地址的集群方式,以及對各種注冊中心的抽象。
dubbo-monitor
監控模塊,統計服務調用次數,調用時間的,調用鏈跟蹤的服務。
dubbo-config
配置模塊,是 Dubbo 對外的 API ,用戶通過 Config 使用 Dubbo ,隱藏 Dubbo 所有細節。
dubbo-container
容器模塊,是一個 Standalone
的容器,以簡單的 Main 加載 Spring 啟動,因為服務通常不需要 Tomcat/JBoss 等 Web 容器的特性,沒必要用 Web 容器去加載服務。
主要模塊
調用方式
異步調用
基於 NIO 的非阻塞實現並行調用,客戶端不需要啟動多線程即可完成並行調用多個遠程服務,相對多線程開銷較小
異步調用
本地調用
使用了Injvm協議,是一個偽協議,它不開啟端口,不發起遠程調用,只在JVM內直接關聯,但執行Dubbo的Filter鏈。
Define injvm protocol:
<dubbo:protocol name="injvm" />
Set default protocol:
`<dubbo:provider protocol="injvm" />`
Set service protocol:
<dubbo:service protocol="injvm" />
Use injvm first:(服務暴露與服務引用都需要聲明injvm=“true”)
`<dubbo:consumer injvm="true" .../>`
`<dubbo:provider injvm="true" .../>`
`或`
`<dubbo:reference injvm="true" .../> <dubbo:service injvm="true" .../>`
容錯機制
調用流程
_1、_Cluster 將 Directory 中的多個 Invoker 偽裝成一個Invoker,對上層透明,偽裝過程包含了容錯邏輯
_2、_Router 負責從多個 Invoker 中按路由規則選出子集,比如讀寫分離,應用隔離等
_3、_LoadBalance 負責從多個 Invoker 中選出具體的一個用於本次調用,選的過程包含了負載均衡算法
調用流程
容錯策略
Dubbo 官網提出總共有六種容錯策略
_1、_Failover Cluster
失敗自動切換,當出現失敗,重試其它服務器。(默認)
_2、_Failfast Cluster
快速失敗,只發起一次調用,失敗立即報錯。通常用於非冪等性的寫操作,比如新增記錄。
_3、_Failsafe Cluster
失敗安全,出現異常時,直接忽略。通常用於寫入審計日志等操作。
_4、_Failback Cluster
失敗自動恢復,后台記錄失敗請求,定時重發。通常用於消息通知操作。
_5、_Forking Cluster
並行調用多個服務器,只要一個成功即返回。通常用於實時性要求較高的讀操作,但需要浪費更多服務資源。
可通過 forks=”2”來設置最大並行數。
_6、_Broadcast Cluster
廣播調用所有提供者,逐個調用,任意一台報錯則報錯。(2.1.0 開始支持) 通常用於通知所有提供者更新緩存或日志等本地資源信息。
總結:在實際應用中查詢語句容錯策略建議使用默認 Failover Cluster
,而增刪改建議使用 Failfast Cluster
或者使用 Failover Cluster
(retries=”0”)策略,防止出現數據重復添加等等其它問題!建議在設計接口時候把查詢接口方法單獨做一個接口提供查詢。
連接方式
Dubbo 的客戶端和服務端有三種連接方式,分別是:廣播、直連和使用Zookeeper
注冊中心。
Dubbo 廣播
這種方式是dubbo官方入門程序所使用的連接方式,但是這種方式有很多問題,在企業開發中不使用廣播的方式。
服務端配置:
`<!--配制dubbo-->`
`<!--提供應用信息,用於計算依賴關系-->`
`<dubbo:application name="demo-service"/>`
`<!--使用multicast廣播注冊暴露服務地址-->`
`<dubbo:registry address="multicast://192.168.9.4:88888" />`
`<!--使用dubbo協議在20880端口暴露服務-->`
`<dubbo:protocol name="dubbo" port="20880"/>`
`<!--聲明暴露的服務接口-->`
`<dubbo:service interface="com.demo.manger.service.TestService" ref="testServiceImpl" />`
客戶端配置:
`<!--配合dubbo-->`
`<!--提供應用信息,用於計算依賴關系-->`
`<dubbo:application name="demo-web"/>`
`<!--使用multicast廣播注冊中心暴露服務地址 -->`
`<dubbo:registry address="multicast://19.188.8.9:8888"/>`
`<!--聲明需要暴露的接口-->`
`<dubbo:reference interface="com.demo.manager.service.TestService" id="testService" timeout="1000000" />`
Dubbo 直連
這種方式在企業中一般在開發中環境中使用,但是生產環境很少使用,因為服務是直接調用,沒有使用注冊中心,很難對服務進行管理。Dubbo 直連,首先要取消廣播,然后客戶端直接到指定需要的服務的 url 獲取服務即可。
服務端配置:
`<!--配制dubbo-->`
`<!--提供應用信息,用於計算依賴關系-->`
`<dubbo:application name="demo-service"/>`
`<!--使用multicast廣播注冊暴露服務地址-->`
`<-- <dubbo:registry address="multicast://192.168.9.4:88888" /> -->`
`<dubbo:registry adress="N/A">`
`<!--使用dubbo協議在20880端口暴露服務-->`
`<dubbo:protocol name="dubbo" port="20880"/>`
`<!--聲明暴露的服務接口-->`
`<dubbo:service interface="com.demo.manger.service.TestService" ref="testServiceImpl" />`
客戶端配置:
`<!--配合dubbo-->`
`<!--提供應用信息,用於計算依賴關系-->`
`<dubbo:application name="demo-web"/>`
`<!--使用multicast廣播注冊中心暴露服務地址 -->`
`<-- <dubbo:registry address="multicast://19.188.8.9:8888"/> -->`
`<!--聲明需要暴露的接口-->`
`<dubbo:reference interface="com.demo.manager.service.TestService" id="testService" timeout="1000000" url="dubbo://127.0.0.1:20880" />`
zookeeper 注冊中心
Dubbo 注冊中心和廣播注冊中心配置類似,不過需要指定注冊中心類型和注冊中心地址,這個時候就不是把服務信息進行廣播了,而是告訴給注冊中心進行管理,這個時候我們就需要有一個注冊中心,官方推薦使用 zookeeper 作為注冊中心。
Zookeeper 注冊中心
注冊中心負責服務地址的注冊與查找,相當於目錄服務,服務提供者在啟動時與注冊中心交互,消費者不斷的發起請求獲取服務信息,注冊中心不轉發請求,壓力較小
服務端配置:
`<!--配制dubbo-->`
`<!--提供應用信息,用於計算依賴關系-->`
`<dubbo:application name="demo-service"/>`
`<!--使用multicast廣播注冊暴露服務地址-->`
`<!-- <dubbo:registry address="multicast://192.168.9.4:88888" /> -->`
`<!--<dubbo:registry adress="N/A"> -->`
`<dubbo:registry protocol="zookeeper" address="192.168.37,136:2181">`
`<!--使用dubbo協議在20880端口暴露服務-->`
`<dubbo:protocol name="dubbo" port="20880"/>`
`<!--聲明暴露的服務接口-->`
`<dubbo:service interface="com.demo.manger.service.TestService" ref="testServiceImpl" />`
客戶端配置:
`<!--配合dubbo-->`
`<!--提供應用信息,用於計算依賴關系-->`
`<dubbo:application name="demo-web"/>`
`<!--使用multicast廣播注冊中心暴露服務地址 -->`
`<-- <dubbo:registry address="multicast://19.188.8.9:8888"/> -->`
`<dubbo:registry protocol="zookeeper" address="192.168.37.1336:2181"/>`
`<!--聲明需要暴露的接口-->`
`<dubbo:reference interface="com.demo.manager.service.TestService" id="testService" timeout="1000000" />`
策略
負載均衡策略
_1、_Random LoadBalance,隨機(默認的負載均衡策略)
RandomLoadBalance 是加權隨機算法的具體實現,可以完全隨機,也可以按權重設置隨機概率。
_2、_RoundRobin LoadBalance,輪循
可以輪詢和加權輪詢。存在響應慢的提供者會累積請求的問題,比如:第二台機器很慢,但沒掛,當請求調到第二台時就卡在那,久而久之,所有請求都卡在調到第二台上。
_3、_LeastActive LoadBalance,最少活躍調用數
活躍調用數越小,表明該服務提供者效率越高,單位時間內可處理更多的請求。此時應優先將請求分配給該服務提供者。
_4、_ConsistentHash LoadBalance,一致性 Hash
一致性 Hash 算法,相同參數的請求一定分發到一個 provider 上去。provider 掛掉的時候,會基於虛擬節點均勻分配剩余的流量,抖動不會太大。
集群容錯策略
_1、_failover cluster(默認)
失敗自動切換,調用失敗時,自動重試其他機器。通常用於讀操作,但重試會帶來更長延遲。
_2、_Failfast Cluster
快速失敗,只發起一次調用,失敗立即報錯。通常用於非冪等性的寫操作,比如新增記錄。
_3、_Failsafe Cluster
失敗安全,出現異常時,直接忽略。通常用於寫入審計日志等操作。
_4、_Failback Cluster
失敗自動恢復,后台記錄失敗請求,定時重發。通常用於消息通知操作。
_5、_Forking Cluster
並行調用多個服務器,只要一個成功即返回。通常用於實時性要求較高的讀操作,但需要浪費更多服務資源。
動態代理策略
默認使用 javassist
動態字節碼生成,創建代理類。也可以通過 spi 擴展機制配置自己的動態代理策略。
集群容錯方案
- 配置說明,方案配置方式,優先使用消費端配置
`<!--服務端配置-->`
`<dubbo:service cluster="failover"/>`
`<!--消費端配置-->`
`<dubbo:reference cluster="failover"/>`
-
盡量在只在服務端進行配置
-
cluster類型均為小寫
-
默認為FailoverCluster失敗切換方案
集群容錯方案support
FailoverCluster(默認):失敗切換
-
場景:調用失敗后切換其他服務
-
配置:
`<!--`
`retries:重試次數,不包括第一次,默認2次`
`-->`
`<dubbo:service cluster="failover" retries="3"/>`
-
代碼實現邏輯:
1. 根據負載均衡策略選出需要調用的服務實例,排除已調用的
2. 執行選出的實例,並將其保存到已調用列表中
3. 執行實例成功即返回
4. 執行實例不成功,為到最大重試次數則執行第一步,否則拋出RpcException異常
FailbackCluster:失敗重試
-
場景:調用失敗時記錄失敗請求,定時重發
-
配置:
`<!--`
`retries:重試次數,不包括第一次,默認3次`
`failbacktasks:定時器中最大掛起任務數,默認100`
`-->`
`<dubbo:service cluster="failback" retries="5" failbacktasks="200"/>`
-
代碼實現邏輯
1. 根據負載均衡策略選出需要調用的服務實例
2. 執行選出的實例
3. 執行實例成功即返回
4. 執行異常則創建延時5秒的定時任務,並加入時間輪定時器,第一次需要進行定時器初始化,分為32個時間片,每1秒滾動一次,最大掛起任務默認100個,超出最大任務數時拋出RejectedExecutionException異常。
5. 重試執行定時任務,次數超出最大執行次數停止,並輸出error日志,默認為3次。
FailfastCluster:快速失敗
-
場景:調用失敗立即報錯
-
配置:
`<dubbo:service cluster="failfast"/>`
-
代碼實現邏輯
1. 根據負載均衡策略選出需要調用的服務實例
2. 執行選出的實例
3. 執行實例成功即返回,失敗拋出RpcException異常
FailsafeCluster:安全失敗
-
場景:調用失敗后忽略
-
配置:
`<dubbo:service cluster="failsafe"/>`
-
代碼實現邏輯
1. 根據負載均衡策略選出需要調用的服務實例
2. 執行選出的實例
3. 執行實例成功即返回,失敗輸出error日志,並返RpcResult,視為忽略。
ForkingCluster:並發處理
-
場景:並發調用指定數量的服務,一個成功則返回,對實時性要求高的場景,要求快速返回,需要使用更多服務器資源。
-
配置:
`<!--`
`forks:最大並發數,默認2`
`timeout:並發返回超時時間,默認1000ms`
`-->`
`<dubbo:service cluster="forking" forks="3" timeout="500"/>`
-
代碼實現邏輯
1. 根據負載均衡策略選出幾個不同的服務實例
2. 並發執行選出的幾個實例,並將返回結果放入堵塞隊列中
3. 返回堵塞隊列中的第一個值,如規定時間內未獲取到隊列中的值或獲取到異常值則返回RPC異常。
BroadcastCluster:廣播
-
場景:廣播方式逐個調用服務提供者,有一個報錯則返回錯誤,多用於通知服務提供者更新本地資源信息,如緩存,日志等。
-
配置:
`<dubbo:service cluster="broadcast"/>`
-
代碼實現邏輯
1. 循環逐個執行所有服務實例信息
2. 保存一份返回結果和異常信息
3. 執行完全部實例后,如異常信息不為空,則拋出異常信息,否則返回最后一個實例的結果。
AvailableCluster:可用服務
-
場景:調用第一個可用服務
-
配置:
`<dubbo:service cluster="available"/>`
-
代碼實現邏輯
1. 循環所有服務實例信息
2. 執行第一個可用的實例,並返回結果
3. 如無可用實例則返回RpcException異常
MergeableCluster:合並處理
-
場景:返回合並或疊加處理結果
-
配置:
`<!--`
`merger:合並發放名`
`timeout:調用服務超時時間,默認1000ms`
`-->`
`<dubbo:service cluster="mergeable" merger="true" timeout="500"/>`
-
代碼實現邏輯
1. 判斷merger,為空、null、0、false、N/A是執行第一個可用服務並返回結果,無可用則執行第一個實例,並返回結果。
2. 獲取方法實例的返回類型
3. 異步調用所有實例,並將異步結果Result存儲到結果集中,返回異常輸出error日志
4. 結果集為空返回 RpcException,大小為 1時返回第一個Result
5. 當merger的第一個字符為“.”時,判斷當 merger 實例返回類型不為void,且返回類型必須是結果集中第一個返回類型的父類型或相同類型時,循環執行merger實例,每一次都傳入上一次的返回結果,最終返回獲取最后一次結果,非上述情況時循環執行merger實例,返回結果集中的第一個結果。
6. 當merger為true或default時使用Dubbo默認合並器,否則使用自定義merger合並器,合並后返回
RegistryAwareCluster:默認標識、注冊標識
-
場景:調用注冊默認標識的服務
-
配置:
`<!--`
`default:默認標識`
`-->`
`<dubbo:registry address="zookeeper://xxx..." default="true"/>`
`<dubbo:service cluster="registryaware"/>`
-
代碼實現邏輯
1.8 循環所有服務實例信息
2. 執行第一個可用的實例且default為true的實例
3. 無默認實例則執行第一個可用的實例
4. 無可用的實例則拋出RpcException異常
主要配置
配置應用信息:
`<dubbo:application name=“appName-provider” />`
配置注冊中心相關信息:
`<dubbo:registryid=“zk” protocol=“zookeeper” address=“127.0.0.1:2181” />`
配置服務協議:
`<dubbo:protocol name=“dubbo” port=“20880” threadpool=“cached” threads=“80” />`
配置所有暴露服務缺省值:
`<dubbo:provider registry=“zk” protocol=“dubbo” retries=“0” version=“1.0.0” timeout=“3000” threadpool=“cached” threads=“4”/>`
配置暴露服務:
`<dubbo:service interface=“com.orgname.app.serviceX” ref=“serviceX” />`
配置所有引用服務缺省值:
`<dubbo:consumer check=“false” timeout=“1000” version=“1.0” retries=“0” async=“false” />`
注解配置:
`com.alibaba.dubbo.config.annotation.Service 配置暴露服務`
`com.alibaba.dubbo.config.annotation.Reference配置引用服務`
超時設置
Dubbo消費端
全局超時配置
`<dubbo:consumer timeout="5000" />`
指定接口以及特定方法超時配置
`<dubbo:reference interface="com.foo.BarService" timeout="2000">`
`<dubbo:method name="sayHello" timeout="3000" />`
`</dubbo:reference>`
Dubbo服務端
全局超時配置
`<dubbo:provider timeout="5000" />`
指定接口以及特定方法超時配置
`<dubbo:provider interface="com.foo.BarService" timeout="2000">`
`<dubbo:method name="sayHello" timeout="3000" />`
`</dubbo:provider>`
支持協議
_1、_Dubbo 協議(官方推薦協議)
優點:采用NIO復用單一長連接,並使用線程池並發處理請求,減少握手和加大並發效率,性能較好(推薦使用)
缺點:大文件上傳時,可能出現問題(不使用 Dubbo 文件上傳)
_2、_RMI(Remote Method Invocation)協議
優點:JDK 自帶的能力。可與原生 RMI 互操作,基於 TCP 協議
缺點:偶爾連接失敗.
_3、_Hessian協議
優點:可與原生 Hessian 互操作,基於 HTTP 協議
缺點:需 hessian.jar 支持,http 短連接的*_開銷大_8
常用設計模式
Dubbo 框架在初始化和通信過程中使用了多種設計模式,可靈活控制類加載、權限控制等功能。
工廠模式
Provider 在 export 服務時,會調用 ServiceConfig 的 export 方法。ServiceConfig 中有個字段:
`private static final Protocol protocol =`
`ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();`
Dubbo 里有很多這種代碼。這也是一種工廠模式,只是實現類的獲取采用了 JDK SPI 的機制。這么實現的優點是可擴展性強,想要擴展實現,只需要在 classpath下增加個文件就可以了,代碼零侵入。另外,像上面的 Adaptive 實現,可以做到調用時動態決定調用哪個實現,但是由於這種實現采用了動態代理,會造成代碼調試比較麻煩,需要分析出實際調用的實現類。
裝飾器模式
Dubbo 在啟動和調用階段都大量使用了裝飾器模式。以 Provider 提供的調用鏈為例,具體的調用鏈代碼是在 ProtocolFilterWrapper 的buildInvokerChain 完成的,具體是將注解中含有 group=provider 的 Filter 實現,按照 order 排序,最后的調用順序是:
`EchoFilter -> ClassLoaderFilter -> GenericFilter -> ContextFilter ->`
`ExecuteLimitFilter -> TraceFilter -> TimeoutFilter -> MonitorFilter ->`
`ExceptionFilter`
更確切地說,這里是裝飾器和責任鏈模式的混合使用。例如,EchoFilter 的作用是判斷是否是回聲測試請求,是的話直接返回內容,這是一種責任鏈的體現。而像ClassLoaderFilter 則只是在主功能上添加了功能,更改當前線程的 ClassLoader,這是典型的裝飾器模式。
觀察者模式
Dubbo 的 Provider 啟動時,需要與注冊中心交互,先注冊自己的服務,再訂閱自己的服務,訂閱時,采用了觀察者模式,開啟一個 listener。注冊中心會每 5 秒定時檢查是否有服務更新,如果有更新,向該服務的提供者發送一個 notify 消息,provider 接受到 notify 消息后,即運行 NotifyListener 的 notify 方法,執行監聽器方法。
動態代理模式
Dubbo 擴展 JDK SPI 的類 ExtensionLoader 的 Adaptive 實現是典型的動態代理實現。Dubbo 需要靈活地控制實現類,即在調用階段動態地根據參數決定調用哪個實現類,所以采用先生成代理類的方法,能夠做到靈活的調用。生成代理類的代碼是 ExtensionLoader 的 createAdaptiveExtensionClassCode 方法。代理類的主要邏輯是,獲取 URL 參數中指定參數的值作為獲取實現類的 key
工作流程
整體流程:
第一步:provider 向注冊中心去注冊
第二步:consumer 從注冊中心訂閱服務,注冊中心會通知 consumer 注冊好的服務
第三步:consumer 調用 provider
第四步:consumer 和 provider 都異步通知監控中心
流程圖
總結
最后用一張圖來形象的模擬 Dubbo 的使用:
使用
以上只是我總結的一些關於 dubbo 最基礎的原理及使用介紹,至於代碼編寫過程的 bug 處理經驗,環境搭建、項目布局等等問題,需要我們在平時開發中,將系統知識與實戰經驗相結合去總結,這樣才能真正的去掌握這項技術點。
Dubbo 目前是我用到過的最多的分布式框架,寫出來的內容也是最多的,不過由於Dubbo用的太多,而 SpringCloud 難度比 Dubbo 要小很多,現在大部分項目都即將開始轉投到了 SpringCloud 上面,后面也會出更多的 SpringCloud 相關的文章。