讓我帶你弄明白什麼是RPC,幫你理清你的思路

讓我帶你弄明白什麼是RPC,幫你理清你的思路

RPC Dubbo

一、系統架構的演變

1、單一應用架構

將所有的功能模組都放在1個工程中編碼、編譯、打包並且部署在1個tomcat容器中的架構。這樣維護成本比較低。

讓我帶你弄明白什麼是RPC,幫你理清你的思路

優點:

專案前期開發週期快,團隊成員少的時候能夠快速迭代;

架構簡單:MVC架構,只需藉助IDE開發,除錯即可。

易於測試:只通過單元測試或者瀏覽器完成

易於部署

缺點:

隨著時間推移業務增加,功能不斷迭代,專案會變得臃腫,業務耦合嚴重

新增業務困難

核心業務與邊緣業務混在一塊兒,出現問題相互影響。

2、垂直架構

為了解決單體框架出現的問題,開始按照業務做垂直劃分。把原來的一個單體拆成一堆單體應用,這時候就由原來的單應用變成多應用部署。

讓我帶你弄明白什麼是RPC,幫你理清你的思路

優點:

可以針對不同模組進行最佳化;

方便水平擴充套件,負載均衡,容錯率提高。

系統間相互獨立,互不影響,新的業務迭代時會更加高效。

缺點:

服務之間相互呼叫,如果某個服務的埠或者IP地址發生改變,呼叫的系統得手動改變。

搭建叢集之後,實現負載均衡比較複雜;

服務之間呼叫方式不統一,基於httpclient、webservice,介面協議不統一。

服務監控不到位:除了依靠埠、程序的監控,呼叫的成功率、失敗率、總耗時等等這些的監控指標是沒有用的。

3、SOA架構

即面向服務的架構。其思想就是根據實際業務,把業務拆分成合適的、獨立部署的模組,模組之間相互獨立(透過Webservice/Double等技術進行通訊)

二、RPC

1、RPC核心和呼叫的大體流程

1)RPC(remote procedure call) 遠端過程呼叫,簡單的理解是一個節點請求另外1個節點的服務。

呼叫過程:

讓我帶你弄明白什麼是RPC,幫你理清你的思路

讓我帶你弄明白什麼是RPC,幫你理清你的思路

客戶端(Client):服務調⽤⽅。

客戶端存根(Client Stub):存放服務端地址資訊,將客戶端的請求引數資料資訊打包成⽹絡訊息, 再透過⽹絡傳輸傳送給服務端。

服務端存根(Server Stub):接收客戶端傳送過來的請求訊息並進⾏解包,然後再調⽤本地服務進⾏ 處理。

服務端(Server):服務的真正提供者。

Network Service:底層傳輸,可以是 TCP 或 HTTP

2、完整的RPC架構圖

在一個典型RPC的使用場景中,包括了服務發現、負載、容錯、網路傳輸、序列化等元件,其中RPC協議就指了程式如何進行網路傳輸和序列化。

讓我帶你弄明白什麼是RPC,幫你理清你的思路

讓我帶你弄明白什麼是RPC,幫你理清你的思路

3、RPC核心功能及實現

服務定址

資料流的序列化和反序列化

三、網路傳輸

1、服務定址

在RPC中,所有的函式都必須有自己的一個ID。這個ID在所有程序中都是唯一確定的。

客戶在做遠端呼叫時必須附上這個ID。然後我們還需要在客戶端和服務端分別維護⼀個 函式和Call ID的對應表。

當客戶端需要進⾏遠端調⽤時,它就查⼀下這個表,找出相應的 Call ID,然後把它傳給服務端,服務端也透過查表,來確定客戶端需要調⽤的函式,然後執⾏相應函式的程式碼

2、序列化和反序列化

本地呼叫,我們只需要把引數壓到棧裡,然後讓函式直接從棧裡讀就行。但是在遠端呼叫時客戶端和服務端是不同的程序,不能透過記憶體傳遞引數。這時就需要客戶端把引數轉換成一個位元組流,傳給服務端後,再把位元組流轉成自己能讀取的格式。

定義:

序列化:將物件轉換轉換成二進位制流程的過程叫做序列化

反序列化:將二進位制流轉換成物件的過程叫做反序列化

3、網路傳輸

網路傳輸層需要把call ID和序列化的引數位元組流傳給服務端,然後再把序列化好的呼叫結果傳給客戶端。

所以,要實現一個RPC框架,只需要把以下三點實現了就基本完成; :

call id對映:可以直接使用函式字串,也可以使用整數ID。對映表一般是1個Hash表。

