【转载保存】dubbo学习笔记
Dubbo
Dubbo簡介
首先,我理解的Dubbo,從大的方向來看是單體應用到分布式應用過度期的一個產物,具體來說應該是分布式應用從早期的SOA到微服務過度的一個產物。
在編寫分布式場景下高并發、高擴展的系統對技能的要求很高,因為這個過程會涉及到序列化/反序列化、多線程、網絡編程、設計模式、性能優化等眾多專業知識。而Dubbo框架對這些專業知識做了更高層的抽象和封裝,提供了開箱即用的特性。所以換句話說Dubbo是為了解決大流量、高并發場景下提供高可用、提升系統性能的這樣一個服務治理方案,也是優秀的RPC框架之一,因此被眾多公司采用并根據自己的業務實現擴展。
Dubbo具體是一個什么?可不可以在稍微細化說明一下?
上面有提到Dubbo是一個優秀的RPC框架,是Java項目中卓越的框架之一。它提供了注冊中心機制,解耦了服務方和消費方動態發現的問題,并提供了高可靠能力,大量采用微內核+富插件設計思想,包括框架自身核心特性都作為擴展點實現,提供了靈活的擴展點能力。
關于Dubbo框架的架構通過下面這張圖來先簡單了解下,這是dubbo官方提供的,我這邊重新畫了一下,也是為了加深自己的理解。如下:
1.服務提供者Provider啟動時會向注冊中心把自己的元數據注冊上去(比如服務IP、端口及需要注冊的接口定義等信息);
2.服務消費方Consumer在啟動時會從注冊中心訂閱(第一次訂閱會拉取全量數據)服務提供方的元數據;
3.而當注冊中心數據發生變更時會推送給訂閱的Consumer(比如Provider某個節點Down掉了,又或者服務提供方進行了擴容,增加了節點。);
4.在Consumer獲取到元數據后,Consumer可以發起RPC調用(本質其實是Socket通信+動態代理這樣的一個實現機制);
5.RPC調用前后會向監控中心上報統計信息(比如并發數和調用接口)。
Dubbo特性總結
1.面向接口代理的改性能RPC調用
提供了高性能的基于代理的遠程調用能力,服務以接口為粒度,為開發者屏蔽調用的遠程細節。
2.服務自動注冊與發現
支持多種注冊中心服務,服務實例上下線實時感知(上圖中的第3步)。
3.運行期流量調度
內置條件、腳本等路由策略,通過配置不同的路由規則,輕松實現灰度發布、同機房優先等功能。
4.智能負載均衡
內置多種負載均衡策略,智能感知下游節點健康狀況,顯著減少調用延遲,提高系統吞吐量。
5.高度可擴展能力
遵循微內核+插件的設計思想,所有核心能力如Protocol、Transport、Serialization被設計為擴展點,平等對待內置實現和第三方實現。(Dubbo SPI 擴展點機制)
6.可視化的服務治理與運維
提供了豐富服務治理、運維工具(Dubbo Admin):隨時查詢服務元數據、服務簡況狀態及調用統計,實時下發路由策略、調整配置參數。
Dubbo解決的問題
很多同學使用Dubbo有很長一段時間,如果被問起為什么要使用Dubbo,解決了什么問題?我們可能會隨口而出:通過RPC調用實現應用間解耦,進而實現將復雜應用微服務化; 保證了系統功能的專一性,進而提高代碼或者業務功能的復用性; 分布式的架構設計以及智能的負載均衡提升了我們系統的吞吐量,進而提高了性能……沒錯,是這樣的。但是除了這些可能也回答不出來什么了。這里關于Dubbo解決了什么問題做一下整理,在被別人問道的時候心里也清楚,最關鍵的是為我們在后續的業務選型上可以做出正確的判斷。
一開始就提到了單體應用到分布式的演變,那么隨著業務的不斷擴展,服務規模和架構的不斷演進,在大規模服務化之前,應用可能只是通過Java 的 RMI協議 或Hession等工具實現簡單地暴露和引用遠程服務,通過配置URL地址進行調用,最后通過F5等硬件進行負載均衡。那既然可以實現簡單的遠程調用,為什么阿里還要投入這么大成本開發Dubbo呢,當然肯定有必要的一些考量,除了我剛剛提到的那點,Dubbo主要解決以下幾個問題:
(1) 高性能、透明的RPC調用。只要涉及服務之間的通信,RPC就必不可。Dubbo可以讓我們像調用本地服務一樣簡單的去調用遠程服務,而不需要再代碼中顯示的指定遠程調用。整個過程對上層開發者透明,Dubbo會自動完成后續的所有操作,例如:負載均衡、路由、協議轉換、序列化等。開發者只需要接收對應的調用結果。
(2)服務的自動注冊與發現。當服務越來越多時,服務URL配置管理變得非常困難,服務的注冊與發現已經不可能由人工來管理。此時需要一個注冊中心,動態的注冊和發現服務,使服務的位置透明。Dubbo適配了多種注冊中心。
(3)自動負載與容錯。 當服務越來越多時,F5一年負載均衡器的單點壓力也原來越大,Dubbo提供了完整的集群容錯機制,可以實現軟件層面的負載均衡,依次降低硬件的壓力。Dubbo還提供了調用失敗的各種容錯機制,如Failover、Failfast、結果集合并等。
(4)動態的流量調度。在應用運行時,某些服務節點可能因為硬件原因需要減少負載或者某些節點需要人工手動下線;又或者需要實現單元化的調用、灰度功能。通過Dubbo Admin管理控制臺,用戶可以在界面上動態地調整每個服務的權重、路由規則、禁用/啟用,實現運行時的流量調度。
(5)依賴分析和調用統計
Dubbo可以介入第三方的APM做分布式鏈路追蹤與性能分析,或者使用已有的獨立監控中心的調用次數及耗時,我們可以通過這些數據反推系統容量,在合適的時候通過加機器進行擴容,并且可以預估需要添加多少臺機器。
Dubbo總體分層
下面分享一下Dubbo的分層結構,每一層所做的事情,讓我們對Dubbo有一個更進一步的了解。
Dubbo總體分為業務層(Biz)、RPC層、Remote三層。如果把每一層繼續細化,那一共可以分為10層。可以參考下圖,圖中左邊是具體的分層,右邊是該層中比較重要的接口:
其中Service與Config兩層可以認為是API層,主要提供給API使用者,使用者無需關系底層的實現,只需要配置和完成業務代碼即可;后面所有層級合在一起,可以認為是SPI層,主要提供給擴展者使用,即用戶可以基于Dubbo框架做定制性的二次開發,擴展其功能。提供這個分層圖的主要目的是在大家學習期源碼的時候可以按照這個分層學習會清晰一下(我也在學習中)。
Dubbo核心組件
在學習任何一個框架或者是存儲等之前,了解其核心組件是非常有必要的。Dubbo框架中分層代表了不同的邏輯實現,他們是一個個組件,這些組件構成了整個Dubbo體系,在使用方角度更多接觸的可能是配置,更多底層構件被抽象和隱藏了。Dubbo一個很大的特性和亮點便是提供了非常高的擴展性,而這個恰恰得益于各個組件明確的分工設計,每個組件提供了靈活的擴展點,如下:
1.Service層
業務層。包括業務代碼接口與實現,即我們自己實現的業務代碼。
2.config層
配置層。主要圍繞ServiceConfig(暴露的服務配置)和ReferenceConfig(引用的服務配置)兩個實現類展開,初始化配置信息。可以理解為該層管理整個Dubbo配置。
3.proxy層
服務代理層。在Dubbo中,無論生產者還是消費者,框架都會生成一個代理類,整個過程對上層是透明的。當調用一個遠程接口時,看起來就像調用本地接口一樣,代理層會自動做遠程調用并返回結果,即讓業務層對遠程調用完全無感。
4.registry層
注冊層。服務Dubbo框架的服務注冊與發現。當所有新的服務加入或者就服務下線時,注冊中心都會感知并通知訂閱方。整個過程不需要人工參與。
5.cluster層
集群容錯層。主要負責遠程調用失敗時的容錯策略(如失敗重試、快速失敗);選擇具體調用節點時的復雜均衡策略(如隨機、一致性Hash等);特殊調用路徑的路由策略(如某個Consumer只會調用某個IP的Provider)。
6.monitor層
監控層。復雜監控統計調用次數和調用時間等。
7.protocol層
遠程調用層。封裝RPC調用的具體過程,Protocol是Invoker暴露(發布一個新功能讓別人調用)和引用(引用一個遠程服務到本地)的主功能入口。它負責管理Invoker的整個生命周期。Invoker是Dubbo哦核心模型,框架中所有其他模型都向它靠攏,或者轉換成它,它代表一個可執行體。
8.exchange層
信息交換層。建立Request-Response模型,封裝請求響應模式,如吧同步請求轉化為一步請求。
9.transport層
網絡傳輸層。把網絡傳輸抽象為同一的接口,如Mina和Netty雖然接口不一樣,但是Dubbo在他們上面又封裝了統一的接口。我們也可以根據其擴展接口添加更多的網絡傳輸方式。
10.Serialize層
序列化層。如果數據要通過網絡進行發送,則需要先做做序列化,變成二進制流。序列化層負責管理整個框架網絡傳輸時的序列化/反序列化工作。
Dubbo總體調用過程
或許目前有些同學還不能理解整個組件穿起來的工作工程,所以先以服務暴露/注冊為例子簡單描述下。首先服務端(Provider服務提供者)在框架啟動時,會初始化服務實例,通過Proxy組件調用具體協議(Protocol),把服務端要暴露的接口封裝成Incoker(真實類型時AbstractProxyInvoker),然后轉換成Exporter,這個時候框架會打開服務端口等,并記錄服務實例到內存中,最后通過Registry吧服務元數據注冊到注冊中心(比如Zookeeper)。這就是服務端整個接口暴露的過程。關于這里提到了幾個組件,在做一下具體的說明:
(1)Proxy組件:我們知道Dubbo中只需喲引用一個接口就可以調用遠程的服務。其實是Dubbo框架為我們生成了代理類,調用方法其實也是Proxy組件為我們生成的代理方法,最后會自動發起遠程/本地調用,并返回結果,整個過程對我們是完全透明的。
(2)Protocol:其實協議就是對數據的一種約定。它可以把我們對接口的配置通過不同的協議轉換成不同的Invoker對象。例如:用DubboProtocol可以把XML文件中一個遠程接口的配置轉換成一個DubboInvoker。
(3)Exporter:用于暴露到注冊中心的對象,它的內部屬性持有了Invoker對象,我們可以認為它是在Invoker上包了一層。
(4)Registry:把Exporter注冊到注冊中心。
以上就是整個服務暴露的過程,如果Consumer在啟動時在注冊中心訂閱了服務端的元數據(實例的IP、端口、實例上暴露的接口信息),這樣Consumer就可以得到剛才暴露的服務了。
下面在來看一下Consumer調用服務的整個流程,這里只針對遠程調用。Dubbo組件在調用流程中的角色及位置如下圖:
首先,調用者從Proxy開始,Proxy持有一個Invoker對象。然后觸發Invoker調用。在Invoker調用中,需要使用Cluster負責容錯,比如失敗重試。Custer在調用之前會通過Directory獲取所有可以調用的遠程服務Invoker列表(一個接口可能有多個實例/節點提供服務)。由于可以調用的服務有很多,此時如果用戶配置了路由規則(如制定某些方法只能調用某個節點),那么Cluster還會根據路由規則將Invoker列表過濾一遍。
然后,存活下來的Invoker可能還會有很多,此時需要調用哪一個呢?這時候會通過loadBalance方位做負載均衡,最終選出一個可以調用的Invoker。這個Invoker在調用前又會經過一個FilterChain(過濾器鏈),這個FilterChain通常負責處理上下文、限流、計數等。
接著,會使用Client做數據傳輸,如我們常見的Netty Client等(Socket通信)。傳輸之前肯定需要做一個私有協議的構造,此時會用到Codec接口(主要作用是負責協議的編解碼)。在這之后便是序列化過程了(Serialication)、然后傳輸到服務提供端。服務提供者接受到數據包,也會使用Codec處理協議頭及一些包信息等。處理完之后在對整個報文做反序列化處理。
隨后這個請求會被分配到線程池(TheadPool)中進行處理,Server會處理這些請求,根據請求查找賭贏的Exporter(它內部持有了Invoker)。Invoker是被“封裝器模式”一層一層套了非常多的Filter的,因此在調用最終的實現類之前,又會經過一個服務提供者端的過濾器鏈。
最終得到了具體的接口的真實實現并調用,在原路返回結果。
這里的話整個RPC的調用過程就結束了。
創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎總結
以上是生活随笔為你收集整理的【转载保存】dubbo学习笔记的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【转载保存】Jsoup解析html常用方
- 下一篇: 常见压缩算法学习