RPC框架及選型

1. RPC 概述

http://dubbo.apache.org/zh-cn/docs/user/references/protocol/dubbo.html (以dubbo為例)

1.1 什么是RPC

RPC(Remote Procedure Call Protocol)遠(yuǎn)程過程調(diào)用協(xié)議:
它是一種通過網(wǎng)絡(luò)從遠(yuǎn)程計(jì)算機(jī)程序上請(qǐng)求服務(wù),而不需要了解底層網(wǎng)絡(luò)技術(shù)的協(xié)議。
簡言之,RPC使得程序能夠像訪問本地系統(tǒng)資源一樣,去訪問遠(yuǎn)端系統(tǒng)資源。
比較關(guān)鍵的一些方面包括:
通訊協(xié)議、序列化、資源(接口)描述、服務(wù)框架、性能、語言支持等。

#簡單的說
RPC就是從一臺(tái)機(jī)器(客戶端)上通過參數(shù)傳遞的方式調(diào)用 --> 
另一臺(tái)機(jī)器(服務(wù)器)上的一個(gè)函數(shù)或方法(可以統(tǒng)稱為服務(wù)) --> 
并得到返回的結(jié)果。

RPC 調(diào)用的分類

#從調(diào)用過程來看,可以分為同步通信RPC和異步通信RPC
>> 同步調(diào)用:客戶端等待調(diào)用執(zhí)行完成并獲取到執(zhí)行結(jié)果。
>> 異步調(diào)用:客戶端調(diào)用后不用等待執(zhí)行結(jié)果返回,但依然可以通過回調(diào)通知等方式獲取返回結(jié)果。
若客戶端不關(guān)心調(diào)用返回結(jié)果,則變成單向異步調(diào)用,單向調(diào)用不用返回結(jié)果。
異步和同步的區(qū)分在于是否等待服務(wù)端執(zhí)行完成并返回結(jié)果。

#從是否跨平臺(tái)可分為
單語言 RPC,如 RMI, Remoting;
跨平臺(tái) RPC,如 google protobuffer, restful json,http XML。

#從通信協(xié)議層面可以分為
基于 HTTP 協(xié)議的 RPC;
基于二進(jìn)制協(xié)議的 RPC;
基于 TCP 協(xié)議的 RPC。

1.2 RPC協(xié)議

在一個(gè)典型的RPC的使用場景中,
包含了服務(wù)發(fā)現(xiàn)、負(fù)載、容錯(cuò)、序列化和網(wǎng)絡(luò)傳輸?shù)冉M件,
其中RPC協(xié)議指明了程序如何進(jìn)行序列化和網(wǎng)絡(luò)傳輸,
也就是說一個(gè)RPC協(xié)議的實(shí)現(xiàn)等于一個(gè)非透明的RPC調(diào)用。

簡單來說,分布式框架的核心是RPC框架,RPC框架的核心是RPC協(xié)議。
RPC協(xié)議在RPC調(diào)用中的位置.png

RPC協(xié)議的基本組成

>> IP:服務(wù)提供者的地址
>> 端口:協(xié)議指定開放端口
>> 運(yùn)行服務(wù)
(1)netty
(2)mima
(3)rmi
(4)servlet容器(Jetty、Tomcat、Jboss)
>> 協(xié)議報(bào)文編解碼
>> 序列化方式
(1)Hessian2Serialization
(2)DubboSerialization
(3)JavaSerialization
(4)JsonSerialization
RPC協(xié)議的基本組成.png

RPC消息協(xié)議

#消息邊界

RPC 需要在一條 TCP 鏈接上進(jìn)行屢次消息傳遞。
在連續(xù)的兩條消息之間必需有明確的分割規(guī)則,以便接收端可以將消息分割開來,
這里的接收端可以是 RPC 服務(wù)器接收請(qǐng)求,也可以是 RPC 用戶端接收響應(yīng)。

基于 TCP 鏈接之上的單條消息假如過大,就會(huì)被網(wǎng)絡(luò)協(xié)議棧拆分為多個(gè)數(shù)據(jù)包進(jìn)行傳送。
假如消息過小,網(wǎng)絡(luò)協(xié)議棧可能會(huì)將多個(gè)消息組合成一個(gè)數(shù)據(jù)包進(jìn)行發(fā)送。
對(duì)于接收端來說它看到的只是一串串的字節(jié)數(shù)組,假如沒有明確的消息邊界規(guī)則,
接收端是無從知道這一串字節(jié)數(shù)組到底是包含多條消息還是只是某條消息的一部分。

