RPC核心原理

什麼是RPC

當一個系統並的龐大的時候就會對服務進行拆分。假如一個電商系統就會給拆分爲用戶服務,訂單服務,商品服務等。而在給用戶提供服務的時候這些服務之間經常會需要協作,需要傳遞一些數據。有很多種解決方案,而RPC是一種使用非常廣的方案。
一句話概括,RPC就是一種解決服務之間協作,用來傳遞消息的一種技術方案。

RPC通信流程

RPC分爲服務提供方和服務調用方。 服務提供方和服務消息方之間傳遞信息需要通過網絡傳遞。網絡中傳遞信息都是二進制的,因此我們需要在服務提供方將java對象轉換成二進制文件,在通過網絡傳遞到服務提供方,服務提供方將二進制文件轉換成java對象來進行數據處理。這個將java對象轉成二進制叫做序列化,將二進制轉成java對象的過程就叫做反序列化。

在這裏插入圖片描述

RPC中的協議

在數據序列化的過程中,是需要使用協議的。因爲服務之間序列化的技術有很多種,如JSON,thrift等等, 服務需要知道使用是哪種技術來將二進制數據轉換成java對象。
我們可能還會在協議中放協議長度,協議標示,消息ID,消息類型等等參數。因此我們可以將協議設計成以面的這種方式。
在這裏插入圖片描述
圖中說的魔術位是指這是什麼協議,如DUBBO,HTTP,REDIS等等協議。

下圖是傳遞過程
在這裏插入圖片描述

RPC中的網絡通信技術

在服務調用方和服務提供方之間會建立一個長連接,而在java中處理長連接的較好方式就是使用NIO多路複用,java原生的NIO的API並不是很友好,因此大多數的RPC都是採用的netty來實現的。 netty就是對nio進行了封裝。

服務之間傳輸的二進制文件有可能不是一次性傳輸完成的,因此就需要多次接收然後在拼接。 這個過程是在應用的內存空間進行的,這有可能會造成數據的多次移動,複製等等操作,浪費了服務器資源。 因此netty使用了零拷貝技術。大體過程如下:
在這裏插入圖片描述

RPC中的動態代理

在我們使用RPC的時候會發現他和普通的接口使用方式是一樣的,由其是在配合spring的時候。都是注入一個接口bean然後直接調用方法。 這背後的原理就是動態代理。 RPC會自動給接口生成一個代理類,項目運行過程中實際綁定的是這個接口生成的代理類。這樣在接口方法被調用的時候, 它實際上就會被攔截到。這樣就可以在生成的代理類中添加遠程調用邏輯了。

大家可以看下圖,圖分兩部分, 上部分爲開發人員的視角,下部分爲rpc的視角。
在這裏插入圖片描述
動態代理的實現技術有很多,JDK原生就有動態代理。當然也可以使用別的技術如:Byte Buddy。

RPC的架構設計

RPC的架構圖
在這裏插入圖片描述
在這裏插入圖片描述
主要分爲可以擴展和不可擴展的。可擴展的是將所以功能插件化。dubbo也是插件化的,對java自帶的插件機制進行了擴展,讓其可以自動注入依賴類。

RPC使用的服務發現

註冊中心的作用

服務的調用方和服務提供方有可能不是在一臺機子上,也有可能一個服務調用方會調用多個服務提供方,那服務調用方怎麼獲取服務提高方的地址來發起調用呢? 這個時候就需要有註冊中心了。

服務註冊:在服務提供方啓動的時候,將對外暴露的接口註冊到註冊中心之中,註冊中心將這個服務節點的IP和接口保存下來。

服務訂閱:在服務調用方啓動的時候,去註冊中心查找並訂閱提供方的IP,然後緩存到本地,並用於後續的遠程調用。

在這裏插入圖片描述

註冊中心選擇

使用zookeeper做爲註冊中心一種選擇比較廣泛的應用,zk+dubbo很多公司都會這麼使用。
zk搭建的註冊中心集羣是強一制性的,就是CP,如果master節點出現問題,集羣會重新選舉出一個master節點,在這其間集羣是沒有辦法使用的。不能接受請求。

zookeeper除了做爲註冊中心使用還是可以做爲配置中心使用的。
zookeeper還可以實現分佈式鎖。
zookeeper還是hadoop生態圈中的重要一員。

使用eureka也是一個種選擇,他是最終一至性的集羣(AP)。 是用java語言寫的, 分爲服務端和客戶端, 有自我保護功能, 如果在短時間內,發現很多節點都無法心跳檢查,他會懷疑是網絡問題,就不會在註冊中心把註冊的服務刪除掉。 服務調用方可以正常調用。

