花了一個(gè)星期福贞,我終于把RPC框架整明白了!

轉(zhuǎn)載http://developer.51cto.com/art/201906/597963.htm
【51CTO.com原創(chuàng)稿件】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 等。

下面重點(diǎn)介紹三種:

  • 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ā)框架旺订。

用戶(hù)只要在其之上進(jìn)行二次開(kāi)發(fā)就行弄企,應(yīng)用對(duì)于底層的 RPC 通訊等都是透明的。不過(guò)這個(gè)對(duì)于用戶(hù)來(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ò)傳輸和序列化。

圖 1:完整 RPC 架構(gòu)圖

如下是 Dubbo 的設(shè)計(jì)架構(gòu)圖晦闰,分層清晰放祟,功能復(fù)雜:

圖 2:Dubbo 架構(gòu)圖

RPC 核心功能

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

圖 3:RPC 核心功能

一個(gè) RPC 的核心功能主要有 5 個(gè)部分組成呻右,分別是:客戶(hù)端跪妥、客戶(hù)端 Stub、網(wǎng)絡(luò)傳輸模塊声滥、服務(wù)端 Stub眉撵、服務(wù)端等。

圖 4:RPC 核心功能圖

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

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

Python 自帶 RPC Demo

Server.py:

<pre style="padding: 0px; margin: 0px 0px 1em; font-family: "Courier New", monospace; font-size: 12px; width: 1194.92px; overflow: auto; background: rgb(230, 230, 230); color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">

  1. from SimpleXMLRPCServer import SimpleXMLRPCServer
  2. def fun_add(a,b):
  3. totle = a + b
  4. return totle
  5. if name == 'main':
  6. s = SimpleXMLRPCServer(('0.0.0.0', 8080)) #開(kāi)啟xmlrpcserver
  7. s.register_function(fun_add) #注冊(cè)函數(shù)fun_add
  8. print "server is online..."
  9. s.serve_forever() #開(kāi)啟循環(huán)等待

</pre>

Client.py:

<pre style="padding: 0px; margin: 0px 0px 1em; font-family: "Courier New", monospace; font-size: 12px; width: 1194.92px; overflow: auto; background: rgb(230, 230, 230); color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">

  1. from xmlrpclib import ServerProxy #導(dǎo)入xmlrpclib的包
  2. s = ServerProxy("http://172.171.5.205:8080") #定義xmlrpc客戶(hù)端
  3. print s.fun_add(2,3)

</pre>

開(kāi)啟服務(wù)端:

開(kāi)啟客戶(hù)端:

Wireshark 抓包分析過(guò)程

客戶(hù)端去往服務(wù)端:

  • 客戶(hù)端 IP:172.171.4.176
  • 服務(wù)端 IP:172.171.5.95

通信使用 HTTP 協(xié)議晦款,XML 文件傳輸格式炎功。傳輸?shù)淖侄伟ǎ悍椒?methodName,兩個(gè)參數(shù) 2柬赐,3亡问。

圖 5:Request 抓包

服務(wù)端返回結(jié)果,字段返回值 Value肛宋,結(jié)果是 5:

圖 6:Response 抓包

在這兩次網(wǎng)絡(luò)傳輸中使用了 HTTP 協(xié)議州藕,建立 HTTP 協(xié)議之間有 TCP 三次握手,斷開(kāi) HTTP 協(xié)議時(shí)有 TCP 四次揮手酝陈。

圖 7:基于 HTTP 協(xié)議的 RPC 連接過(guò)程

詳細(xì)調(diào)用過(guò)程

Python 自帶 RPC 的 Demo 小程序的實(shí)現(xiàn)過(guò)程床玻,流程和分工角色可以用下圖來(lái)表示:

圖 8:RPC 調(diào)用詳細(xì)流程圖

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

  • 服務(wù)消費(fèi)者(Client 客戶(hù)端)通過(guò)本地調(diào)用的方式調(diào)用服務(wù)。
  • 客戶(hù)端存根(Client Stub)接收到調(diào)用請(qǐng)求后負(fù)責(zé)將方法沉帮、入?yún)⒌刃畔⑿蛄谢?組裝)成能夠進(jìn)行網(wǎng)絡(luò)傳輸?shù)南Ⅲw锈死。
  • 客戶(hù)端存根(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)方偎行。
  • 客戶(hù)端存根(Client Stub)接收到消息,并進(jìn)行解碼(反序列化)贰拿。
  • 服務(wù)消費(fèi)方得到最終結(jié)果蛤袒。

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

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)程中都是唯一確定的晴及。

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

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

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

