1. 程式人生 > >系統間通訊方式之(RPC的基本概念)(十)

系統間通訊方式之(RPC的基本概念)(十)

1、概述

經過了詳細的資訊格式、網路IO模型的講解,並且通過JAVA RMI的講解進行了預熱。從這篇文章開始我們將進入這個系列博文的另一個重點知識體系的講解:RPC。在後續的幾篇文章中,我們首先講解RPC的基本概念,一個具體的RPC實現會有哪些基本要素構成,然後我們詳細介紹一款典型的RPC框架:Apache Thrift。接下來我們聊聊服務治理和DUBBO服務框架。最後總結一下如何在實際工作中選擇合適的RPC框架。

2、RPC概述

2-1、什麼是RPC

RPC(Remote Procedure Call Protocol)遠端過程呼叫協議。一個通俗的描述是:客戶端在不知道呼叫細節的情況下,呼叫存在於遠端計算機上的某個物件,就像呼叫本地應用程式中的物件一樣。比較正式的描述是:一種通過網路從遠端計算機程式上請求服務,而不需要了解底層網路技術的協議。那麼我們至少從這樣的描述中挖掘出幾個要點:

  • RPC是協議:既然是協議就只是一套規範,那麼就需要有人遵循這套規範來進行實現。目前典型的RPC實現包括:Dubbo、Thrift、GRPC、Hetty等。這裡要說明一下,目前技術的發展趨勢來看,實現了RPC協議的應用工具往往都會附加其他重要功能,例如Dubbo還包括了服務管理、訪問許可權管理等功能。

  • 網路協議和網路IO模型對其透明:既然RPC的客戶端認為自己是在呼叫本地物件。那麼傳輸層使用的是TCP/UDP還是HTTP協議,又或者是一些其他的網路協議它就不需要關心了。既然網路協議對其透明,那麼呼叫過程中,使用的是哪一種網路IO模型呼叫者也不需要關心。

  • 資訊格式對其透明:我們知道在本地應用程式中,對於某個物件的呼叫需要傳遞一些引數,並且會返回一個呼叫結果。至於被呼叫的物件內部是如何使用這些引數,並計算出處理結果的,呼叫方是不需要關心的。那麼對於遠端呼叫來說,這些引數會以某種資訊格式傳遞給網路上的另外一臺計算機,這個資訊格式是怎樣構成的,呼叫方是不需要關心的

  • 應該有跨語言能力:為什麼這樣說呢?因為呼叫方實際上也不清楚遠端伺服器的應用程式是使用什麼語言執行的。那麼對於呼叫方來說,無論伺服器方使用的是什麼語言,本次呼叫都應該成功,並且返回值也應該按照呼叫方程式語言所能理解的形式進行描述

那麼上面的描述情況可以用下圖表示:

這裡寫圖片描述

2-2、RPC要素

當然,上圖是作為RPC的呼叫者所觀察到的現象(而實際情況是客戶端或多或少的還是需要知道一些呼叫RPC的細節)。但是我們是要講解RPC的基本概念,所以RPC協議內部是怎麼回事就要說清楚:

這裡寫圖片描述

  • Client:RPC協議的呼叫方。就像上文所描述的那樣,最理想的情況是RPC Client在完全不知道有RPC框架存在的情況下發起對遠端服務的呼叫。但實際情況來說Client或多或少的都需要指定RPC框架的一些細節。

  • Server:在RPC規範中,這個Server並不是提供RPC伺服器IP、埠監聽的模組。而是遠端服務方法的具體實現(在JAVA中就是RPC服務介面的具體實現)。其中的程式碼是最普通的和業務相關的程式碼,甚至其介面實現類本身都不知道將被某一個RPC遠端客戶端呼叫

  • Stub/Proxy:RPC代理存在於客戶端,因為要實現客戶端對RPC框架“透明”呼叫,那麼客戶端不可能自行去管理訊息格式、不可能自己去管理網路傳輸協議,也不可能自己去判斷呼叫過程是否有異常。這一切工作在客戶端都是交給RPC框架中的“代理”層來處理的。

  • Message Protocol:在上文我們已經說到,一次完整的client-server的互動肯定是攜帶某種兩端都能識別的,共同約定的訊息格式。RPC的訊息管理層專門對網路傳輸所承載的訊息資訊進行編號和解碼操作。目前流行的技術趨勢是不同的RPC實現,為了加強自身框架的效率都有一套(或者幾套)私有的訊息格式。例如前文所講到的RMI框架使用的訊息協議為JRMP;後文我們將詳細講解的RPC框架Thrift也有私有的訊息協議,“- Transfer/Network Protocol”(當然它還支援一些通用的訊息格式,如JSON)。

  • Transfer/Network Protocol:傳輸協議層負責管理RPC框架所使用的網路協議、網路IO模型。例如Hessian的傳輸協議基於HTTP(應用層協議);而Thrift的傳輸協議基於TCP(傳輸層協議)。傳輸層還需要統一RPC客戶端和RPC服務端所使用的IO模型;常用的IO模型在之前已經詳細講解過了(可參見我之前的博文《架構設計:系統間通訊(3)——IO通訊模型和JAVA實踐 上篇》)

  • Selector/Processor:存在於RPC服務端,由於伺服器端某一個RPC介面的實現的特性(它並不知道自己是一個將要被RPC提供給第三方系統呼叫的服務)。所以在RPC框架中應該有一種“負責執行RPC介面實現”的角色。它負責了包括:管理RPC介面的註冊、判斷客戶端的請求許可權、控制介面實現類的執行在內的各種工作。

  • IDL:實際上IDL(介面定義語言)並不是RPC實現中所必須的。但是需要跨語言的RPC框架一定會有IDL部分的存在。這是因為要找到一個各種語言能夠理解的訊息結構、介面定義的描述形式。如果您的RPC實現沒有考慮跨語言性,那麼IDL部分就不需要包括,例如JAVA RMI因為就是為了在JAVA語言間進行使用,所以JAVA RMI就沒有相應的IDL。

  • 一定要說明一點,不同的RPC框架實現都有一定設計差異。例如生成Stub的方式不一樣,IDL描述語言不一樣、服務註冊的管理方式不一樣、執行服務實現的方式不一樣、採用的訊息格式封裝不一樣、採用的網路協議不一樣。但是基本的思路都是一樣的,上圖中的所列出的要素也都是具有的。

