1. 程式人生 > 實用技巧 >Dubbo實現原理淺析

Dubbo實現原理淺析

參考:

https://blog.csdn.net/paul_wei2008/article/details/19355681

https://blog.csdn.net/ygl19920119/article/details/88342523

https://blog.csdn.net/l_bestcoder/article/details/79368530

https://blog.csdn.net/chao_19/article/details/51764150

Dubbo基本原理機制

分散式服務框架:
–高效能和透明化的RPC遠端服務呼叫方案
–SOA服務治理方案
-Apache MINA框架基於Reactor模型通訊框架,基於tcp長連線
Dubbo預設協議採用單一長連線和NIO非同步通訊,
適合於小資料量大併發的服務呼叫,以及服務消費者機器數遠大於服務提供者機器數的情況
分析原始碼,基本原理如下:
  1. client一個執行緒呼叫遠端介面,生成一個唯一的ID(比如一段隨機字串,UUID等),Dubbo是使用AtomicLong從0開始累計數字的
  2. 將打包的方法呼叫資訊(如呼叫的介面名稱,方法名稱,引數值列表等),和處理結果的回撥物件callback,全部封裝在一起,組成一個物件object
  3. 向專門存放呼叫資訊的全域性ConcurrentHashMap裡面put(ID, object)
  4. 將ID和打包的方法呼叫資訊封裝成一物件connRequest,使用IoSession.write(connRequest)非同步傳送出去
  5. 當前執行緒再使用callback的get()方法試圖獲取遠端返回的結果,在get()內部,則使用synchronized獲取回撥物件callback的鎖, 再先檢測是否已經獲取到結果,如果沒有,然後呼叫callback的wait()方法,釋放callback上的鎖,讓當前執行緒處於等待狀態。
  6. 服務端接收到請求並處理後,將結果(此結果中包含了前面的ID,即回傳)傳送給客戶端,客戶端socket連線上專門監聽訊息的執行緒收到訊息,分析結果,取到ID,再從前面的ConcurrentHashMap裡面get(ID),從而找到callback,將方法呼叫結果設定到callback物件裡。
  7. 監聽執行緒接著使用synchronized獲取回撥物件callback的鎖(因為前面呼叫過wait(),那個執行緒已釋放callback的鎖了),再notifyAll(),喚醒前面處於等待狀態的執行緒繼續執行(callback的get()方法繼續執行就能拿到呼叫結果了),至此,整個過程結束。
  • 當前執行緒怎麼讓它“暫停”,等結果回來後,再向後執行?
答:先生成一個物件obj,在一個全域性map裡put(ID,obj)存放起來,再用synchronized獲取obj鎖,再呼叫obj.wait()讓當前執行緒處於等待狀態,然後另一訊息監聽執行緒等到服 務端結果來了後,再map.get(ID)找到obj,再用synchronized獲取obj鎖,再呼叫obj.notifyAll()喚醒前面處於等待狀態的執行緒。
  • 正如前面所說,Socket通訊是一個全雙工的方式,如果有多個執行緒同時進行遠端方法呼叫,這時建立在client server之間的socket連線上會有很多雙方傳送的訊息傳遞,前後順序也可能是亂七八糟的,server處理完結果後,將結果訊息傳送給client,client收到很多訊息,怎麼知道哪個訊息結果是原先哪個執行緒呼叫的?
答:使用一個ID,讓其唯一,然後傳遞給服務端,再服務端又回傳回來,這樣就知道結果是原先哪個執行緒的了。

dubbo的工作原理

轉載地址:https://blog.csdn.net/A_BlackMoon/article/details/85609328;https://blog.csdn.net/Cs_orange/article/details/82313866

dubbo的工作原理

1、面試題

說一下的dubbo的工作原理?註冊中心掛了可以繼續通訊嗎?說說一次rpc請求的流程?

2、面試官心裡分析

MQ、ES、Redis、Dubbo,上來先問你一些思考的問題,原理(kafka高可用架構原理、es分散式架構原理、redis執行緒模型原理、Dubbo工作原理),生產環境裡可能會碰到的一些問題(每種技術引入之後生產環境都可能會碰到一些問題),系統設計(設計MQ,設計搜尋引擎,設計一個快取,設計rpc框架),

當然比如說,hard面試官,死扣,結合專案死扣細節,百度(深入底層,基礎性),阿里(結合專案死扣細節,扣很深的技術底層),小米(資料結構和演算法)。

那既然開始聊分散式系統了,自然重點先聊聊dubbo了,畢竟dubbo是目前事實上大部分公司的分散式系統的rpc框架標準,基於dubbo也可以構建一整套的微服務架構。但是需要自己大量開發。

當然去年開始spring cloud非常火,現在大量的公司開始轉向spring cloud了,spring cloud人家畢竟是微服務架構的全家桶式的這麼一個東西。但是因為很多公司還在用dubbo,所以dubbo肯定會是目前面試的重點,何況人家dubbo現在重啟開源社群維護了,未來應該也還是有一定市場和地位的。

既然聊dubbo,那肯定是先從dubbo原理開始聊了,你先說說dubbo支撐rpc分散式呼叫的架構是啥,然後說說一次rpc請求dubbo是怎麼給你完成的,對吧。

3、面試題剖析

(1)dubbo工作原理

第一層:service層,介面層,給服務提供者和消費者來實現的
第二層:config層,配置層,主要是對dubbo進行各種配置的
第三層:proxy層,服務代理層,透明生成客戶端的stub和服務單的skeleton
第四層:registry層,服務註冊層,負責服務的註冊與發現
第五層:cluster層,叢集層,封裝多個服務提供者的路由以及負載均衡,將多個例項組合成一個服務
第六層:monitor層,監控層,對rpc介面的呼叫次數和呼叫時間進行監控
第七層:protocol層,遠端呼叫層,封裝rpc呼叫
第八層:exchange層,資訊交換層,封裝請求響應模式,同步轉非同步
第九層:transport層,網路傳輸層,抽象mina和netty為統一介面
第十層:serialize層,資料序列化層

工作流程:

1)第一步,provider向註冊中心去註冊
2)第二步,consumer從註冊中心訂閱服務,註冊中心會通知consumer註冊好的服務
3)第三步,consumer呼叫provider
4)第四步,consumer和provider都非同步的通知監控中心

dubbo的工作原理.png

(2)註冊中心掛了可以繼續通訊嗎?

可以,因為剛開始初始化的時候,消費者會將提供者的地址等資訊拉取到本地快取,所以註冊中心掛了可以繼續通訊。

(3)Dubbo的執行流程:

專案一啟動,載入配置檔案的時候,就會初始化,服務的提供方ServiceProvider就會向註冊中心註冊自己提供的服務,當消費者在啟動時,就會向註冊中心訂閱自己所需要的服務,如果服務提供方有資料變更等,註冊中心將基於長連線的形式推送變更資料給消費者。
預設使用Dubbo協議:
連線個數:單連線
連線方式:長連線
傳輸協議:TCP
傳輸方式:NIO非同步傳輸
序列化:Hessian二進位制序列化
適用範圍:傳入傳出引數資料包較小(建議小於100K),消費者比提供者個數多,單一消費者無法壓滿提供者,儘量不要使用dubbo協議傳輸大檔案或超大字串
使用場景:常規遠端服務方法呼叫
從上面的適用範圍總結,dubbo適合小資料量大併發的服務呼叫,以及消費者機器遠大於生產者機器數的情況,不適合傳輸大資料量的服務比如檔案、視訊等,除非請求量很低。

(4)Dubbo的安全性如何得到保障:
a.在有註冊中心的情況下,可以通過dubbbo admin中的路由規則,來指定固定ip的消費方來訪問
b.在直連的情況下,通過在服務的提供方中設定密碼(令牌)token,消費方需要在消費時也輸入這 個密碼,才能夠正確使用。
Dubbo新增服務ip白名單,防止不法呼叫

(5)Duubo中如何保證分散式事務?
一般情況下,我們儘量將需要事務的方法放在一個service中,從而避開分步式事務。
Dubbo底層是基於socket: Socket通訊是一個全雙工的方式,如果有多個執行緒同時進行遠端方法呼叫,這時建立在client server之間的socket連線上會有很多雙方傳送的訊息傳遞,前後順序也可能是亂七八糟的,server處理完結果後,將結果訊息傳送給client,client收到很多訊息,怎麼知道哪個訊息結果是原先哪個執行緒呼叫的?
答:使用一個ID,讓其唯一,然後傳遞給服務端,再服務端又回傳回來,這樣就知道結果是原先哪個執行緒的了。

(6)Dubbo的心跳機制:
目的:
維持provider和consumer之間的長連線
實現:
dubbo心跳時間heartbeat預設是1s,超過heartbeat時間沒有收到訊息,就傳送心跳消 息(provider,consumer一樣),如果連著3次(heartbeatTimeout為heartbeat*3)沒有收到心跳響應,provider會關閉channel,而consumer會進行重連;不論是provider還是consumer的心跳檢測都是通過啟動定時任務的方式實現;

