溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

<微服務(wù)架構(gòu)>—RPC入門篇

發(fā)布時(shí)間:2020-07-25 16:26:10 來(lái)源:網(wǎng)絡(luò) 閱讀:326 作者:AltBoy 欄目:編程語(yǔ)言

概念

RPC(Remote Procedure Call):遠(yuǎn)程過(guò)程調(diào)用,它是一種通過(guò)網(wǎng)絡(luò)從遠(yuǎn)程計(jì)算機(jī)程序上請(qǐng)求服務(wù),而不需要了解底層網(wǎng)絡(luò)技術(shù)的思想.RPC 是一種技術(shù)思想而非一種規(guī)范或協(xié)議,常見(jiàn) RPC 技術(shù)和框架有:

  • 應(yīng)用級(jí)的服務(wù)框架:阿里的 Dubbo/Dubbox、Google gRPC、Spring Boot/Spring Cloud
  • 遠(yuǎn)程通信協(xié)議:RMI、Socket、SOAP(HTTP XML)、REST(HTTP JSON)
  • 通信框架:MINA 和 Netty

目前流行的開(kāi)源 RPC 框架還是比較多的,有阿里巴巴的 Dubbo、Facebook 的 Thrift、Google 的 gRPC、Twitter 的 Finagle 等。

  • gRPC:是 Google 公布的開(kāi)源軟件,基于HTTP 2.0的協(xié)議,并支持常見(jiàn)的眾多編程語(yǔ)言。RPC 框架是基于 HTTP 協(xié)議實(shí)現(xiàn)的,底層使用到了 Netty 框架的支持。
  • Thrift:是 Facebook 的開(kāi)源 RPC 框架,主要是一個(gè)跨語(yǔ)言的服務(wù)開(kāi)發(fā)框架。
    用戶只要在其之上進(jìn)行二次開(kāi)發(fā)就行,應(yīng)用對(duì)于底層的 RPC 通訊等都是透明的。不過(guò)這個(gè)對(duì)于用戶來(lái)說(shuō)需要學(xué)習(xí)特定領(lǐng)域語(yǔ)言這個(gè)特性,還是有一定成本的。

  • Dubbo:是阿里集團(tuán)開(kāi)源的一個(gè)極為出名的 RPC 框架,在很多互聯(lián)網(wǎng)公司和企業(yè)應(yīng)用中廣泛使用。協(xié)議和序列化框架都可以插拔是極其鮮明的特色。

完整的RPC框架

在一個(gè)典型 RPC 的使用場(chǎng)景中,包含了服務(wù)發(fā)現(xiàn)、負(fù)載、容錯(cuò)、網(wǎng)絡(luò)傳輸、序列化等組件,其中“RPC 協(xié)議”就指明了程序如何進(jìn)行網(wǎng)絡(luò)傳輸和序列化

<微服務(wù)架構(gòu)>—RPC入門篇

RPC核心功能

RPC 的核心功能是指實(shí)現(xiàn)一個(gè) RPC 最重要的功能模塊,就是上圖中的”RPC 協(xié)議”部分:

<微服務(wù)架構(gòu)>—RPC入門篇

下面分別介紹核心 RPC 框架的重要組成:

  • 客戶端(Client):服務(wù)調(diào)用方。
  • 客戶端存根(Client Stub):存放服務(wù)端地址信息,將客戶端的請(qǐng)求參數(shù)數(shù)據(jù)信息打包成網(wǎng)絡(luò)消息,再通過(guò)網(wǎng)絡(luò)傳輸發(fā)送給服務(wù)端。
  • 服務(wù)端存根(Server Stub):接收客戶端發(fā)送過(guò)來(lái)的請(qǐng)求消息并進(jìn)行解包,然后再調(diào)用本地服務(wù)進(jìn)行處理。
  • 服務(wù)端(Server):服務(wù)的真正提供者。
  • Network Service:底層傳輸,可以是 TCP 或 HTTP。