#比較常使用的兩種分割方式是特殊分割符法和長度前綴法。
>> 特殊分割符法
消息發(fā)送端在每條消息的末尾追加一個(gè)特殊的分割符,并且保證消息中間的數(shù)據(jù)不能包含特殊分割符。
比方最為常見的分割符是 \r\n 。當(dāng)接收端遍歷字節(jié)數(shù)組時(shí)發(fā)現(xiàn)了 ,
就立就可以斷定之前的字節(jié)數(shù)組是一條完整的消息,可以傳遞到上層邏輯繼續(xù)進(jìn)行解決。
HTTP 和 Redis 協(xié)議就大量用了 分割符。
此種消息一般要求消息體的內(nèi)容是"文本消息"。
>> 長度前綴法
消息發(fā)送端在每條消息的開頭添加一個(gè) 4 字節(jié)長度的整數(shù)值,標(biāo)記消息體的長度。
這樣消息接受者首先讀取到長度信息,而后再讀取相應(yīng)長度的字節(jié)數(shù)組即可以將一個(gè)完整的消息分離出來。
此種消息比較常使用于"二進(jìn)制消息"。

#比較
#特殊分割符法
優(yōu)點(diǎn): 消息的可讀性比較強(qiáng),可以直接看到消息的文本內(nèi)容
缺點(diǎn): 是不適合傳遞二進(jìn)制消息,由于二進(jìn)制的字節(jié)數(shù)組里面很容易就冒出連續(xù)的兩個(gè)字節(jié)內(nèi)容正好就是分割符的 ascii 值。
使用: 假如需要傳遞的話,一般是對(duì)二進(jìn)制進(jìn)行 base64 編碼轉(zhuǎn)變成普通文本消息再進(jìn)行傳送。

#長度前綴法
優(yōu)點(diǎn)和缺點(diǎn)同特殊分割符法正好是相反的。
缺點(diǎn): 由于適使用于二進(jìn)制協(xié)議,所以可讀性很差。
優(yōu)點(diǎn): 對(duì)傳遞的內(nèi)容本身沒有特殊限制,文本和內(nèi)容皆可以傳輸,不需要進(jìn)行特殊解決。
HTTP 協(xié)議的 Content-Length 頭信息使用來標(biāo)記消息體的長度,這個(gè)也可以看成是長度前綴法的一種應(yīng)使用。

#應(yīng)用
HTTP 協(xié)議是一種基于特殊分割符和長度前綴法的混合型協(xié)議。
比方 HTTP 的消息頭采使用的是純文本外加 分割符,
而消息體則是通過消息頭中的 Content-Type 的值來決定長度。
HTTP 協(xié)議盡管被稱之為文本傳輸協(xié)議,但是也可以在消息體中傳輸二進(jìn)制數(shù)據(jù)數(shù)據(jù)的,
例如音視頻圖像,所以 HTTP 協(xié)議被稱之為「超文本」傳輸協(xié)議。
消息邊界--特殊分割符法.png
消息邊界--長度前綴法.png
#消息的結(jié)構(gòu)

每條消息都有它包含的語義結(jié)構(gòu)信息,有些消息協(xié)議的結(jié)構(gòu)信息是顯式的,還有些是隱式的。

#顯式結(jié)構(gòu)的消息協(xié)議
比方 json 消息,它的結(jié)構(gòu)即可以直接通過它的內(nèi)容表現(xiàn)出來,所以它是一種顯式結(jié)構(gòu)的消息協(xié)議。
json 這種直觀的消息協(xié)議的可讀性非常棒,但是它的缺點(diǎn)也很顯著,有太多的冗余信息。
比方每個(gè)字符串都用雙引號(hào)來界定邊界,key/value 之間必需有冒號(hào)分割,對(duì)象之間必需用大括號(hào)分割等等。
這些還只是冗余的小頭,最大的冗余還在于連續(xù)的多條 json 消息即便結(jié)構(gòu)完全一樣,
僅僅只是 value 的值不一樣,也需要發(fā)送同樣的 key 字符串信息。

>> 對(duì)顯示結(jié)構(gòu)消息協(xié)議的改進(jìn)之處
消息的結(jié)構(gòu)在同一條消息通道上是可以復(fù)使用的,
比方在建立鏈接的開始 RPC 用戶端和服務(wù)器之間先交流協(xié)商一下消息的結(jié)構(gòu),
后續(xù)發(fā)送消息時(shí)只要要發(fā)送一系列消息的 value 值,
接收端會(huì)自動(dòng)將 value 值和相應(yīng)位置的 key 關(guān)聯(lián)起來,形成一個(gè)完成的結(jié)構(gòu)消息。
在 Hadoop 系統(tǒng)中廣泛用的 avro 消息協(xié)議就是通過這種方式實(shí)現(xiàn)的,
在 RPC 鏈接建立之處就開始交流消息的結(jié)構(gòu),后續(xù)消息的傳遞即可以節(jié)省很多流量。

#隱式結(jié)構(gòu)的消息協(xié)議
消息的隱式結(jié)構(gòu)一般是指那些結(jié)構(gòu)信息由代碼來商定的消息協(xié)議,
在 RPC 交互的消息數(shù)據(jù)中只是純粹的二進(jìn)制數(shù)據(jù),由代碼來確定相應(yīng)位置的二進(jìn)制是屬于哪個(gè)字段。