現在更流程第二種方式,使用最終一致性來做爲註冊中心。

RPC的健康檢查

這裏的健康檢查是指心跳機制,RPC的服務調用方和服務提供方會建議長連接,調用方會訂時給提供方發送請求,查看調用方的狀態。

調用方會計算出一個可用率,這個可用率低於一定的值時就會將這個服務提供方的地址在自己的內存中標記爲不可用。 調用方會隔一段時間在次檢查這個地址, 如果可用率達到標準就會調整爲正常狀態。 否則進入死亡狀態。
可用率的計算是某一個時間窗口內接口調用成功次數的百分比(成功次數/總調用次數)

在這裏插入圖片描述

RPC中的路由選擇

RPC內部可以配置規則, 調用方發起請求的時候,可以根據請求參數的規則來判斷選擇哪一個提供方。
可以通過註冊中心將規則下發到調用方。 在這裏插入圖片描述
RPC路由功能的一個典型應用場景就是灰度發佈,我們可以通過路由功能完成定點調用,黑名單白名單等等功能。

RPC中的負載均衡

RPC中的負載均衡是放在調用方的,每一次發起RPC調用,服務調用者都會通過配置的負載均衡插件,自主選擇一個服務節點,發起RPC調用。
在這裏插入圖片描述

RPC調用方會有一個打分機制, 通過心跳機制將調用方的系統的基礎信息獲取到。如負載指標 ,CPU核心數,內存大小,請求處理的耗時,節點的健康狀態等等。根據這些信息就可以判斷選擇一個調用方。還可以一個百分比的值, 得分高的就分配流量多一些,得分低的就分配流量低一些,還可以單純的隨機,輪循。
在這裏插入圖片描述

RPC中的異常重試

RPC的異常重試功能仍然是在調用方實現的,用戶可以自行設置是否開啓重試以及重試次數。
在這裏插入圖片描述
RPC會捕捉特定的異常,如:網絡超時異常,網絡連接異常等等。一定捕捉了這個異常就會重新調用請求。 當然,服務提供方的接口一定要做好冪等性,哪果沒有做好冪等性就會造成髒數據。
RPC還有一個請求超時的概念,如果服務提供方的接口性能特別慢,雖然可以影響但是超過時間了RPC的調用方也不會等待而是直接返回一個請求超時的異常。

如果我們把請求超時時間設置爲5s,請求重試次數是3次,每一次都耕時兩秒,是不是會導致請求超時,雖然第三次可以正常返回結果,但是服務調用方仍然會返回一個超時異常。
爲了解決這個問題, 我們可每一次重試的時候都會把這次請求的時間重置爲0。 也就是說請求超時時間計算的請求成功的一次時間,而不是重試的所有請求的時間。

RPC關閉服務

RPC在關閉服務的時候,是不可以直接強制關閉的,因爲如果有還沒有處理完的請求還需要處理完,同時不能在接受新的請求了。 Runtime.addShutdownHook方法。
下面這個圖就是關閉服務的流程,不只是RPC的關閉流程是這樣的, 一些中間件如消息隊列和緩存的關閉流程大體都是這樣的。
在這裏插入圖片描述

一般實現這種功能的方式都是使用java自帶的關閉服務的勾子。

RPC啓動流程

有一個叫做啓動預熱的概念:簡單來說,就是讓剛啓動的服務提供方的流程減少,被調用的次數隨着時間增加慢慢增長到正常值。

RPC調用方可以通過註冊中心拿到服務提供方的註冊時間,然後根據這個啓動時間來計算一個權重,剛剛啓動的時候流量低。這個實現可以添加到負載均衡當中。也可以獨立出來。
在這裏插入圖片描述

如果RPC和spring一起使用的時候,也許會發現啓動的速度會有一些慢, 如果這個時間先啓動了rpc,就會信息註冊到註冊中心了,就會接到請求。實際上這個時候spring還沒有完成啓動, 這個時候就需要延時暴露,只有當服務完成啓動的時候纔會將信息註冊到註冊中心。

我們可以在服務提供方應用啓動後,接口註冊到註冊中心前,預留一個Hook過程 ,讓用戶可實現可擴展的Hook邏輯。用戶可以在Hook裏面模擬調用邏輯,從而使用jvm指令能夠預熱起來。並且啓用可以在Hook裏面事先預加載一些資源。只有等資源都加載完成後,最後才把接口註冊到註冊中心。
在這裏插入圖片描述

引用

以上內容爲極客時間《RPC實戰與核心原理》的學習所得。

交個朋友好嗎?

以上內容均爲讀書所得, 更多有趣有料的科技資訊請關注公衆號。(交個朋友)
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章