2-3、典型的RPC框架介紹

  • JAVA RMI:是不是覺得前文中我們介紹RMI所提到幾個關鍵概念在RPC中都找得到一些影子。是的,RPC最早就是由SUN提出,並在後來由IETF ONC修訂。RMI就是一個典型的RPC實現,只不過RMI不支援跨語言性,所以RMI中也沒有IDL存在的必要。但是RMI真心快,並且由於沒有IDL的存在,在構建一套完整的RPC實現時要比其他RPC框架少了一些步驟,所以使用起來也比較簡單。如果您的業務需求中並不存在跨語言的考慮,並且基本上主要系統都是用JAVA實現,那麼RMI絕對是您一個可以考慮的方案。

  • GRPC:GRPC是一個高效能、通用的開源RPC框架,由Google主要面向移動應用開發並基於HTTP/2協議(注意是HTTP/2協議,不是我們常使用的HTTP 1_1。HTTP/2協議詳細的介紹可以參見官方地址:https://http2.github.io/)標準而設計,基於ProtoBuf(Protocol Buffers)序列化協議開發,且支援眾多開發語言。為了支援GRPC的跨語言性,GRPC有一套獨立存在IDL語言。不過由於GRPC是Google的開源產品,在資訊格式封裝方面Google主要還是推廣的自己的ProtoBuf,所以GPRC是不支援其他資訊格式的(至少ProtoBuf效率是大家有目共睹的)。關於GRPC詳細的使用介紹,可以參見官方地址:https://github.com/grpc/grpc

  • Thrift:Thrift是Facebook的一個開源專案,後來進入Apache進行孵化。Thrift也是支援跨語言的,所以它有自己的一套IDL。目前它支援幾乎所有主流的程式語言:C++, Java, Python, PHP, Ruby, Erlang, Perl, Haskell, C#, Cocoa, JavaScript, Node.js, Smalltalk, OCaml and Delphi and other languages。Thrift可以支援多種資訊格式,除了Thrift私有的二進位制編碼規則和一種LVQ(類似於TLV訊息格式)的訊息格式,還有常規的JSON格式。Thrift的網路協議建立在TCP協議基礎上,並且支援阻塞式IO模型和多路IO複用模型。我們將在後文詳細講解Apache Thrift的使用。Thrift也是目前最流行的RPC框架之一,從網路上各種效能測試情況開,Thrift的效能都是領先的。Thrift的官網地址為:http://thrift.apache.org/

  • Hetty:Hetty是一款構建於Netty和Hessian基礎上的高效能的RPC框架。在前幾篇文章中,我已經詳細講述了使用Netty進行網路處理和直接使用JAVA原生的IO模型進行網路處理所帶來的好處(請參見我的另一篇博文《http://blog.csdn.net/yinwenjie/article/details/48969853》)。Hetty的網路協議基於HTTP,由於採用了Netty,所以Hetty支援阻塞式IO模型和多路IO複用模型。Hetty的訊息格式採用私有的二進位制流格式。

  • Dubbo:Dubbo是Alibaba開源的分散式服務框架。注意我說的是分散式服務框架,不是RPC框架(用比較嚴謹的詞語概括,應該是“服務治理框架”)。除了整合RPC的規範外,Dubbo還在RPC的上層搭建服務層功能、配置層功能、服務路由功能(加上真正的RPC規範實現總共有10層)。在後文講解“服務治理”時會重點講解Dubbo的原理和使用。

  • 其他的RPC框架:除了上訴的RPC協議的實現外,還有:Wildfly、Hprose等等。Hprose是一款國人主導的RPC實現,感興趣的讀者可以去看看(http://www.hprose.com/)。另外基於RPC的定義,Xfire,CXF這些Web Service框架也屬於RPC:WSDL描述檔案就是他們的IDL,通過WSDL為不同的程式語言生成Stub、通過不同的Web伺服器管理具體服務實現的執行過程、HTTP是它們的通訊協議、XML是它們的訊息格式。

3、RPC框架的效能依據

這裡寫圖片描述

在物理伺服器效能相同的情況下,以下幾個因素會對一款RPC框架的效能產生直接影響:

  • 所支援的網路IO模型:您的RPC伺服器可以只支援傳統的阻塞式同步IO,也可以做一些改進讓您的RPC伺服器支援非阻塞式同步IO,或者在您的伺服器上實現對多路IO模型的支援。這樣的RPC伺服器的效能在高併發狀態下,會有很大的差別。特別是單位處理效能下對記憶體、CPU資源的使用率

  • 基於的網路協議:一般來說您可以選擇讓您的RPC使用應用層協議,例如HTTP或者之前我們提到的HTTP/2協議,或者使用TCP協議,讓您的RPC框架工作在傳輸層。工作在哪一層網路上會對RPC框架的工作效能產生一定的影響,但是對RPC最終的效能影響並不大。但是至少從各種主流的RPC實現來看,沒有采用UDP協議做為主要的傳輸協議的。

  • 選擇的訊息封裝格式:選擇或者定義一種訊息格式的封裝,要考慮的問題包括:訊息的易讀性、描述單位內容時的訊息體大小、編碼難度、解碼難度、解決半包/粘包問題的難易度。當然如果您只是想定義一種RPC專用的訊息格式,那麼訊息的易讀性可能不是最需要考慮的。訊息封裝格式的設計是目前各種RPC框架效能差異的最重要原因,這就是為什麼幾乎所有主流的RPC框架都會設計私有的訊息封裝格式的原因。

  • 實現的服務處理管理方式:在高併發請求下,如何管理註冊的服務也是一個性能影響點。您可以讓RPC的Selector/Processor使用單個執行緒執行服務的具體實現(這意味著上一個客戶端的請求沒有處理完,下一個客戶端的請求就需要等待)、您也可以為每一個RPC具體服務的實現開啟一個獨立的執行緒執行(可以一次處理多個請求,但是作業系統對於“可執行的最大執行緒數”是有限制的)、您也可以執行緒池來執行RPC具體的服務實現(目前看來,在單個服務節點的情況下,這種方式是比較好的)、您還可以通過註冊代理的方式讓多個服務節點來執行具體的RPC服務實現。

4、後文介紹

後文中,我會花一篇文章的篇幅介紹Apache Thrift RPC框架的使用和Thrift 框架區別於其他RPC框架的技術特點。然後我們談談在大型系統中,針對眾多RPC服務這種情況,如何有效的對RPC服務進行管理。我們首先談談解決思路,然後試著自己解決一下這個問題。最後我們介紹Dubbo分散式服務框架,看看Dubbo是如何解決這個問題的。