假如純粹看消息內(nèi)容是無法知道節(jié)點(diǎn)消息內(nèi)容中的哪些字節(jié)的含義,
它的消息結(jié)構(gòu)是通過代碼的結(jié)構(gòu)順序來確定的。
這種隱式的消息的優(yōu)點(diǎn)就在于節(jié)省傳輸流量,它完全不需要傳輸結(jié)構(gòu)信息。
#消息壓縮

假如消息的內(nèi)容太大,就要考慮對(duì)消息進(jìn)行壓縮解決,這可以減輕網(wǎng)絡(luò)帶寬壓力。
但是這同時(shí)也會(huì)加重 CPU 的負(fù)擔(dān),由于壓縮算法是 CPU 計(jì)算密集型操作,會(huì)導(dǎo)致操作系統(tǒng)的負(fù)載加重。
所以,最終能否進(jìn)行消息壓縮,肯定要根據(jù)業(yè)務(wù)情況加以權(quán)衡。

假如確定壓縮,那么在選擇壓縮算法包時(shí),
務(wù)必篩選那些底層使用 C 語言實(shí)現(xiàn)的算法庫,由于 Python 的字節(jié)碼執(zhí)行起來太慢了。
比較流行的消息壓縮算法有 Google 的 snappy 算法,
它的運(yùn)行性能非常好,壓縮比例盡管不是最優(yōu)的,但是離最優(yōu)的差距已經(jīng)不是很大。
阿里的 SOFA RPC 就用了 snappy 作為協(xié)議層壓縮算法。
消息的顯式結(jié)構(gòu).png
消息的隱式結(jié)構(gòu).png
#流量的極致優(yōu)化

開源的流行 RPC 消息協(xié)議往往對(duì)消息流量優(yōu)化到了極致,
它們通過這種方式來打動(dòng)使用戶,吸引使用戶來用它們。
比方對(duì)于一個(gè)整形數(shù)字,一般用 4 個(gè)字節(jié)來表示一個(gè)整數(shù)值。

但是經(jīng)過研究發(fā)現(xiàn),消息傳遞中大部分用的整數(shù)值都是很小的非負(fù)整數(shù),假如一律用 4 個(gè)字節(jié)來表示一個(gè)整數(shù)會(huì)很白費(fèi)。
所以就發(fā)明了一個(gè)類型叫變長整數(shù)varint。
數(shù)值非常小時(shí),只要要用一個(gè)字節(jié)來存儲(chǔ),數(shù)值略微大一點(diǎn)可以用 2 個(gè)字節(jié),
再大一點(diǎn)就是 3 個(gè)字節(jié),它還可以超過 4 個(gè)字節(jié)使用來表達(dá)長整形數(shù)字。

其原理也很簡單,就是保留每個(gè)字節(jié)的最高位的 bit 來標(biāo)識(shí)能否后面還有字節(jié),
1 表示還有字節(jié)需要繼續(xù)讀,0 表示到讀到當(dāng)前字節(jié)就結(jié)束。

#那假如是負(fù)數(shù)該怎樣辦呢?
-1 的 16 進(jìn)制數(shù)是 0xFFFFFFFF,假如要按照這個(gè)編碼那豈不是要 6 個(gè)字節(jié)才能存的下。
-1 也是非常常見的整數(shù)啊。

于是 zigzag 編碼來了,專門使用來處理負(fù)數(shù)問題。
zigzag 編碼將整數(shù)范圍逐個(gè)映射到自然數(shù)范圍,而后再進(jìn)行 varint 編碼。
zigzag 將負(fù)數(shù)編碼成正奇數(shù),正數(shù)編碼成偶數(shù)。
解碼的時(shí)候遇到偶數(shù)直接除 2 就是原值,遇到奇數(shù)就加 1 除 2 再取負(fù)就是原值。
流量的極致優(yōu)化.png

https://blog.csdn.net/snow____man/article/details/84072901

1.3 RPC的實(shí)現(xiàn)基礎(chǔ)

1、需要有非常高效的網(wǎng)絡(luò)通信,比如一般選擇Netty作為網(wǎng)絡(luò)通信框架;
2、需要有比較高效的序列化框架,比如谷歌的Protobuf序列化框架;
3、可靠的尋址方式(主要是提供服務(wù)的發(fā)現(xiàn)),比如可以使用Zookeeper來注冊(cè)服務(wù)等等;
4、如果是帶會(huì)話(狀態(tài))的RPC調(diào)用,還需要有會(huì)話和狀態(tài)保持的功能;

關(guān)鍵技術(shù)

