您好,登錄后才能下訂單哦!
RPC采用客戶機/服務器模式。請求程序就是一個客戶機,而服務提供程序就是一個服務器。首先,客戶機調用進程發送一個有進程參數的調用信息到服務進程,然后等待應答信息。在服務器端,進程保持睡眠狀態直到調用信息的到達為止。當一個調用信息到達,服務器獲得進程參數,計算結果,發送答復信息,然后等待下一個調用信息,最后,客戶端調用進程接收答復信息,獲得進程結果,然后調用執行繼續進行。
在一個典型 RPC 的使用場景中,包含了服務發現、負載、容錯、網絡傳輸、序列化等組件,其中“RPC 協議”就指明了程序如何進行網絡傳輸和序列化
運行時,一次客戶機對服務器的RPC調用,其內部操作大致有如下十步:
1.調用客戶端句柄;執行傳送參數
2.調用本地系統內核發送網絡消息
3.消息傳送到遠程主機
4.服務器句柄得到消息并取得參數
5.執行遠程過程
6.執行的過程將結果返回服務器句柄
7.服務器句柄返回結果,調用遠程系統內核
8.消息傳回本地主機
9.客戶句柄由內核接收消息
10.客戶接收句柄返回的數據
RPC 的核心功能是指實現一個 RPC 最重要的功能模塊,就是上圖中的”RPC 協議”部分:
下面分別介紹核心 RPC 框架的重要組成:
一次 RPC 調用流程如下:
接口調用通常包含兩個部分,序列化和通信協議。常見的序列化協議包括json、xml、hession、protobuf、thrift、text、bytes等;通信比較流行的是http、soap、websockect,RPC通常基于TCP實現。那么restful使用的序列化協議通常是json,通信協議是http;rpc是一種通信協議,因此如果序列化使用json的話,那么就是json-rpc
RPC 的核心功能主要由 5 個模塊組成,如果想要自己實現一個 RPC,最簡單的方式要實現三個技術點,分別是:
服務尋址可以使用 Call ID 映射。在本地調用中,函數體是直接通過函數指針來指定的,但是在遠程調用中,函數指針是不行的,因為兩個進程的地址空間是完全不一樣的。
所以在 RPC 中,所有的函數都必須有自己的一個 ID。這個 ID 在所有進程中都是唯一確定的。
客戶端在做遠程過程調用時,必須附上這個 ID。然后我們還需要在客戶端和服務端分別維護一個函數和Call ID的對應表。
當客戶端需要進行遠程調用時,它就查一下這個表,找出相應的 Call ID,然后把它傳給服務端,服務端也通過查表,來確定客戶端需要調用的函數,然后執行相應函數的代碼。
實現方式:服務注冊中心。
Registry(服務發現):借助 JNDI 發布并調用了 RMI 服務。實際上,JNDI 就是一個注冊表,服務端將服務對象放入到注冊表中,客戶端從注冊表中獲取服務對象。
RMI 服務在服務端實現之后需要注冊到 RMI Server 上,然后客戶端從指定的 RMI 地址上 Lookup 服務,調用該服務對應的方法即可完成遠程方法調用。
Registry 是個很重要的功能,當服務端開發完服務之后,要對外暴露,如果沒有服務注冊,則客戶端是無從調用的,即使服務端的服務就在那里
客戶端怎么把參數值傳給遠程的函數呢?在本地調用中,我們只需要把參數壓到棧里,然后讓函數自己去棧里讀就行。
但是在遠程過程調用時,客戶端跟服務端是不同的進程,不能通過內存來傳遞參數。
這時候就需要客戶端把參數先轉成一個字節流,傳給服務端后,再把字節流轉成自己能讀取的格式。
只有二進制數據才能在網絡中傳輸,序列化和反序列化的定義是:
這個過程叫序列化和反序列化。同理,從服務端返回的值也需要序列化反序列化的過程。
網絡傳輸:遠程調用往往用在網絡上,客戶端和服務端是通過網絡連接的。
所有的數據都需要通過網絡傳輸,因此就需要有一個網絡傳輸層。網絡傳輸層需要把 Call ID 和序列化后的參數字節流傳給服務端,然后再把序列化后的調用結果傳回客戶端。
只要能完成這兩者的,都可以作為傳輸層使用。因此,它所使用的協議其實是不限的,能完成傳輸就行。
盡管大部分 RPC 框架都使用 TCP 協議,但其實 UDP 也可以,而 gRPC 干脆就用了 HTTP2。
TCP 的連接是最常見的,簡要分析基于 TCP 的連接:通常 TCP 連接可以是按需連接(需要調用的時候就先建立連接,調用結束后就立馬斷掉),也可以是長連接(客戶端和服務器建立起連接之后保持長期持有,不管此時有無數據包的發送,可以配合心跳檢測機制定期檢測建立的連接是否存活有效),多個遠程過程調用共享同一個連接。
所以,要實現一個 RPC 框架,只需要把以下三點實現了就基本完成了:
在 RPC 中可選的網絡傳輸方式有多種,可以選擇 TCP 協議、UDP 協議、HTTP 協議。
由服務的調用方與服務的提供方建立 Socket 連接,并由服務的調用方通過 Socket 將需要調用的接口名稱、方法名稱和參數序列化后傳遞給服務的提供方,服務的提供方反序列化后再利用反射調用相關的方法。
將結果返回給服務的調用方,整個基于 TCP 協議的 RPC 調用大致如此。
但是在實例應用中則會進行一系列的封裝,如 RMI 便是在 TCP 協議上傳遞可序列化的 Java 對象。
該方法更像是訪問網頁一樣,只是它的返回結果更加單一簡單。
其大致流程為:由服務的調用者向服務的提供者發送請求,這種請求的方式可能是 GET、POST、PUT、DELETE 等中的一種,服務的提供者可能會根據不同的請求方式做出不同的處理,或者某個方法只允許某種請求方式。
而調用的具體方法則是根據 URL 進行方法調用,而方法所需要的參數可能是對服務調用方傳輸過去的 XML 數據或者 JSON 數據解析后的結果,返回 JOSN 或者 XML 的數據結果。
由于目前有很多開源的 Web 服務器,如 Tomcat,所以其實現起來更加容易,就像做 Web 項目一樣
基于 TCP 的協議實現的 RPC 調用,由于 TCP 協議處于協議棧的下層,能夠更加靈活地對協議字段進行定制,減少網絡開銷,提高性能,實現更大的吞吐量和并發數。
但是需要更多關注底層復雜的細節,實現的代價更高。同時對不同平臺,如安卓,iOS 等,需要重新開發出不同的工具包來進行請求發送和相應解析,工作量大,難以快速響應和滿足用戶需求。
基于 HTTP 協議實現的 RPC 則可以使用 JSON 和 XML 格式的請求或響應數據。
而 JSON 和 XML 作為通用的格式標準(使用 HTTP 協議也需要序列化和反序列化,不過這不是該協議下關心的內容,成熟的 Web 程序已經做好了序列化內容),開源的解析工具已經相當成熟,在其上進行二次開發會非常便捷和簡單。
但是由于 HTTP 協議是上層協議,發送包含同等內容的信息,使用 HTTP 協議傳輸所占用的字節數會比使用 TCP 協議傳輸所占用的字節數更高。
因此在同等網絡下,通過 HTTP 協議傳輸相同內容,效率會比基于 TCP 協議的數據效率要低,信息傳輸所占用的時間也會更長,當然壓縮數據,能夠縮小這一差距。
在 OpenStack 中服務與服務之間使用 RESTful API 調用,而在服務內部則使用 RPC 調用各個功能模塊。
正是由于使用了 RPC 來解耦服務內部功能模塊,使得 OpenStack 的服務擁有擴展性強,耦合性低等優點。
OpenStack 的 RPC 架構中,加入了消息隊列 RabbitMQ,這樣做的目的是為了保證 RPC 在消息傳遞過程中的安全性和穩定性。
下面分析 OpenStack 中使用 RabbitMQ 如何實現 RPC 的調用。
使用 RabbitMQ 的好處:
RPC 主要用于公司內部的服務調用,性能消耗低,傳輸效率高,實現復雜
RESTful API 主要用于對外的異構環境,瀏覽器接口調用,App 接口調用,第三方接口調用等
RPC 使用場景(大型的網站,內部子系統較多、接口非常多的情況下適合使用 RPC):
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。