一次 RPC 調(diào)用流程如下:

  • 服務(wù)消費(fèi)者(Client 客戶端)通過(guò)本地調(diào)用的方式調(diào)用服務(wù)。
  • 客戶端存根(Client Stub)接收到調(diào)用請(qǐng)求后負(fù)責(zé)將方法、入?yún)⒌刃畔⑿蛄谢?組裝)成能夠進(jìn)行網(wǎng)絡(luò)傳輸?shù)南Ⅲw。
  • 客戶端存根(Client Stub)找到遠(yuǎn)程的服務(wù)地址,并且將消息通過(guò)網(wǎng)絡(luò)發(fā)送給服務(wù)端。
  • 服務(wù)端存根(Server Stub)收到消息后進(jìn)行解碼(反序列化操作)。
  • 服務(wù)端存根(Server Stub)根據(jù)解碼結(jié)果調(diào)用本地的服務(wù)進(jìn)行相關(guān)處理
  • 服務(wù)端(Server)本地服務(wù)業(yè)務(wù)處理。
  • 處理結(jié)果返回給服務(wù)端存根(Server Stub)。
  • 服務(wù)端存根(Server Stub)序列化結(jié)果。
  • 服務(wù)端存根(Server Stub)將結(jié)果通過(guò)網(wǎng)絡(luò)發(fā)送至消費(fèi)方。
  • 客戶端存根(Client Stub)接收到消息,并進(jìn)行解碼(反序列化)。
  • 服務(wù)消費(fèi)方得到最終結(jié)果。

RPC核心之功能實(shí)現(xiàn)

接口調(diào)用通常包含兩個(gè)部分,序列化和通信協(xié)議。常見(jiàn)的序列化協(xié)議包括json、xml、hession、protobuf、thrift、text、bytes等;通信比較流行的是http、soap、websockect,RPC通?;赥CP實(shí)現(xiàn)。那么restful使用的序列化協(xié)議通常是json,通信協(xié)議是http;rpc是一種通信協(xié)議,因此如果序列化使用json的話,那么就是json-rpc

RPC 的核心功能主要由 5 個(gè)模塊組成,如果想要自己實(shí)現(xiàn)一個(gè) RPC,最簡(jiǎn)單的方式要實(shí)現(xiàn)三個(gè)技術(shù)點(diǎn),分別是:

  • 服務(wù)尋址
  • 數(shù)據(jù)流的序列化和反序列化
  • 網(wǎng)絡(luò)傳輸
服務(wù)尋址

服務(wù)尋址可以使用 Call ID 映射。在本地調(diào)用中,函數(shù)體是直接通過(guò)函數(shù)指針來(lái)指定的,但是在遠(yuǎn)程調(diào)用中,函數(shù)指針是不行的,因?yàn)閮蓚€(gè)進(jìn)程的地址空間是完全不一樣的。

所以在 RPC 中,所有的函數(shù)都必須有自己的一個(gè) ID。這個(gè) ID 在所有進(jìn)程中都是唯一確定的。

客戶端在做遠(yuǎn)程過(guò)程調(diào)用時(shí),必須附上這個(gè) ID。然后我們還需要在客戶端和服務(wù)端分別維護(hù)一個(gè)函數(shù)和Call ID的對(duì)應(yīng)表。

當(dāng)客戶端需要進(jìn)行遠(yuǎn)程調(diào)用時(shí),它就查一下這個(gè)表,找出相應(yīng)的 Call ID,然后把它傳給服務(wù)端,服務(wù)端也通過(guò)查表,來(lái)確定客戶端需要調(diào)用的函數(shù),然后執(zhí)行相應(yīng)函數(shù)的代碼。

實(shí)現(xiàn)方式:服務(wù)注冊(cè)中心。

Registry(服務(wù)發(fā)現(xiàn)):借助 JNDI 發(fā)布并調(diào)用了 RMI 服務(wù)。實(shí)際上,JNDI 就是一個(gè)注冊(cè)表,服務(wù)端將服務(wù)對(duì)象放入到注冊(cè)表中,客戶端從注冊(cè)表中獲取服務(wù)對(duì)象。

RMI 服務(wù)在服務(wù)端實(shí)現(xiàn)之后需要注冊(cè)到 RMI Server 上,然后客戶端從指定的 RMI 地址上 Lookup 服務(wù),調(diào)用該服務(wù)對(duì)應(yīng)的方法即可完成遠(yuǎn)程方法調(diào)用。

Registry 是個(gè)很重要的功能,當(dāng)服務(wù)端開(kāi)發(fā)完服務(wù)之后,要對(duì)外暴露,如果沒(méi)有服務(wù)注冊(cè),則客戶端是無(wú)從調(diào)用的,即使服務(wù)端的服務(wù)就在那里

序列化和反序列化

客戶端怎么把參數(shù)值傳給遠(yuǎn)程的函數(shù)呢?在本地調(diào)用中,我們只需要把參數(shù)壓到棧里,然后讓函數(shù)自己去棧里讀就行。

但是在遠(yuǎn)程過(guò)程調(diào)用時(shí),客戶端跟服務(wù)端是不同的進(jìn)程,不能通過(guò)內(nèi)存來(lái)傳遞參數(shù)。