1、動(dòng)態(tài)代理
生成Client Stub(客戶端存根)和Server Stub(服務(wù)端存根)的時(shí)候需要用到Java動(dòng)態(tài)代理技術(shù),
可以使用JDK提供的原生的動(dòng)態(tài)代理機(jī)制,也可以使用開源的:CGLib代理,Javassist字節(jié)碼生成技術(shù)。

2、序列化和反序列化
在網(wǎng)絡(luò)中,所有的數(shù)據(jù)都將會(huì)被轉(zhuǎn)化為字節(jié)進(jìn)行傳送,
所以為了能夠使參數(shù)對(duì)象在網(wǎng)絡(luò)中進(jìn)行傳輸,需要對(duì)這些參數(shù)進(jìn)行序列化和反序列化操作。
>> 序列化:把對(duì)象轉(zhuǎn)換為字節(jié)序列的過程稱為對(duì)象的序列化,也就是編碼的過程。
>> 反序列化:把字節(jié)序列恢復(fù)為對(duì)象的過程稱為對(duì)象的反序列化,也就是解碼的過程。
目前比較高效的開源序列化框架:如Kryo、FastJson和Protobuf等。

3、NIO通信
出于并發(fā)性能的考慮,傳統(tǒng)的阻塞式 IO 顯然不太合適,因此我們需要異步的 IO,即 NIO。
Java 提供了 NIO 的解決方案,Java 7 也提供了更優(yōu)秀的 NIO.2 支持。
可以選擇Netty或者M(jìn)INA來解決NIO數(shù)據(jù)傳輸?shù)膯栴}。

4、服務(wù)注冊(cè)中心
可選:Redis、Zookeeper、Consul 、Etcd。

1.4 RPC具體調(diào)用過程

1、服務(wù)消費(fèi)者(client客戶端)通過調(diào)用本地服務(wù)的方式調(diào)用需要消費(fèi)的服務(wù);
2、客戶端存根(client stub)接收到調(diào)用請(qǐng)求后負(fù)責(zé)將方法、入?yún)⒌刃畔⑿蛄谢ńM裝)成能夠進(jìn)行網(wǎng)絡(luò)傳輸?shù)南Ⅲw;
3、客戶端存根(client stub)找到遠(yuǎn)程的服務(wù)地址,并且將消息通過網(wǎng)絡(luò)發(fā)送給服務(wù)端;
4、服務(wù)端存根(server stub)收到消息后進(jìn)行解碼(反序列化操作);
5、服務(wù)端存根(server stub)根據(jù)解碼結(jié)果調(diào)用本地的服務(wù)進(jìn)行相關(guān)處理;
6、本地服務(wù)執(zhí)行具體業(yè)務(wù)邏輯并將處理結(jié)果返回給服務(wù)端存根(server stub);
7、服務(wù)端存根(server stub)將返回結(jié)果重新打包成消息(序列化)并通過網(wǎng)絡(luò)發(fā)送至消費(fèi)方;
8、客戶端存根(client stub)接收到消息,并進(jìn)行解碼(反序列化);
9、服務(wù)消費(fèi)方得到最終結(jié)果;

而RPC框架的實(shí)現(xiàn)目標(biāo)則是將上面的各步完好地封裝起來,
也就是把調(diào)用、編碼/解碼的過程給封裝起來,
讓用戶感覺上像調(diào)用本地服務(wù)一樣的調(diào)用遠(yuǎn)程服務(wù)。

#更進(jìn)一步的解釋為:
>> RPC 服務(wù)方通過 RpcServer 去導(dǎo)出(export)遠(yuǎn)程接口方法,
客戶方通過RpcClient 去引入(import)遠(yuǎn)程接口方法。 
>> 客戶方像調(diào)用本地方法一樣去調(diào)用遠(yuǎn)程接口方法,
RPC 框架提供接口的代理實(shí)現(xiàn),實(shí)際的調(diào)用將委托給代理 RpcProxy 。
>> 代理封裝調(diào)用信息并將調(diào)用轉(zhuǎn)交給 RpcInvoker 去實(shí)際執(zhí)行。 
>> 在客戶端的 RpcInvoker 通過連接器 RpcConnector 去維持與服務(wù)端的通道 RpcChannel, 
并使用 RpcProtocol 執(zhí)行協(xié)議編碼(encode)并將編碼后的請(qǐng)求消息通過通道發(fā)送給服務(wù)方。
>> RPC 服務(wù)端接收器 RpcAcceptor 接收客戶端的調(diào)用請(qǐng)求,同樣使用 RpcProtocol 執(zhí)行協(xié)議解碼(decode)。 
>> 解碼后的調(diào)用信息傳遞給 RpcProcessor 去控制處理調(diào)用過程
>> 最后再委托調(diào)用給 RpcInvoker 去實(shí)際執(zhí)行并返回調(diào)用結(jié)果。

