Dubbo服務發現、引用過程
本文轉載自微信公眾號「大魚仙人」,作者大魚仙人。轉載本文請聯系大魚仙人公眾號。
一.前言
前面兩篇分別介紹了Dubbo的入門和Dubbo的服務暴露
- Dubbo起飛
- 面試官問我:解釋一下Dubbo服務暴露
這篇我們要說的服務引用,服務引用是有兩種情況的,也可以看做是兩種時機,第一個是在Spring容器調用ReferenceBean的afterPropertiesSet方法時引用服務,第二個就是在ReferenceBean對應的服務被注入到其他類中時引用。這兩個引用服務的時機區別在于,第一個是餓漢式的,第二個是懶漢式的
是不是一說餓漢和懶漢,大家順便回憶了一波單例模式
默認情況下,Dubbo使用懶漢式引用服務。如果需要使用餓漢式,可通過配置
服務引入的三種方式:
第一種是引用本地 (JVM) 服務,上篇在服務暴露里面也說過了每個服務都會通過走injvm協議然后走本地的暴露,因為存在一個服務端和消費端是同一臺機器上的情況,這樣就直接走本地調用了,不需要走遠程調用了,節省網絡開銷
第二是通過直連方式引用遠程服務,這種在線上基本不會采用這種形式的,一般都是平時我們自己測試用,直接寫死服務端的地址來調用
第三是通過注冊中心引用遠程服務,Consumer 通過注冊中心得知 Provider 的相關信息,然后進行服務的引入
不管是哪種引用方式,最后都會得到一個 Invoker 實例。如果有多個注冊中心,多個服務提供者,這個時候會得到一組 Invoker 實例,此時需要通過集群管理類 Cluster 將多個 Invoker 合并成一個實例。合并后的 Invoker 實例已經具備調用本地或遠程服務的能力了
但是呢,開發者秉承不對用戶業務代碼侵入的原則,所以此時框架還需要通過代理工廠類ProxyFactory為服務接口生成代理類,并讓代理類去調用Invoker邏輯,避免了Dubbo框架代碼對業務代碼的侵入
二.服務發現
dubbo的服務發現,就是通過從注冊中心訂閱服務提供者,并且組裝成URL,然后通過URL創建出invoker來實現的
服務的引入和服務的暴露一樣,也是通過 spring 自定義標簽機制解析生成對應的 Bean,Provider Service 對應解析的是 ServiceBean 而 Consumer Reference 對應的是 ReferenceBean
dubbo 的服務發現,是通過從注冊中心訂閱服務提供者組裝成 URL,然后通過 URL 創建出 Invoker 來實現的。
這里是入口,進去看ReferenceBean
createLazyProxy中我們會看到DubboReferenceLazyInitTargetSource這一目標資源,點進來new的地方,里面的protected的方法createObject調用了getCallProxy,而這個方法最終調用的是referenceConfig.get()
點進來,我們進入的是ReferenceConfig類的方法,而非ReferenceBean的,這是因為ReferenceConfig是作為ReferenceBean的內部的屬性出現的
Init()方法內部主要就是通過Map設置各種參數,我們看init其中的一個方法叫做createProxy,我們根據名字也可以知道大概意思就是創建代理對象,點進去看看
如果是走本地的話,那么直接構建個本地協議的 URL 然后進行服務的引入,即 refprotocol.refer,這個方法之后會做分析,本地的引入就不深入了,就是去之前服務暴露的 exporterMap 拿到服務
如果不是本地,那肯定是遠程了,接下來就是判斷是點對點直連 provider 還是通過注冊中心拿到 provider 信息再連接 provider 了,我們分析一下配置了 url 的情況,如果配置了 url 那么不是直連的地址,就是注冊中心的地址
這其實就是整個流程了,簡述一下就是先檢查配置,通過配置構建一個 map ,然后利用 map 來構建 URL ,再通過 URL 上的協議利用自適應擴展機制調用對應的 protocol.refer 得到相應的 invoker
我給大家總結個流程圖,這樣大家看著更加清晰
三.服務引用
Dubbo 的服務引用,實際上是為引用的接口創建一個 Proxy,這個 Proxy 的功能就是去執行 refprotocol.refer(interfaceClass, url) 創建出來的 Invoker。當服務提供者有多個時,就創建一個 ClusterInvoker。
Cluster 是一個 SPI 擴展點,默認使用com.alibaba.dubbo.rpc.cluster.support.FailoverCluster
所以,Consumer 端服務調用的邏輯被封裝在 refprotocol.refer(interfaceClass, url) 創建出來的 Invoker 上
主要就是獲取注冊中心實例,然后調用 doRefer 進行真正的 refer。
這里會向注冊中心注冊自身的信息,生成一個Invoker,底層生成用于遠程調用的invoker,然后通過cluster包裝一下再得到ClusterInvoker,因此一個服務可能有多個提供者,然后最后注冊相應的監聽器
拿到了Provider的信息之后就可以通過監聽觸發 Protocol# refer 了,具體調用哪個 protocol 還是得看 URL的協議的,我們看下這個內部DubboProtocol的refer
而這個connect最終返回 HeaderExchangeClient里面封裝的是 NettyClient,然后最終得到的invoker就是對這個client的封裝,最終將返回一個Proxy的代理對象
四.回顧
其實整個流程看代碼啥的,一開始可能會遇到很多新名詞,但是細細一想其實不難,靜下心來好好分析,就都很簡單了
其實就是通過各種配置參數和協議組裝成相應的URL,然后通過自動適配去對相應的實現類進行相應的服務的引入和后續的調用
如果是寫死的地址就直接連接,是注冊中心就向注冊中心注冊信息,然后訂閱注冊中心的相關信息,得到服務提供者的IP端口號等信息,通過netty進行連接,底層會通過directory和cluster進行底層多個服務的屏蔽和負載均衡的處理,得到代理對象Invoker,再通過動態代理封裝得到代理類,總之就是不侵入業務代碼,能用代理解決的就用代理
不侵入代碼的最好辦法就是加代理,遇事不決加層代理