要調(diào)用服務(wù)且改,首先你需要一個(gè)服務(wù)注冊(cè)中心去查詢(xún)對(duì)方服務(wù)都有哪些實(shí)例。Dubbo 的服務(wù)注冊(cè)中心是可以配置的板驳,官方推薦使用 Zookeeper又跛。

實(shí)現(xiàn)案例:RMI(Remote Method Invocation,遠(yuǎn)程方法調(diào)用)也就是 RPC 本身的實(shí)現(xiàn)方式若治。

圖 9:RMI 架構(gòu)圖

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

RMI 服務(wù)在服務(wù)端實(shí)現(xiàn)之后需要注冊(cè)到 RMI Server 上,然后客戶(hù)端從指定的 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è)郊供,則客戶(hù)端是無(wú)從調(diào)用的峡碉,即使服務(wù)端的服務(wù)就在那里。

序列化和反序列化

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

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

這時(shí)候就需要客戶(hù)端把參數(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ò)上芽突,客戶(hù)端和服務(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é)果傳回客戶(hù)端钠糊。

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

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

TCP 的連接是最常見(jiàn)的,簡(jiǎn)要分析基于 TCP 的連接:通常 TCP 連接可以是按需連接(需要調(diào)用的時(shí)候就先建立連接云稚,調(diào)用結(jié)束后就立馬斷掉)隧饼,也可以是長(zhǎng)連接(客戶(hù)端和服務(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 之類(lèi)的撞叨。
  • 網(wǎng)絡(luò)傳輸庫(kù):可以自己寫(xiě) Socket金踪,或者用 Asio,ZeroMQ牵敷,Netty 之類(lèi)胡岔。

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

在第三節(jié)中說(shuō)明了要實(shí)現(xiàn)一個(gè) RPC,需要選擇網(wǎng)絡(luò)傳輸?shù)姆绞健?/p>

圖 10:網(wǎng)絡(luò)傳輸

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

每一種協(xié)議對(duì)整體的性能和效率都有不同的影響奕锌,如何選擇一個(gè)正確的網(wǎng)絡(luò)傳輸協(xié)議呢?首先要搞明白各種傳輸協(xié)議在 RPC 中的工作方式。

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

由服務(wù)的調(diào)用方與服務(wù)的提供方建立 Socket 連接村生,并由服務(wù)的調(diào)用方通過(guò) Socket 將需要調(diào)用的接口名稱(chēng)惊暴、方法名稱(chēng)和參數(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)銷(xiāo),提高性能马昙,實(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)和滿(mǎn)足用戶(hù)需求证杭。

基于 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 簡(jiǎn)介

以下摘錄自知乎:

對(duì)于初學(xué)者航邢,舉一個(gè)飯店的例子來(lái)解釋這三個(gè)分別是什么吧。不是***恰當(dāng)骄蝇,但是應(yīng)該足以解釋這三者的區(qū)別膳殷。

RPC:假設(shè)你是一個(gè)飯店里的服務(wù)員,顧客向你點(diǎn)菜九火,但是你不會(huì)做菜赚窃,所以你采集了顧客要點(diǎn)什么之后告訴后廚去做顧客點(diǎn)的菜,這叫 RPC(remote procedure call)岔激,因?yàn)閺N房的廚師相對(duì)于服務(wù)員而言是另外一個(gè)人(在計(jì)算機(jī)的世界里就是 Remote 的機(jī)器上的一個(gè)進(jìn)程)勒极。廚師做好了的菜就是RPC的返回值。

任務(wù)隊(duì)列和消息隊(duì)列:本質(zhì)都是隊(duì)列虑鼎,所以就只舉一個(gè)任務(wù)隊(duì)列的例子辱匿。假設(shè)這個(gè)飯店在高峰期顧客很多,而廚師只有很少的幾個(gè)炫彩,所以服務(wù)員們不得不把單子按下單順序放在廚房的桌子上匾七,供廚師們一個(gè)一個(gè)做,這一堆單子就是任務(wù)隊(duì)列江兢,廚師們每做完一個(gè)菜昨忆,就從桌子上的訂單里再取出一個(gè)單子繼續(xù)做菜。

角色分擔(dān)如下圖:

圖 11:RabbitMQ 在 RPC 中角色

使用 RabbitMQ 的好處:

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

RabbitMQ 采用信道通信褐啡,不采用 TCP 直接通信。一條線程一條信道鳖昌,多條線程多條信道备畦,公用一個(gè) TCP 連接。