#通過上述分析可知,這里面包括以下核心組件:
1、RpcServer
負(fù)責(zé)導(dǎo)出(export)遠(yuǎn)程接口
2、RpcClient
負(fù)責(zé)導(dǎo)入(import)遠(yuǎn)程接口的代理實(shí)現(xiàn)
3、RpcProxy
遠(yuǎn)程接口的代理實(shí)現(xiàn)
4、RpcInvoker
客戶方實(shí)現(xiàn):負(fù)責(zé)編碼調(diào)用信息和發(fā)送調(diào)用請(qǐng)求到服務(wù)方并等待調(diào)用結(jié)果返回
服務(wù)方實(shí)現(xiàn):負(fù)責(zé)調(diào)用服務(wù)端接口的具體實(shí)現(xiàn)并返回調(diào)用結(jié)果
5、RpcProtocol
負(fù)責(zé)協(xié)議編/解碼
6、RpcConnector
負(fù)責(zé)維持客戶方和服務(wù)方的連接通道和發(fā)送數(shù)據(jù)到服務(wù)方
7、RpcAcceptor
負(fù)責(zé)接收客戶方請(qǐng)求并返回請(qǐng)求結(jié)果
8、RpcProcessor
負(fù)責(zé)在服務(wù)方控制調(diào)用過程,包括管理調(diào)用線程池、超時(shí)時(shí)間等
9、RpcChannel
數(shù)據(jù)傳輸通道
RPC的實(shí)現(xiàn)原理架構(gòu)圖1.png
RPC的實(shí)現(xiàn)原理架構(gòu)圖2.png
RPC結(jié)構(gòu).png

舉例說明

比如說,A服務(wù)器想調(diào)用B服務(wù)器上的一個(gè)方法:User getUserByName(String userName)

#1、建立通信
首先要解決通訊的問題:
即A機(jī)器想要調(diào)用B機(jī)器,首先得建立起通信連接。
主要是通過在客戶端和服務(wù)器之間建立TCP連接,遠(yuǎn)程過程調(diào)用的所有交換的數(shù)據(jù)都在這個(gè)連接里傳輸。
通常這個(gè)連接可以是按需連接(需要調(diào)用的時(shí)候就先建立連接,調(diào)用結(jié)束后就立馬斷掉),
也可以是長連接(客戶端和服務(wù)器建立起連接之后保持長期持有,不管此時(shí)有無數(shù)據(jù)包的發(fā)送,
可以配合心跳檢測機(jī)制定期檢測建立的連接是否存活有效),多個(gè)遠(yuǎn)程過程調(diào)用共享同一個(gè)連接。

#2、服務(wù)尋址
要解決尋址的問題,也就是說,A服務(wù)器上的應(yīng)用怎么告訴底層的RPC框架,
如何連接到B服務(wù)器(如主機(jī)或IP地址)以及特定的端口,方法的名稱名稱是什么。
通常情況下我們需要提供B機(jī)器(主機(jī)名或IP地址)以及特定的端口,
然后指定調(diào)用的方法或者函數(shù)的名稱以及入?yún)⒊鰠⒌刃畔ⅲ@樣才能完成服務(wù)的一個(gè)調(diào)用。可
靠的尋址方式(主要是提供服務(wù)的發(fā)現(xiàn))是RPC的實(shí)現(xiàn)基石,比如可以采用Redis或者Zookeeper來注冊(cè)服務(wù)等等。
#2.1、從服務(wù)提供者的角度看:
當(dāng)服務(wù)提供者啟動(dòng)的時(shí)候,需要將自己提供的服務(wù)注冊(cè)到指定的注冊(cè)中心,
以便服務(wù)消費(fèi)者能夠通過服務(wù)注冊(cè)中心進(jìn)行查找;
當(dāng)服務(wù)提供者由于各種原因致使提供的服務(wù)停止時(shí),需要向注冊(cè)中心注銷停止的服務(wù);
服務(wù)的提供者需要定期向服務(wù)注冊(cè)中心發(fā)送心跳檢測,
服務(wù)注冊(cè)中心如果一段時(shí)間未收到來自服務(wù)提供者的心跳后,
認(rèn)為該服務(wù)提供者已經(jīng)停止服務(wù),則將該服務(wù)從注冊(cè)中心上去掉。
#2.2、從調(diào)用者的角度看:
服務(wù)的調(diào)用者啟動(dòng)的時(shí)候根據(jù)自己訂閱的服務(wù)向服務(wù)注冊(cè)中心查找服務(wù)提供者的地址等信息;
當(dāng)服務(wù)調(diào)用者消費(fèi)的服務(wù)上線或者下線的時(shí)候,注冊(cè)中心會(huì)告知該服務(wù)的調(diào)用者;
服務(wù)調(diào)用者下線的時(shí)候,則取消訂閱。