這時(shí)候就需要客戶端把參數(shù)先轉(zhuǎn)成一個(gè)字節(jié)流,傳給服務(wù)端后,再把字節(jié)流轉(zhuǎn)成自己能讀取的格式。

只有二進(jìn)制數(shù)據(jù)才能在網(wǎng)絡(luò)中傳輸,序列化和反序列化的定義是:

  • 將對(duì)象轉(zhuǎn)換成二進(jìn)制流的過(guò)程叫做序列化
  • 將二進(jìn)制流轉(zhuǎn)換成對(duì)象的過(guò)程叫做反序列化

這個(gè)過(guò)程叫序列化和反序列化。同理,從服務(wù)端返回的值也需要序列化反序列化的過(guò)程。

網(wǎng)絡(luò)傳輸

網(wǎng)絡(luò)傳輸:遠(yuǎn)程調(diào)用往往用在網(wǎng)絡(luò)上,客戶端和服務(wù)端是通過(guò)網(wǎng)絡(luò)連接的。

所有的數(shù)據(jù)都需要通過(guò)網(wǎng)絡(luò)傳輸,因此就需要有一個(gè)網(wǎng)絡(luò)傳輸層。網(wǎng)絡(luò)傳輸層需要把 Call ID 和序列化后的參數(shù)字節(jié)流傳給服務(wù)端,然后再把序列化后的調(diào)用結(jié)果傳回客戶端。

只要能完成這兩者的,都可以作為傳輸層使用。因此,它所使用的協(xié)議其實(shí)是不限的,能完成傳輸就行。

盡管大部分 RPC 框架都使用 TCP 協(xié)議,但其實(shí) UDP 也可以,而 gRPC 干脆就用了 HTTP2。

TCP 的連接是最常見(jiàn)的,簡(jiǎn)要分析基于 TCP 的連接:通常 TCP 連接可以是按需連接(需要調(diào)用的時(shí)候就先建立連接,調(diào)用結(jié)束后就立馬斷掉),也可以是長(zhǎng)連接(客戶端和服務(wù)器建立起連接之后保持長(zhǎng)期持有,不管此時(shí)有無(wú)數(shù)據(jù)包的發(fā)送,可以配合心跳檢測(cè)機(jī)制定期檢測(cè)建立的連接是否存活有效),多個(gè)遠(yuǎn)程過(guò)程調(diào)用共享同一個(gè)連接。

所以,要實(shí)現(xiàn)一個(gè) RPC 框架,只需要把以下三點(diǎn)實(shí)現(xiàn)了就基本完成了:

  • Call ID 映射:可以直接使用函數(shù)字符串,也可以使用整數(shù) ID。映射表一般就是一個(gè)哈希表。
  • 序列化反序列化:可以自己寫(xiě),也可以使用 Protobuf 或者 FlatBuffers 之類的。
  • 網(wǎng)絡(luò)傳輸庫(kù):可以自己寫(xiě) Socket,或者用 Asio,ZeroMQ,Netty 之類。

RPC 核心之網(wǎng)絡(luò)傳輸協(xié)議

在 RPC 中可選的網(wǎng)絡(luò)傳輸方式有多種,可以選擇 TCP 協(xié)議、UDP 協(xié)議、HTTP 協(xié)議。

基于 TCP 協(xié)議的 RPC 調(diào)用

由服務(wù)的調(diào)用方與服務(wù)的提供方建立 Socket 連接,并由服務(wù)的調(diào)用方通過(guò) Socket 將需要調(diào)用的接口名稱、方法名稱和參數(shù)序列化后傳遞給服務(wù)的提供方,服務(wù)的提供方反序列化后再利用反射調(diào)用相關(guān)的方法。

將結(jié)果返回給服務(wù)的調(diào)用方,整個(gè)基于 TCP 協(xié)議的 RPC 調(diào)用大致如此。

但是在實(shí)例應(yīng)用中則會(huì)進(jìn)行一系列的封裝,如 RMI 便是在 TCP 協(xié)議上傳遞可序列化的 Java 對(duì)象。

基于 HTTP 協(xié)議的 RPC 調(diào)用

該方法更像是訪問(wèn)網(wǎng)頁(yè)一樣,只是它的返回結(jié)果更加單一簡(jiǎn)單。