序列化反序列化:可以自己寫,也可以使用protobuf或者FlatBuffers之類的。

網路傳輸,可以自己寫Socket,或者用Netty

四、Dubbo

經典的RPC框架

讓我帶你弄明白什麼是RPC,幫你理清你的思路

呼叫關係說明:

服務容器負責啟動,載入,執行服務提供者。

服務提供者在啟動時,向註冊中心註冊自己提供的服務。

服務消費者在啟動時,向註冊中心訂閱自己所需的服務。

註冊中心返回服務提供者地址列表給消費者,如果有變更,註冊中心將基於長連線推送變更資料給消費者。

服務消費者,從提供者地址列表中,基於軟負載均衡演算法,選一臺提供者進行呼叫,如果呼叫失敗,再選另一臺呼叫。

服務消費者和提供者,在記憶體中累計呼叫次數和呼叫時間,定時每分鐘傳送一次統計資料到監控中心。

讓我帶你弄明白什麼是RPC,幫你理清你的思路

Config 配置中心

Proxy 代理

Registry 註冊中心

Cluster 負載均衡、路由(輪詢、權重、一致性hash)

Monitor 監控

Protocal 發起呼叫

Exchange 交換

Transport 傳輸(netty)

Serialize 序列化

dubbo 架構特點分別是

連通性

健壯性

伸縮性

、以及

向未來架構的升級

連通性

註冊中心負責服務地址的註冊與查詢,相當於目錄服務,服務提供者和消費者只在啟動時與註冊中心互動,註冊中心不轉發請求,壓力較小

監控中心負責統計各服務呼叫次數,呼叫時間等,統計先在記憶體彙總後每分鐘一次傳送到監控中心伺服器,並以報表展示

服務提供者向註冊中心註冊其提供的服務,並彙報呼叫時間到監控中心,此時間不包含網路開銷

服務消費者向註冊中心獲取服務提供者地址列表,並根據負載演算法直接呼叫提供者,同時彙報呼叫時間到監控中心,此時間包含網路開銷

註冊中心,服務提供者,服務消費者三者之間均為長連線,監控中心除外

註冊中心透過長連線感知服務提供者的存在,服務提供者宕機,註冊中心將立即推送事件通知消費者

註冊中心和監控中心全部宕機,不影響已執行的提供者和消費者,消費者在本地快取了提供者列表

註冊中心和監控中心都是可選的,服務消費者可以直連服務提供者 監控中心宕掉不影響使用,只是丟失部分取樣資料

資料庫宕掉後,註冊中心仍能透過快取提供服務列表查詢,但不能註冊新服務

註冊中心對等叢集,任意一臺宕掉後,將自動切換到另一臺 註冊中心全部宕掉後,服務提供者和服務消費者仍能透過本地快取通訊

服務提供者無狀態,任意一臺宕掉後,不影響使用

服務提供者全部宕掉後,服務消費者應用將無法使用,並無限次重連等待服務提供者恢復

註冊中心為對等叢集,可動態增加機器部署例項,所有客戶端將自動發現新的註冊中心

服務提供者無狀態,可動態增加機器部署例項,註冊中心將推送新的服務提供者資訊給消費者

健壯性

監控中心宕掉不影響使用,只是丟失部分取樣資料

資料庫宕掉後,註冊中心仍能透過快取提供服務列表查詢,但不能註冊新服務

註冊中心對等叢集,任意一臺宕掉後,將自動切換到另一臺

註冊中心全部宕掉後,服務提供者和服務消費者仍能透過本地快取通訊

服務提供者無狀態,任意一臺宕掉後,不影響使用

服務提供者全部宕掉後,服務消費者應用將無法使用,並無限次重連等待服務提供者恢復

伸縮性

註冊中心為對等叢集,可動態增加機器部署例項,所有客戶端將自動發現新的註冊中心

服務提供者無狀態,可動態增加機器部署例項,註冊中心將推送新的服務提供者資訊給消費者

五、Springboot 中Dubbo使用

1、安裝

安裝zookeeper

監控中心 dubbo admin

2、使用方式

配置類、註解方式

Service 一定要引入dubbo的註解。

看官網

3、超時配置

配置超時時間的優先順序如下:

方法級優先,介面級次之,全域性配置再次之

如果級別一樣,則消費方優先,提供方次之。 其中,服務提供方配置,透過URL經由註冊中心傳遞給消費方。

Dubbo常用的配置

啟動檢查

超時配置

重試次數

冪等(設定重試次數,查詢、刪除、修改**) ⾮冪等(不能設定,新增)

多版本分組

六、api 和spi

Spi:呼叫方來定製介面,實現方來針對介面實現不同的實現。呼叫方來選擇自己需要的實現方法。

讓我帶你弄明白什麼是RPC,幫你理清你的思路

常見的spi:

資料庫驅動

日誌log

dubbo擴充套件點開發

Springboot自動裝配機

JDK中的spi實現方式

讓我帶你弄明白什麼是RPC,幫你理清你的思路

原理:

在ServiceLoader的load⽅法中⾸先會獲取上下⽂類載入器,然後構造⼀個ServiceLoader,在 ServiceLoader中有⼀個懶載入器,懶載入器會透過BufferedReader來從META-INF/services路徑 下讀取 對應的接⼝名的全路徑名⽂件,也就是我們配置的⽂件,然後透過⽂件的類解析器讀取⽂件中的內 容,再透過類載入器載入類的全路徑。

缺點:

無法按需載入;

不具有IOC的功能;

serviceLoader不是執行緒安全的,會出現執行緒安全問題

dubbo中的spi實現方式

man=dubbo。impl。Man

woman=dubbo。impl。Woman

讓我帶你弄明白什麼是RPC,幫你理清你的思路

可以按需載入,可擴充套件的能力

七、執行緒派發模型

1、配置

讓我帶你弄明白什麼是RPC,幫你理清你的思路

Netty

在netty中存在兩種執行緒:boss執行緒和worker執行緒

1)boss執行緒

作用:accept客戶端的連線:將接收到的連線註冊到一個worker執行緒上。

通常情況下,服務端每繫結一個埠,開啟一個boss執行緒

2)worker執行緒

作用:處理註冊在其身上的連線connection上的各種io事件

個數:核數+1

注意:一個worker執行緒可以註冊多個connection;

⼀個connection只能註冊在⼀個worker執行緒上

八、執行緒池的5種派發策略

預設是all:所有訊息都派發到執行緒池,包括請求,響應,連線事件,斷開事件,⼼跳等。 即 worker執行緒接收到事件後,將該事件提交到業務執行緒池中,⾃⼰再去處理其他事。

direct

:worker執行緒接收到事件後,由worker執⾏到底(所有訊息都不派發到執行緒池,全部在 Io執行緒上直接執⾏)。

message

:只有請求響應訊息派發到執行緒池,其它連線斷開事件,⼼跳等訊息,直接在 IO線 程上執⾏

execution:

只請求訊息派發到執行緒池,不含響應(客戶端執行緒池),響應和其它連線斷開事 件,⼼跳等訊息,直接在 IO 執行緒上執⾏

connection:

在 IO 執行緒上,將連線斷開事件放⼊佇列,有序逐個執⾏,其它訊息派發到執行緒

dubbo4個常用的threadpool:

fixed 固定⼤⼩執行緒池,啟動時建⽴執行緒,不關閉,⼀直持有。

cached 快取執行緒池,空閒⼀分鐘⾃動刪除,需要時重建。

limited 可伸縮執行緒池,但池中的執行緒數只會增⻓不會收縮。只增⻓不收縮的⽬的是為了避免 收縮時突然來了⼤流量引起的效能問題。

eager 優先建立Worker執行緒池。在任務數量⼤於corePoolSize但是⼩於maximumPoolSize 時,優先建立Worker來處理任務。當任務數量⼤於maximumPoolSize時,將任務放⼊阻塞佇列中。阻塞佇列充滿時丟擲RejectedExecutionException。(相⽐於cached:cached在任務數量超過maximumPoolSize時直接丟擲異常⽽不是將任務放⼊阻塞佇列。

Dubbo的執行緒派發模型

讓我帶你弄明白什麼是RPC,幫你理清你的思路

整體步驟:(受限於派發策略,以預設的all為例)

客戶端的主執行緒發出⼀個請求後獲得future,在執⾏get時進⾏阻塞等待;

服務端使⽤worker執行緒(netty通訊模型)接收到請求後,將請求提交到server執行緒池中進⾏處理!

server執行緒處理完成之後,將相應結果返回給客戶端的worker執行緒池(netty通訊模型),最後,worker執行緒將響應結果提交到client執行緒池進⾏處理!

client執行緒將響應結果填充到future中,然後喚醒等待的主執行緒,主執行緒獲取結果,返回給客戶端。

讓我帶你弄明白什麼是RPC,幫你理清你的思路

如果文章有錯的地方歡迎指正,大家可以在評論區互相交流。

關注我,來個贊 麼麼噠!