#3、網(wǎng)絡(luò)傳輸
#3.1、序列化
當(dāng)A機(jī)器上的應(yīng)用發(fā)起一個(gè)RPC調(diào)用時(shí),
調(diào)用方法和其入?yún)⒌刃畔⑿枰ㄟ^底層的網(wǎng)絡(luò)協(xié)議如TCP傳輸?shù)紹機(jī)器,由于網(wǎng)絡(luò)協(xié)議是基于二進(jìn)制的,
所以我們傳輸?shù)膮?shù)數(shù)據(jù)都需要先進(jìn)行序列化(Serialize)或者編組(marshal)成二進(jìn)制的形式才能在網(wǎng)絡(luò)中進(jìn)行傳輸。
然后通過尋址操作和網(wǎng)絡(luò)傳輸將序列化或者編組之后的二進(jìn)制數(shù)據(jù)發(fā)送給B機(jī)器。
#3.2、反序列化
當(dāng)B機(jī)器接收到A機(jī)器的應(yīng)用發(fā)來的請(qǐng)求之后,又需要對(duì)接收到的參數(shù)等信息進(jìn)行反序列化操作(序列化的逆操作),
即將二進(jìn)制信息恢復(fù)為內(nèi)存中的表達(dá)方式,然后再找到對(duì)應(yīng)的方法(尋址的一部分)
進(jìn)行本地調(diào)用(一般是通過生成代理Proxy去調(diào)用,通常會(huì)有JDK動(dòng)態(tài)代理、CGLIB動(dòng)態(tài)代理、
Javassist生成字節(jié)碼技術(shù)等),之后得到調(diào)用的返回值。

#4、服務(wù)調(diào)用
B機(jī)器進(jìn)行本地調(diào)用(通過代理Proxy和反射調(diào)用)之后得到了返回值,
此時(shí)還需要再把返回值發(fā)送回A機(jī)器,同樣也需要經(jīng)過序列化操作,
然后再經(jīng)過網(wǎng)絡(luò)傳輸將二進(jìn)制數(shù)據(jù)發(fā)送回A機(jī)器,
而當(dāng)A機(jī)器接收到這些返回值之后,則再次進(jìn)行反序列化操作,恢復(fù)為內(nèi)存中的表達(dá)方式,
最后再交給A機(jī)器上的應(yīng)用進(jìn)行相關(guān)處理(一般是業(yè)務(wù)邏輯處理操作)。

通常,經(jīng)過以上四個(gè)步驟之后,一次完整的RPC調(diào)用算是完成了,
另外可能因?yàn)榫W(wǎng)絡(luò)抖動(dòng)等原因需要重試等。

2.主流RPC框架有哪些

1、RMI
利用java.rmi包實(shí)現(xiàn),基于Java遠(yuǎn)程方法協(xié)議(Java Remote Method Protocol) 和java的原生序列化。

2、Hessian
是一個(gè)輕量級(jí)的remoting onhttp工具,使用簡單的方法提供了RMI的功能。 
基于HTTP協(xié)議,采用二進(jìn)制編解碼。

3、protobuf-rpc-pro
是一個(gè)Java類庫,提供了基于 Google 的 Protocol Buffers 協(xié)議的遠(yuǎn)程方法調(diào)用的框架。
基于 Netty 底層的 NIO 技術(shù)。
支持 TCP 重用/ keep-alive、SSL加密、RPC 調(diào)用取消操作、嵌入式日志等功能。

4、Apache Thrift
是一種可伸縮的跨語言服務(wù)的軟件框架。
它擁有功能強(qiáng)大的代碼生成引擎,無縫地支持C + +,C#,Java,Python和PHP和Ruby。
Thrift允許你定義一個(gè)描述文件,描述數(shù)據(jù)類型和服務(wù)接口。
依據(jù)該文件,編譯器方便地生成RPC客戶端和服務(wù)器通信代碼。
最初由facebook開發(fā)用做系統(tǒng)內(nèi)個(gè)語言之間的RPC通信,
2007年由facebook貢獻(xiàn)到apache基金 ,現(xiàn)在是apache下的opensource之一 。
支持多種語言之間的RPC方式的通信, 底層通訊基于SOCKET。

5、Avro
出自Hadoop之父Doug Cutting, 
在Thrift已經(jīng)相當(dāng)流行的情況下推出Avro的目標(biāo)不僅是提供一套類似Thrift的通訊中間件,
更是要建立一個(gè)新的,標(biāo)準(zhǔn)性的云計(jì)算的數(shù)據(jù)交換和存儲(chǔ)的Protocol。
支持HTTP,TCP兩種協(xié)議。

6、Dubbo
Dubbo是 阿里巴巴公司開源的一個(gè)高性能優(yōu)秀的服務(wù)框架,
使得應(yīng)用可通過高性能的 RPC 實(shí)現(xiàn)服務(wù)的輸出和輸入功能,可以和 Spring框架無縫集成。

7、Motan
新浪微博內(nèi)部RPC框架。

2.1 跟語言平臺(tái)綁定的開源 RPC 框架