其大致流程為:由服務(wù)的調(diào)用者向服務(wù)的提供者發(fā)送請(qǐng)求,這種請(qǐng)求的方式可能是 GET、POST、PUT、DELETE 等中的一種,服務(wù)的提供者可能會(huì)根據(jù)不同的請(qǐng)求方式做出不同的處理,或者某個(gè)方法只允許某種請(qǐng)求方式。

而調(diào)用的具體方法則是根據(jù) URL 進(jìn)行方法調(diào)用,而方法所需要的參數(shù)可能是對(duì)服務(wù)調(diào)用方傳輸過(guò)去的 XML 數(shù)據(jù)或者 JSON 數(shù)據(jù)解析后的結(jié)果,返回 JOSN 或者 XML 的數(shù)據(jù)結(jié)果。

由于目前有很多開(kāi)源的 Web 服務(wù)器,如 Tomcat,所以其實(shí)現(xiàn)起來(lái)更加容易,就像做 Web 項(xiàng)目一樣

兩種方式對(duì)比

基于 TCP 的協(xié)議實(shí)現(xiàn)的 RPC 調(diào)用,由于 TCP 協(xié)議處于協(xié)議棧的下層,能夠更加靈活地對(duì)協(xié)議字段進(jìn)行定制,減少網(wǎng)絡(luò)開(kāi)銷,提高性能,實(shí)現(xiàn)更大的吞吐量和并發(fā)數(shù)。

但是需要更多關(guān)注底層復(fù)雜的細(xì)節(jié),實(shí)現(xiàn)的代價(jià)更高。同時(shí)對(duì)不同平臺(tái),如安卓,iOS 等,需要重新開(kāi)發(fā)出不同的工具包來(lái)進(jìn)行請(qǐng)求發(fā)送和相應(yīng)解析,工作量大,難以快速響應(yīng)和滿足用戶需求。

基于 HTTP 協(xié)議實(shí)現(xiàn)的 RPC 則可以使用 JSON 和 XML 格式的請(qǐng)求或響應(yīng)數(shù)據(jù)。

而 JSON 和 XML 作為通用的格式標(biāo)準(zhǔn)(使用 HTTP 協(xié)議也需要序列化和反序列化,不過(guò)這不是該協(xié)議下關(guān)心的內(nèi)容,成熟的 Web 程序已經(jīng)做好了序列化內(nèi)容),開(kāi)源的解析工具已經(jīng)相當(dāng)成熟,在其上進(jìn)行二次開(kāi)發(fā)會(huì)非常便捷和簡(jiǎn)單。

但是由于 HTTP 協(xié)議是上層協(xié)議,發(fā)送包含同等內(nèi)容的信息,使用 HTTP 協(xié)議傳輸所占用的字節(jié)數(shù)會(huì)比使用 TCP 協(xié)議傳輸所占用的字節(jié)數(shù)更高。

因此在同等網(wǎng)絡(luò)下,通過(guò) HTTP 協(xié)議傳輸相同內(nèi)容,效率會(huì)比基于 TCP 協(xié)議的數(shù)據(jù)效率要低,信息傳輸所占用的時(shí)間也會(huì)更長(zhǎng),當(dāng)然壓縮數(shù)據(jù),能夠縮小這一差距。

使用 RabbitMQ 的 RPC 架構(gòu)

在 OpenStack 中服務(wù)與服務(wù)之間使用 RESTful API 調(diào)用,而在服務(wù)內(nèi)部則使用 RPC 調(diào)用各個(gè)功能模塊。

正是由于使用了 RPC 來(lái)解耦服務(wù)內(nèi)部功能模塊,使得 OpenStack 的服務(wù)擁有擴(kuò)展性強(qiáng),耦合性低等優(yōu)點(diǎn)。

OpenStack 的 RPC 架構(gòu)中,加入了消息隊(duì)列 RabbitMQ,這樣做的目的是為了保證 RPC 在消息傳遞過(guò)程中的安全性和穩(wěn)定性。

下面分析 OpenStack 中使用 RabbitMQ 如何實(shí)現(xiàn) RPC 的調(diào)用。

