參考:
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異步通訊,
適合於小數據量大並發的服務調用,以及服務消費者機器數遠大於服務提供者機器數的情況
分析源代碼,基本原理如下:
- client一個線程調用遠程接口,生成一個唯一的ID(比如一段隨機字符串,UUID等),Dubbo是使用AtomicLong從0開始累計數字的
- 將打包的方法調用信息(如調用的接口名稱,方法名稱,參數值列表等),和處理結果的回調對象callback,全部封裝在一起,組成一個對象object
- 向專門存放調用信息的全局ConcurrentHashMap里面put(ID, object)
- 將ID和打包的方法調用信息封裝成一對象connRequest,使用IoSession.write(connRequest)異步發送出去
- 當前線程再使用callback的get()方法試圖獲取遠程返回的結果,在get()內部,則使用synchronized獲取回調對象callback的鎖, 再先檢測是否已經獲取到結果,如果沒有,然后調用callback的wait()方法,釋放callback上的鎖,讓當前線程處於等待狀態。
- 服務端接收到請求並處理后,將結果(此結果中包含了前面的ID,即回傳)發送給客戶端,客戶端socket連接上專門監聽消息的線程收到消息,分析結果,取到ID,再從前面的ConcurrentHashMap里面get(ID),從而找到callback,將方法調用結果設置到callback對象里。
- 監聽線程接着使用synchronized獲取回調對象callback的鎖(因為前面調用過wait(),那個線程已釋放callback的鎖了),再notifyAll(),喚醒前面處於等待狀態的線程繼續執行(callback的get()方法繼續執行就能拿到調用結果了),至此,整個過程結束。
- 當前線程怎么讓它“暫停”,等結果回來后,再向后執行?
- 正如前面所說,Socket通信是一個全雙工的方式,如果有多個線程同時進行遠程方法調用,這時建立在client server之間的socket連接上會有很多雙方發送的消息傳遞,前后順序也可能是亂七八糟的,server處理完結果后,將結果消息發送給client,client收到很多消息,怎么知道哪個消息結果是原先哪個線程調用的?


dubbo的工作原理
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轉為客戶端需要的接口