>> Dubbo:
國內(nèi)最早開源的 RPC 框架,由阿里巴巴公司開發(fā)并于 2011 年末對(duì)外開源,僅支持 Java 語言。
>> Motan:
微博內(nèi)部使用的 RPC 框架,于 2016 年對(duì)外開源,僅支持 Java 語言。
>> Tars:
騰訊內(nèi)部使用的 RPC 框架,于 2017 年對(duì)外開源,僅支持 C++ 語言。
>> Spring Cloud:
國外 Pivotal 公司 2014 年對(duì)外開源的 RPC 框架,僅支持 Java 語言。

2.1.1 Dubbo

http://www.lxweimin.com/p/49a675abcad0 (參考該文)

2.1.2 Motan

https://github.com/weibocom/motan/wiki (參考官方wiki)

Motan 是國內(nèi)另外一個(gè)比較有名的開源的 RPC 框架,同樣也只支持 Java 語言實(shí)現(xiàn)。

Motan 與 Dubbo 的架構(gòu)類似,都需要在 Client 端(服務(wù)消費(fèi)者)和 Server 端(服務(wù)提供者)引入 SDK,
其中 Motan 框架主要包含下面幾個(gè)功能模塊。
>> register:
用來和注冊(cè)中心交互,包括注冊(cè)服務(wù)、訂閱服務(wù)、服務(wù)變更通知、服務(wù)心跳發(fā)送等功能。
Server 端會(huì)在系統(tǒng)初始化時(shí)通過 register 模塊注冊(cè)服務(wù),
Client 端會(huì)在系統(tǒng)初始化時(shí)通過 register 模塊訂閱到具體提供服務(wù)的 Server 列表,
當(dāng) Server 列表發(fā)生變更時(shí)也由 register 模塊通知 Client。
>> protocol:
用來進(jìn)行 RPC 服務(wù)的描述和 RPC 服務(wù)的配置管理,
這一層還可以添加不同功能的 filter 用來完成統(tǒng)計(jì)、并發(fā)限制等功能。
>> serialize:
將 RPC 請(qǐng)求中的參數(shù)、結(jié)果等對(duì)象進(jìn)行序列化與反序列化,
即進(jìn)行對(duì)象與字節(jié)流的互相轉(zhuǎn)換,默認(rèn)使用對(duì) Java 更友好的 Hessian 2 進(jìn)行序列化。
>> transport:
用來進(jìn)行遠(yuǎn)程通信,默認(rèn)使用 Netty NIO 的 TCP 長鏈接方式。
>> cluster:
Client 端使用的模塊,cluster 是一組可用的 Server 在邏輯上的封裝,
包含若干可以提供 RPC 服務(wù)的 Server,
實(shí)際請(qǐng)求時(shí)會(huì)根據(jù)不同的高可用與負(fù)載均衡策略選擇一個(gè)可用的 Server 發(fā)起遠(yuǎn)程調(diào)用。
Motan架構(gòu)圖.png

2.1.3 Spring Cloud

Spring Cloud 是為了解決微服務(wù)架構(gòu)中服務(wù)治理而提供的一系列功能的開發(fā)框架,
它是完全基于 Spring Boot 進(jìn)行開發(fā)的,Spring Cloud 利用 Spring Boot 特性整合了開源行業(yè)中優(yōu)秀的組件,
整體對(duì)外提供了一套在微服務(wù)架構(gòu)中服務(wù)治理的解決方案。
只支持 Java 語言平臺(tái)。
Spring-Cloud架構(gòu)圖.png

2.2 跨語言平臺(tái)的開源 RPC 框架

>> gRPC:
Google 于 2015 年對(duì)外開源的跨語言 RPC 框架,支持常用的 
C++、Java、Python、Go、Ruby、PHP、Android Java、Objective-C 等多種語言。
>> Thrift:
最初是由 Facebook 開發(fā)的內(nèi)部系統(tǒng)跨語言的 RPC 框架,2007 年貢獻(xiàn)給了 Apache 基金,
成為 Apache 開源項(xiàng)目之一,支持常用的 C++、Java、PHP、Python、Ruby、Erlang 等多種語言。

2.2.1 gRPC

gRPC,它的原理是通過 IDL(Interface Definition Language)文件定義服務(wù)接口的參數(shù)和返回值類型,
然后通過代碼生成程序生成服務(wù)端和客戶端的具體實(shí)現(xiàn)代碼,這樣在 gRPC 里,
客戶端應(yīng)用可以像調(diào)用本地對(duì)象一樣調(diào)用另一臺(tái)服務(wù)器上對(duì)應(yīng)的方法。

#它的主要特性包括三個(gè)方面
>> 通信協(xié)議采用了 HTTP/2,因?yàn)?HTTP/2 提供了連接復(fù)用、雙向流、服務(wù)器推送、
請(qǐng)求優(yōu)先級(jí)、首部壓縮等機(jī)制,所以在通信過程中可以節(jié)省帶寬、降低 TCP 連接次數(shù)、
節(jié)省 CPU,尤其對(duì)于移動(dòng)端應(yīng)用來說,可以幫助延長電池壽命。
>> IDL 使用了ProtoBuf,ProtoBuf 是由 Google 開發(fā)的一種數(shù)據(jù)序列化協(xié)議,
它的壓縮和傳輸效率極高,語法也簡單,所以被廣泛應(yīng)用在數(shù)據(jù)存儲(chǔ)和通信協(xié)議上。
>> 多語言支持,能夠基于多種語言自動(dòng)生成對(duì)應(yīng)語言的客戶端和服務(wù)端的代碼。
gRPC.png

2.2.2 Thrift

Thrift 是一種輕量級(jí)的跨語言 RPC 通信方案,支持多達(dá) 25 種編程語言。
為了支持多種語言,跟 gRPC 一樣,Thrift 也有一套自己的接口定義語言 IDL,
可以通過代碼生成器,生成各種編程語言的 Client 端和 Server 端的 SDK 代碼,
這樣就保證了不同語言之間可以相互通信。

#Thrift RPC 框架的特性。
>> 支持多種序列化格式:如 Binary、Compact、JSON、Multiplexed 等。
>> 支持多種通信方式:如 Socket、Framed、File、Memory、zlib 等。
>> 服務(wù)端支持多種處理方式:如 Simple 、Thread Pool、Non-Blocking 等。

https://github.com/apache/thrift/raw/master/doc/images/thrift-layers.png

2.n 總結(jié)

從長遠(yuǎn)來看,支持多語言是 RPC 框架未來的發(fā)展趨勢。
正是基于此判斷,各個(gè) RPC 框架都提供了 Sidecar 組件來支持多語言平臺(tái)之間的 RPC 調(diào)用。
>> Dubbo 在重啟了維護(hù),并且宣稱要引入 Sidecar 組件來構(gòu)建Dubbo Mesh提供多語言支持。
>> Motan 也在去年對(duì)外開源了其內(nèi)部的 Sidecar 組件:
Motan-go,目前支持 PHP、Java 語言之間的相互調(diào)用。
>> Spring Cloud 也提供了 Sidecar 組件 spring-cloud-netflix-sideca,
可以讓其他語言也可以使用 Spring Cloud 的組件。

所以未來語言不會(huì)成為使用上面這幾種 RPC 框架的約束,
而 gRPC 和 Thrift 雖然支持跨語言的 RPC 調(diào)用,但是因?yàn)樗鼈冎惶峁┝俗罨镜?RPC 框架功能,
缺乏一系列配套的服務(wù)化組件和服務(wù)治理功能的支撐,所以使用它們作為跨語言調(diào)用的 RPC 框架,
就需要自己考慮注冊(cè)中心、熔斷、限流、監(jiān)控、分布式追蹤等功能的實(shí)現(xiàn)。
不過好在大多數(shù)功能都有開源實(shí)現(xiàn),可以直接采用。

參考資源
http://www.lxweimin.com/p/bbaf6af8c01f (RPC解析)
http://www.lxweimin.com/p/f2b8e0b11f73 (RPC框架選型參考)
https://www.cnblogs.com/wangkc/p/10864937.html (RPC與HTTP比較)
https://www.songma.com/news/txtlist_i3460v.html (RPC協(xié)議構(gòu)成)
https://blog.csdn.net/nyyjs/article/details/77850523 (RPC調(diào)用中的反射技術(shù))
http://www.360doc.com/content/18/0531/07/36490684_758407718.shtml (RPC調(diào)用中的反射技術(shù))
http://www.lxweimin.com/p/f4f789846fde (RPC調(diào)用過程詳解)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容

  • 1.概述 七層模型五層模型四層模型應(yīng)用層表示層應(yīng)用層應(yīng)用層會(huì)話層傳輸層傳輸層傳輸層網(wǎng)絡(luò)層網(wǎng)絡(luò)層網(wǎng)絡(luò)層數(shù)據(jù)鏈路層數(shù)據(jù)...
    suxin1932閱讀 3,088評(píng)論 1 3
  • 走在黑黑的小巷里 沒有狗吠車喧 一切仿佛被時(shí)間定格 還記得曾經(jīng)的你 也是定格在這默默的世界 遠(yuǎn)方的你是否睡了 我多...
    晨曦城光閱讀 164評(píng)論 0 1
  • 與茶有緣閱讀 95評(píng)論 0 2
  • 因熱愛而學(xué)習(xí) 小女兒告訴我,她幫大女兒辦了一件大事。原來一個(gè)月前,大女兒從海淘網(wǎng)上買一雙鞋子。今天快遞員打電話,因...
    漂亮的花閱讀 287評(píng)論 0 5