Dubbo的zookeeper做註冊中心,如果註冊中心全部掛掉,釋出者和訂閱者還能通訊嗎?
可以通訊的,啟動dubbo時,消費者會從zk拉取註冊的生產者的地址介面等資料,快取在本地。每次呼叫時,按照本地儲存的地址進行呼叫;
註冊中心對等叢集,任意一臺宕機後,將會切換到另一臺;註冊中心全部宕機後,服務的提供者和消費者仍能通過本地快取通訊。服務提供者無狀態,任一臺 宕機後,不影響使用;服務提供者全部宕機,服務消費者會無法使用,並無限次重連等待服務者恢復;
掛掉是不要緊的,但前提是你沒有增加新的服務,如果你要呼叫新的服務,則是不能辦到的。

Dubbo實現原理淺析

一、電商系統架構演進

1、單一應用架構

當網站規模很小時,採用單一應用框架,把所有的服務集中在一個應用中,但隨著網站規模增大,單一應用框架會越來越難維護。

2、垂直應用架構

把應用垂直的拆分開來,拆分成如支付、查詢等垂直模組,每個模組都有從頂層顯示層到底層資料持久層的業務邏輯,每個模組都是一個獨立的子系統。雖然在一定程度上降低了開發成本和維護成本,但是會導致許多底層業務邏輯程式碼的重複。

3、分散式應用架構

把核心的業務抽離出來,作為獨立的服務,供上層消費者呼叫。

這種架構就可以用到Dubbo框架,Dubbo是一個RPC(Remote Procedure Call Protocal)框架,用於實現SOA架構,在分散式情況下可以遠端呼叫其他伺服器暴露的方法。

二、Dubbo原理

1、如何做到透明化的呼叫遠端服務?

採用JDK動態代理技術或CGLib位元組碼生成(asm)技術。一般都採用JDK動態代理,因為程式碼易維護。當呼叫生產者類的服務時,其實呼叫的是代理類的方法,代理類中執行了通訊的業務邏輯,並且獲得最後的執行結果。

2、訊息的資料結構

服務呼叫者請求訊息:介面名、方法名、引數型別及引數值、超時時間、RequestID

服務生產者返回訊息:返回值、狀態碼、RequestID

RequestID:因為訊息的傳送與接收是非同步的,為了辨別返回的訊息屬於哪個請求。

3、序列化方式

序列化就是將資料結構或物件轉化為二進位制串的過程,只有轉換成了二進位制串才能進行網路傳輸。

要考慮通用性、效能以及可擴充套件性。dubbo採用hessian。

4、通訊方式

基於NIO。

5、釋出服務

ZooKeeper提供了服務的註冊於自動發現功能,服務提供者的增加、刪除對呼叫者來說是透明的。還提供心跳機制來檢測服務提供者是否還存在。

Dubbo解析及原理淺析

一、Duboo基本概念解釋

Dubbo是一種分散式服務框架。 Webservice也是一種服務框架,但是webservice並不是分散式的服務框架,他需要結合F5實現負載均衡。因此,dubbo除了可以提供服務之外,還可以實現軟負載均衡。它還提供了兩個功能Monitor 監控中心和呼叫中心。這兩個是可選的,需要單獨配置。

Dubbo的計數架構圖如下:

我們解釋以下這個架構圖:

Consumer服務消費者,Provider服務提供者。Container服務容器。消費當然是invoke提供者了,invoke這條實線按照圖上的說明當然同步的意思了,多說一句,在實際呼叫過程中,Provider的位置對於Consumer來說是透明的,上一次呼叫服務的位置(IP地址)和下一次呼叫服務的位置,是不確定的。這個地方就是實現了軟負載。

服務提供者先啟動start,然後註冊register服務。

消費訂閱subscribe服務,如果沒有訂閱到自己想獲得的服務,它會不斷的嘗試訂閱。新的服務註冊到註冊中心以後,註冊中心會將這些服務通過notify到消費者。

Monitor這是一個監控,圖中虛線表明Consumer 和Provider通過非同步的方式傳送訊息至Monitor,Consumer和Provider會將資訊存放在本地磁碟,平均1min會發送一次資訊。Monitor在整個架構中是可選的(圖中的虛線並不是可選的意思),Monitor功能需要單獨配置,不配置或者配置以後,Monitor掛掉並不會影響服務的呼叫。

二、dubbo原理

本篇部落格的內容總體上比較抽象,如果一個想馬上使用dubbo的同學來說,讀這篇部落格效果不太好,本篇部落格沒有寫怎麼使用、配置dubbo,接下來,我再令寫一篇dubbo入門包含demo的部落格。

I、初始化過程細節:
上圖中的第一步start,就是將服務裝載容器中,然後準備註冊服務。和Spring中啟動過程類似,spring啟動時,將bean裝載進容器中的時候,首先要解析bean。所以dubbo也是先讀配置檔案解析服務。
解析服務:
1)、基於dubbo.jar內的Meta-inf/spring.handlers配置,spring在遇到dubbo名稱空間時,會回撥DubboNamespaceHandler類。
2)、所有的dubbo標籤,都統一用DubboBeanDefinitionParser進行解析,基於一對一屬性對映,將XML標籤解析為Bean物件。
原始碼截圖:
在ServiceConfig.export 或者ReferenceConfig.get 初始化時,將Bean物件轉會為url格式,將所以Bean屬性轉成url的引數。
然後將URL傳給Protocol擴充套件點,基於擴充套件點的Adaptive機制,根據URL的協議頭,進行不同協議的服務暴露和引用。
暴露服務:

a、 只暴露服務埠

在沒有使用註冊中心的情況,這種情況一般適用在開發環境下,服務的呼叫這和提供在同一個IP上,只需要開啟服務的埠即可。
即,當配置 or
ServiceConfig解析出的URL的格式為:
Dubbo://service-host/com.xxx.TxxService?version=1.0.0
基於擴充套件點的Adaptiver機制,通過URL的“dubbo://”協議頭識別,直接呼叫DubboProtocol的export()方法,開啟服務埠。

b、向註冊中心暴露服務:

和上一種的區別:需要將服務的IP和埠一同暴露給註冊中心。
ServiceConfig解析出的url格式為:
registry://registry-host/com.alibaba.dubbo.registry.RegistryService?export=URL.encode(“dubbo://service-host/com.xxx.TxxService?version=1.0.0”)

基於擴充套件點的Adaptive機制,通過URL的“registry://”協議頭識別,呼叫RegistryProtocol的export方法,將export引數中的提供者URL先註冊到註冊中心,再重新傳給Protocol擴充套件點進行暴露:
Dubbo://service-host/com.xxx.TxxService?version=1.0.0

引用服務:

a、直接引用服務:

在沒有註冊中心的,直連提供者情況下,
ReferenceConfig解析出的URL格式為:
Dubbo://service-host/com.xxx.TxxService?version=1.0.0

基於擴充套件點的Adaptive機制,通過url的“dubbo://”協議頭識別,直接呼叫DubboProtocol的refer方法,返回提供者引用。

b、從註冊中心發現引用服務:

此時,ReferenceConfig解析出的URL的格式為:
registry://registry-host/com.alibaba.dubbo.registry.RegistryService?refer=URL.encode(“consumer://consumer-host/com.foo.FooService?version=1.0.0”)

基於擴充套件點的Apaptive機制,通過URL的“registry://”協議頭識別,就會呼叫RegistryProtocol的refer方法,基於refer引數總的條件,查詢提供者URL,如:
Dubbo://service-host/com.xxx.TxxService?version=1.0.0

基於擴充套件點的Adaptive機制,通過提供者URL的“dubbo://”協議頭識別,就會呼叫DubboProtocol的refer()方法,得到提供者引用。
然後RegistryProtocol將多個提供者引用,通過Cluster擴充套件點,偽裝成單個提供這引用返回。

三、遠端呼叫細節:

服務提供者暴露一個服務的詳細過程:

上圖是服務提供者暴露服務的主過程:
首先ServiceConfig類拿到對外提供服務的實際類ref,然後將ProxyFactory類的getInvoker方法使用ref生成一個AbstractProxyInvoker例項,到這一步就完成具體服務到invoker的轉化。接下來就是Invoker轉換到Exporter的過程。
Dubbo處理服務暴露的關鍵就在Invoker轉換到Exporter的過程,下面我們以Dubbo和rmi這兩種典型協議的實現來進行說明:
Dubbo的實現:
Dubbo協議的Invoker轉為Exporter發生在DubboProtocol類的export方法,它主要是開啟socket偵聽服務,並接收客戶端發來的各種請求,通訊細節由dubbo自己實現。
Rmi的實現:
RMI協議的Invoker轉為Exporter發生在RmiProtocol類的export方法,他通過Spring或Dubbo或JDK來實現服務,通訊細節由JDK底層來實現。

服務消費者消費一個服務的詳細過程

上圖是服務消費的主過程:
首先ReferenceConfig類的init方法呼叫Protocol的refer方法生成Invoker例項。接下來把Invoker轉為客戶端需要的介面