一條 TCP 連接可以容納***條信道(硬盤(pán)容量足夠的話)许昨,不會(huì)造成性能瓶頸懂盐。

RabbitMQ 的三種類(lèi)型的交換器

RabbitMQ 使用 Exchange(交換機(jī))和 Queue(隊(duì)列)來(lái)實(shí)現(xiàn)消息隊(duì)列。

在 RabbitMQ 中一共有三種交換機(jī)類(lèi)型糕档,每一種交換機(jī)類(lèi)型都有很鮮明的特征莉恼。

基于這三種交換機(jī)類(lèi)型,OpenStack 完成兩種 RPC 的調(diào)用方式速那。首先簡(jiǎn)單介紹三種交換機(jī)俐银。

圖 12:RabbitMQ 架構(gòu)圖

①?gòu)V播式交換器類(lèi)型(Fanout)

該類(lèi)交換器不分析所接收到消息中的 Routing Key,默認(rèn)將消息轉(zhuǎn)發(fā)到所有與該交換器綁定的隊(duì)列中去端仰。

圖 13:廣播式交換機(jī)

②直接式交換器類(lèi)型(Direct)

該類(lèi)交換器需要精確匹配 Routing Key 與 Binding Key悉患,如消息的 Routing Key = Cloud,那么該條消息只能被轉(zhuǎn)發(fā)至 Binding Key = Cloud 的消息隊(duì)列中去榆俺。

圖 14:直接式交換機(jī)

③主題式交換器(Topic Exchange)

該類(lèi)交換器通過(guò)消息的 Routing Key 與 Binding Key 的模式匹配,將消息轉(zhuǎn)發(fā)至所有符合綁定規(guī)則的隊(duì)列中坞淮。

Binding Key 支持通配符茴晋,其中“*”匹配一個(gè)詞組,“#”匹配多個(gè)詞組(包括零個(gè))回窘。

圖 15:主題式交換機(jī)

注:以上四張圖片來(lái)自博客園诺擅,如有侵權(quán),請(qǐng)聯(lián)系作者:https://www.cnblogs.com/dwlsxj/p/RabbitMQ.html啡直。

當(dāng)生產(chǎn)者發(fā)送消息 Routing Key=F.C.E 的時(shí)候烁涌,這時(shí)候只滿(mǎn)足 Queue1,所以會(huì)被路由到 Queue 中酒觅。

如果 Routing Key=A.C.E 這時(shí)候會(huì)被同時(shí)路由到 Queue1 和 Queue2 中撮执,如果 Routing Key=A.F.B 時(shí),這里只會(huì)發(fā)送一條消息到 Queue2 中舷丹。

Nova 基于 RabbitMQ 實(shí)現(xiàn)兩種 RPC 調(diào)用:

  • RPC.CALL(調(diào)用)
  • RPC.CAST(通知)

其中 RPC.CALL 基于請(qǐng)求與響應(yīng)方式抒钱,RPC.CAST 只是提供單向請(qǐng)求,兩種 RPC 調(diào)用方式在 Nova 中均有典型的應(yīng)用場(chǎng)景。

RPC.CALL

RPC.CALL 是一種雙向通信流程谋币,即 RabbitMQ 接收消息生產(chǎn)者生成的系統(tǒng)請(qǐng)求消息仗扬,消息消費(fèi)者經(jīng)過(guò)處理之后將系統(tǒng)相應(yīng)結(jié)果反饋給調(diào)用程序。

圖 16:RPC.CALL 原理圖

一個(gè)用戶(hù)通過(guò) Dashboard 創(chuàng)建一個(gè)虛擬機(jī)蕾额,界面經(jīng)過(guò)消息封裝后發(fā)送給 NOVA-API早芭。

NOVA-API 作為消息生產(chǎn)者,將該消息以 RPC.CALL 方式通過(guò) Topic 交換器轉(zhuǎn)發(fā)至消息隊(duì)列诅蝶。

此時(shí)退个,Nova-Compute 作為消息消費(fèi)者,接收該信息并通過(guò)底層虛擬化軟件執(zhí)行相應(yīng)虛擬機(jī)的啟動(dòng)進(jìn)程秤涩。

待用戶(hù)虛擬機(jī)成功啟動(dòng)之后帜乞,Nova-Compute 作為消息生產(chǎn)者通過(guò) Direct 交換器和響應(yīng)的消息隊(duì)列將虛擬機(jī)啟動(dòng)成功響應(yīng)消息反饋給 Nova-API。