使用 RabbitMQ 的好處:

  • 同步變異步:可以使用線程池將同步變成異步,但是缺點(diǎn)是要自己實(shí)現(xiàn)線程池,并且強(qiáng)耦合。使用消息隊(duì)列可以輕松將同步請(qǐng)求變成異步請(qǐng)求。
  • 低內(nèi)聚高耦合:解耦,減少?gòu)?qiáng)依賴。
  • 流量削峰:通過(guò)消息隊(duì)列設(shè)置請(qǐng)求值,超過(guò)閥值的拋棄或者轉(zhuǎn)到錯(cuò)誤界面。
  • 網(wǎng)絡(luò)通信性能提高:TCP 的創(chuàng)建和銷毀開(kāi)銷大,創(chuàng)建 3 次握手,銷毀 4 次分手,高峰時(shí)成千上萬(wàn)條的鏈接會(huì)造成資源的巨大浪費(fèi),而且操作系統(tǒng)每秒處理 TCP 的數(shù)量也是有數(shù)量限制的,必定造成性能瓶頸。
  • RabbitMQ 采用信道通信,不采用 TCP 直接通信。一條線程一條信道,多條線程多條信道,公用一個(gè) TCP 連接。

簡(jiǎn)單對(duì)比 RPC 和 Restful API

REST API的幾個(gè)特點(diǎn)為:
  • 資源:就是網(wǎng)絡(luò)上的一個(gè)實(shí)體,或者說(shuō)是網(wǎng)絡(luò)上的一個(gè)具體信息。它可以是一段文本、一張圖片、一首歌曲、一種服務(wù),就是一個(gè)具體的實(shí)在
  • 統(tǒng)一接口:RESTful 架構(gòu)風(fēng)格規(guī)定,數(shù)據(jù)的元操作,即 CRUD(Create,Read,Update 和 Delete,即數(shù)據(jù)的增刪查改)操作,分別對(duì)應(yīng)于 HTTP 方法:GET 用來(lái)獲取資源,POST 用來(lái)新建資源(也可以用于更新資源),PUT 用來(lái)更新資源,DELETE 用來(lái)刪除資源,這樣就統(tǒng)一了數(shù)據(jù)操作的接口,僅通過(guò) HTTP 方法,就可以完成對(duì)數(shù)據(jù)的所有增刪查改工作。
  • URL:可以用一個(gè) URI(統(tǒng)一資源定位符)指向資源,即每個(gè) URI 都對(duì)應(yīng)一個(gè)特定的資源。
    要獲取這個(gè)資源,訪問(wèn)它的 URI 就可以,因此 URI 就成了每一個(gè)資源的地址或識(shí)別符
  • 無(wú)狀態(tài):所謂無(wú)狀態(tài)的,即所有的資源,都可以通過(guò) URI 定位,而且這個(gè)定位與其他資源無(wú)關(guān),也不會(huì)因?yàn)槠渌Y源的變化而改變
RPC 和 Restful API 對(duì)比
  • 面對(duì)對(duì)象不同:RPC 更側(cè)重于動(dòng)作,REST 的主體是資源
  • 傳輸效率:RPC 效率更高。RPC,使用自定義的 TCP 協(xié)議,可以讓請(qǐng)求報(bào)文體積更小,或者使用 HTTP2 協(xié)議,也可以很好的減少報(bào)文的體積,提高傳輸效率
  • 復(fù)雜度:RPC 實(shí)現(xiàn)復(fù)雜,流程繁瑣,REST 調(diào)用及測(cè)試都很方便
  • 靈活性:HTTP 相對(duì)更規(guī)范,更標(biāo)準(zhǔn),更通用,無(wú)論哪種語(yǔ)言都支持 HTTP 協(xié)議;RPC 可以實(shí)現(xiàn)跨語(yǔ)言調(diào)用,但整體靈活性不如 RESTful

總結(jié)

RPC 主要用于公司內(nèi)部的服務(wù)調(diào)用,性能消耗低,傳輸效率高,實(shí)現(xiàn)復(fù)雜

RESTful API 主要用于對(duì)外的異構(gòu)環(huán)境,瀏覽器接口調(diào)用,App 接口調(diào)用,第三方接口調(diào)用等

RPC 使用場(chǎng)景(大型的網(wǎng)站,內(nèi)部子系統(tǒng)較多、接口非常多的情況下適合使用 RPC):

  • 長(zhǎng)鏈接。不必每次通信都要像 HTTP 一樣去 3 次握手,減少了網(wǎng)絡(luò)開(kāi)銷。
  • 注冊(cè)發(fā)布機(jī)制。RPC 框架一般都有注冊(cè)中心,有豐富的監(jiān)控管理;發(fā)布、下線接口、動(dòng)態(tài)擴(kuò)展等,對(duì)調(diào)用方來(lái)說(shuō)是無(wú)感知、統(tǒng)一化的操作。
  • 安全性,沒(méi)有暴露資源操作。
  • 微服務(wù)支持。就是最近流行的服務(wù)化架構(gòu)、服務(wù)化治理,RPC 框架是一個(gè)強(qiáng)力的支撐。
向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI