遠程服務調用PRC發展史

本文是《鳳凰架構》一書的讀書筆記。

img

RPC 調用簡介

RPC(遠程服務調用)是指位於互不重合的內存地址空間中(可以是一臺機器也可以是不同網絡分區的不同機器)的兩個程序,在語言層面上,以同步的方式使用帶寬有限的信道來傳輸程序控制信息。

通過網絡進行分佈式運算的八宗罪(8 Fallaciesof Distributed Computing)

image-20210727170337843

RPC協議要解決的三個問題

1. 如何表示數據(如何進行序列化)

每種RPC協議都應該要有對應的序列化協議。

  • CORBA的通用數據表示(Common Data Representation,CDR)
  • Java RMI的Java對象序列化流協議(Java Object Serialization StreamProtocol)
  • gRPC的Protocol Buffers
  • Web Service的XML序列化
  • 衆多輕量級RPC支持的JSON序列化

2. 如何傳遞數據(使用什麼協議來傳遞數據)

如何傳遞數據,準確地說,是指如何通過網絡,在兩個服務的Endpoint之間相互操作、交換數據。

兩個服務交互數據不是隻扔個序列化數據流來表示參數和結果就行許多在此之外的信息,譬如異常、超時、安全、認證、授權、事務等,都可能產生雙方需要交換信息的需求。在計算機科學中,專門有一個名詞“Wire Protocol”來表示這種兩個Endpoint之間交換這類數據的行爲,常見的Wire Protocol如下。

  • Java RMI的Java遠程消息交換協議(Java Remote Message Protocol,JRMP,也支持RMI-IIOP)
  • CORBA的互聯網ORB間協議(Internet Inter ORB Protocol,IIOP,是GIOP協議在IP協議上的實現版本)
  • Web Service的簡單對象訪問協議(Simple Object Access Protocol,SOAP)
  • 如果要求足夠簡單,雙方都是HTTP Endpoint,直接使用HTTP協議也是可以的(如JSON-RPC)

3. 如何表示方法

確定表示方法在本地方法調用中並不是太大的問題,編譯器或者解釋器會根據語言規範,將調用的方法簽名轉換爲進程空間中子過程入口位置的指針。不過一旦要考慮不同語言,事情又立刻麻煩起來,每種語言的方法簽名都可能有差別,所以“如何表示同一個方法”“如何找到對應的方法”還是需要一個統一的跨語言的標準纔行。

這個標準可以非常簡單,譬如直接給程序的每個方法都規定一個唯一的、在任何機器上都絕不重複的編號,調用時壓根不管它是什麼方法、簽名是如何定義的,直接傳這個編號就能找到對應的方法。

類似地,用於表示方法的協議還有:

  • CORBA的OMG接口定義語言(OMG Interface Definition Language,OMGIDL)
  • Web Service的Web服務描述語言(Web Service Description Language,WSDL)
  • JSON-RPC的JSON Web服務協議(JSON Web Service Protocol,JSON-WSP)

沒有一個簡單、通用、高性能的協議能同時解決上面的問題

面向透明的、簡單的RPC協議,如DCE/RPC、DCOM、Java RMI,要麼依賴於操作系統,要麼依賴於特定語言,總有一些先天約束;

那些面向通用的、普適的RPC協議,如CORBA,就無法逃過使用複雜性的困擾,CORBA煩瑣的OMGIDL、ORB都是很好的佐證;

而那些意圖通過技術手段來屏蔽複雜性的RPC協議,如Web Service,又不免受到性能問題的束縛。

簡單、普適、高性能這三點,似乎真的很難同時滿足。

各有側重點的RPC

由於一直沒有一個同時滿足以上三點的“完美RPC協議”出現,所以遠程服務器調用這個小小的領域,逐漸進入羣雄混戰、百家爭鳴的戰國時代,距離“統一”越來越遠,並一直延續至今。現在,已經相繼出現過RMI(Sun/Oracle)、Thrift(Facebook/Apache)、Dubbo(阿里巴巴/Apache)、gRPC(Google)、Motan1/2(新浪)、Finagle(Twitter)、brpc(百度/Apache)、.NETRemoting(微軟)、Arvo(Hadoop)、JSON-RPC 2.0(公開規範,JSON-RPC工作組)等難以窮舉的協議和框架。這些RPC功能、特點不盡相同,有的是某種語言私有,有的支持跨越多種語言,有的運行在應用層HTTP協議之上,有的直接運行於傳輸層TCP/UDP協議之上,但並不存在哪一款是“最完美的RPC”。今時今日,任何一款具有生命力的RPC框架,都不再去追求大而全的“完美”,而是以某個具有針對性的特點作爲主要的發展方向。

  • 朝着性能發展,代表爲gRPC和Thrift。決定RPC性能的主要因素有兩個:序列化效率和信息密度。序列化效率很好理解,序列化輸出結果的容量越小,速度越快,效率自然越高;信息密度則取決於協議中有效負載(Payload)所佔總傳輸數據的比例大小,使用傳輸協議的層次越高,信息密度就越低,SOAP使用XML拙劣的性能表現就是前車之鑑。gRPC和Thrift都有自己優秀的專有序列化器,而傳輸協議方面,gRPC是基於HTTP/2的,支持多路複用和Header壓縮,Thrift則直接基於傳輸層的TCP協議來實現,省去了應用層協議的額外開銷。
  • 朝着簡化發展,代表爲JSON-RPC,說要選功能最強、速度最快的RPC可能會很有爭議,但選功能弱的、速度慢的,JSON-RPC肯定會是候選人之一。犧牲了功能和效率,換來的是協議的簡單輕便,接口與格式都更爲通用,尤其適合用於Web瀏覽器這類一般不會有額外協議支持、額外客戶端支持的應用場合。

到了最近幾年,RPC框架有明顯向更高層次(不僅僅負責調用遠程服務,還管理遠程服務)與插件化方向發展的趨勢,不再追求獨立地解決RPC的全部三個問題(表示數據、傳遞數據、表示方法),而是將一部分功能設計成擴展點,讓用戶自己選擇。框架聚焦於提供核心的、更高層次的能力,譬如提供負載均衡、服務註冊、可觀察性等方面的支持。這一類框架的代表有Facebook的Thrift與阿里的Dubbo

Dubbo默認有自己的傳輸協議(Dubbo協議),同時也支持其他協議;默認採用Hessian 2作爲序列化器,如果你有JSON的需求,可以替換爲Fastjson,如果你對性能有更高的追求,可以替換爲Kryo、FST、Protocol Buffers等效率更好的序列化器,如果你不想依賴其他組件庫,也可以直接使用JDK自帶的序列化器。這種設計在一定程度上緩和了RPC框架必須取捨、難以完美的缺憾。

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