此時(shí) Nova-API 作為消息消費(fèi)者接收該消息并通知用戶(hù)虛擬機(jī)啟動(dòng)成功筐眷。

RPC.CALL 工作原理如下圖:

圖 17:RPC.CALL 具體實(shí)現(xiàn)圖

工作流程:

  • 客戶(hù)端創(chuàng)建 Message 時(shí)指定 reply_to 隊(duì)列名黎烈、correlation_id 標(biāo)記調(diào)用者。
  • 通過(guò)隊(duì)列匀谣,服務(wù)端收到消息照棋。調(diào)用函數(shù)處理,然后返回武翎。
  • 返回的隊(duì)列是 reply_to 指定的隊(duì)列烈炭,并攜帶 correlation_id。
  • 返回消息到達(dá)客戶(hù)端宝恶,客戶(hù)端根據(jù) correlation_id 判斷是哪一個(gè)函數(shù)的調(diào)用返回符隙。

如果有多個(gè)線程同時(shí)進(jìn)行遠(yuǎn)程方法調(diào)用,這時(shí)建立在 Client Server 之間的 Socket 連接上會(huì)有很多雙方發(fā)送的消息傳遞垫毙,前后順序也可能是隨機(jī)的霹疫。

Server 處理完結(jié)果后,將結(jié)果消息發(fā)送給 Client综芥,Client 收到很多消息丽蝎,怎么知道哪個(gè)消息結(jié)果是原先哪個(gè)線程調(diào)用的?

Client 線程每次通過(guò) Socket 調(diào)用一次遠(yuǎn)程接口前,生成一個(gè)唯一的 ID膀藐,即 Request ID(Request ID必需保證在一個(gè) Socket 連接里面是唯一的)屠阻,一般常常使用 AtomicLong 從 0 開(kāi)始累計(jì)數(shù)字生成唯一 ID。

RPC.CAST

RPC.CAST 的遠(yuǎn)程調(diào)用流程與 RPC.CALL 類(lèi)似额各,只是缺少了系統(tǒng)消息響應(yīng)流程国觉。

一個(gè) Topic 消息生產(chǎn)者發(fā)送系統(tǒng)請(qǐng)求消息到 Topic 交換器,Topic 交換器根據(jù)消息的 Routing Key 將消息轉(zhuǎn)發(fā)至共享消息隊(duì)列虾啦。

與共享消息隊(duì)列相連的所有 Topic 消費(fèi)者接收該系統(tǒng)請(qǐng)求消息蛉加,并把它傳遞給響應(yīng)的服務(wù)端進(jìn)行處理蚜枢。

其調(diào)用流程如圖所示:

圖 18:RPC.CAST 原理圖

連接設(shè)計(jì)

RabbitMQ 實(shí)現(xiàn)的 RPC 對(duì)網(wǎng)絡(luò)的一般設(shè)計(jì)思路:消費(fèi)者是長(zhǎng)連接,發(fā)送者是短連接针饥。但可以自由控制長(zhǎng)連接和短連接厂抽。

一般消費(fèi)者是長(zhǎng)連接,隨時(shí)準(zhǔn)備接收處理消息;而且涉及到 RabbitMQ Queues丁眼、Exchange 的 auto-deleted 等沒(méi)特殊需求沒(méi)必要做短連接筷凤。發(fā)送者可以使用短連接,不會(huì)長(zhǎng)期占住端口號(hào)苞七,節(jié)省端口資源藐守。

Nova 中 RPC 代碼設(shè)計(jì):

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

RESTful API 架構(gòu)

REST ***的幾個(gè)特點(diǎn)為:資源、統(tǒng)一接口蹂风、URI 和無(wú)狀態(tài)卢厂。

①資源

所謂"資源",就是網(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源的變化而改變。有狀態(tài)和無(wú)狀態(tài)的區(qū)別雪情,舉個(gè)簡(jiǎn)單的例子說(shuō)明一下遵岩。

如查詢(xún)員工的工資,如果查詢(xún)工資是需要登錄系統(tǒng)巡通,進(jìn)入查詢(xún)工資的頁(yè)面尘执,執(zhí)行相關(guān)操作后,獲取工資的多少宴凉,則這種情況是有狀態(tài)的誊锭。

因?yàn)椴樵?xún)工資的每一步操作都依賴(lài)于前一步操作,只要前置操作不成功弥锄,后續(xù)操作就無(wú)法執(zhí)行丧靡。

如果輸入一個(gè) URI 即可得到指定員工的工資,則這種情況是無(wú)狀態(tài)的叉讥,因?yàn)楂@取工資不依賴(lài)于其他資源或狀態(tài)窘行。

且這種情況下,員工工資是一個(gè)資源图仓,由一個(gè) URI 與之對(duì)應(yīng)罐盔,可以通過(guò) HTTP 中的 GET 方法得到資源,這是典型的 RESTful 風(fēng)格救崔。

RPC 和 Restful API 對(duì)比

面對(duì)對(duì)象不同:

  • RPC 更側(cè)重于動(dòng)作惶看。
  • REST 的主體是資源。

RESTful 是面向資源的設(shè)計(jì)架構(gòu)六孵,但在系統(tǒng)中有很多對(duì)象不能抽象成資源纬黎,比如登錄,修改密碼等而 RPC 可以通過(guò)動(dòng)作去操作資源劫窒。所以在操作的全面性上 RPC 大于 RESTful本今。

傳輸效率:

  • RPC 效率更高。RPC主巍,使用自定義的 TCP 協(xié)議冠息,可以讓請(qǐng)求報(bào)文體積更小,或者使用 HTTP2 協(xié)議孕索,也可以很好的減少報(bào)文的體積逛艰,提高傳輸效率。

復(fù)雜度:

  • RPC 實(shí)現(xiàn)復(fù)雜搞旭,流程繁瑣散怖。
  • REST 調(diào)用及測(cè)試都很方便菇绵。

RPC 實(shí)現(xiàn)(參見(jiàn)***節(jié))需要實(shí)現(xiàn)編碼,序列化镇眷,網(wǎng)絡(luò)傳輸?shù)纫ё睢6?RESTful 不要關(guān)注這些,RESTful 實(shí)現(xiàn)更簡(jiǎn)單偏灿。

靈活性:

  • 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ù)雜橄妆。

HTTP 主要用于對(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)銷(xiāo)阁猜。
  • 注冊(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)力的支撐凶赁。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末咧栗,一起剝皮案震驚了整個(gè)濱河市逆甜,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌致板,老刑警劉巖交煞,帶你破解...
    沈念sama閱讀 210,978評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異斟或,居然都是意外死亡素征,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,954評(píng)論 2 384
  • 文/潘曉璐 我一進(jìn)店門(mén)萝挤,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)御毅,“玉大人,你說(shuō)我怎么就攤上這事怜珍《饲” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 156,623評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵酥泛,是天一觀的道長(zhǎng)今豆。 經(jīng)常有香客問(wèn)我,道長(zhǎng)柔袁,這世上最難降的妖魔是什么呆躲? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,324評(píng)論 1 282
  • 正文 為了忘掉前任,我火速辦了婚禮捶索,結(jié)果婚禮上插掂,老公的妹妹穿的比我還像新娘。我一直安慰自己情组,他們只是感情好燥筷,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,390評(píng)論 5 384
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著院崇,像睡著了一般肆氓。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上底瓣,一...
    開(kāi)封第一講書(shū)人閱讀 49,741評(píng)論 1 289
  • 那天谢揪,我揣著相機(jī)與錄音,去河邊找鬼捐凭。 笑死拨扶,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的茁肠。 我是一名探鬼主播患民,決...
    沈念sama閱讀 38,892評(píng)論 3 405
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼垦梆!你這毒婦竟也來(lái)了匹颤?” 一聲冷哼從身側(cè)響起仅孩,我...
    開(kāi)封第一講書(shū)人閱讀 37,655評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎印蓖,沒(méi)想到半個(gè)月后辽慕,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,104評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡赦肃,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,451評(píng)論 2 325
  • 正文 我和宋清朗相戀三年溅蛉,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片他宛。...
    茶點(diǎn)故事閱讀 38,569評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡船侧,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出厅各,到底是詐尸還是另有隱情勺爱,我是刑警寧澤,帶...
    沈念sama閱讀 34,254評(píng)論 4 328
  • 正文 年R本政府宣布讯检,位于F島的核電站琐鲁,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏人灼。R本人自食惡果不足惜围段,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,834評(píng)論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望投放。 院中可真熱鬧奈泪,春花似錦、人聲如沸灸芳。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,725評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)烙样。三九已至冯遂,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間谒获,已是汗流浹背蛤肌。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,950評(píng)論 1 264
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留批狱,地道東北人裸准。 一個(gè)月前我還...
    沈念sama閱讀 46,260評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像赔硫,于是被迫代替她去往敵國(guó)和親炒俱。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,446評(píng)論 2 348