分佈式通信之遠程調用

前言

分佈式的本質就是多進程協作,共同完成任務。要協作,自然免不了通信。從今天開始就來介紹多個進程之間通信的相關內容,那麼首先先介紹分佈式通信中的遠程調用

什麼是遠程調用?

以電商購物平臺爲例,每一筆交易都涉及訂單系統支付系統庫存系統,假設三個系統分別部署在三臺機器 A、B、C 中獨立運行,訂單交易流程如下所示:

  1. 用戶下單時,調用本地(機器 A)的訂單系統進行下單;
  2. 下單完成後,會遠程調用機器 B 上的支付系統進行支付,待支付完成後返回結果,之後在本地更新訂單狀態;
  3. 在本地遠程調用機器 C 上的倉庫系統出貨,出貨完成後返回出貨結果。

在整個過程中,“下單”和“訂單狀態更新”兩個操作屬於本地調用,而“支付”和“出貨”這兩個操作是通過本地的訂單系統調用其他兩個機器上的函數(方法)實現的,屬於遠程調用。

整個訂單交易流程如下圖所示。
在這裏插入圖片描述

  • 本地調用通常指的是:進程內函數之間的相互調用;
  • 遠程調用,是進程間函數的相互調用,是進程間通信 IPC(Inter-Process Communication)的一種方式。通過遠程調用,一個進程可以看到其他進程的函數、方法等。

在分佈式領域中,一個系統由很多服務組成,不同的服務由各自的進程單獨負責,因此,遠程調用在分佈式通信中尤爲重要。
根據進程是否部署在同一臺機器上,遠程調用可以分爲如下兩類:

  • 本地過程調用(Local Procedure Call,LPC),是指運行在同一臺機器上的進程之間的互相通信,即在多進程操作系統中,運行的不同進程之間可以通過 LPC進行函數調用
  • 遠程過程調用(Remote Procedure Call,RPC),是指不同機器中運行的進程之間的相互通信,某一機器上運行的進程在不知道底層通信細節的情況下,就像訪問本地服務一樣,去調用遠程機器上的服務。

在這兩種遠程調用中,RPC 中的不同進程是跨機器的,適用於分佈式場景,也是本文介紹的重點。

遠程調用的原理及應用

我們熟知的 B/S ( Browser/Server,瀏覽器 / 服務器) 架構。在這種架構中,被調用方(服務器)有一個開放的接口,然後調用方(用戶)通過 Browser 使用這個接口,來間接調用被調用方相應的服務,從而實現遠程調用。

但是,B/S 架構是基於 HTTP 協議實現的,每次調用接口時,都需要先進行 HTTP 請求。既繁瑣又費時,不適用於有低時延要求的大規模分佈式系統,所以遠程調用的實現大多采用更底層的網絡通信協議。

接下來,介紹兩種常用的遠程調用機制:

  • 遠程過程調用 RPC(Remote Procedure Call)
  • 遠程方法調用 RMI(Remote Method Invocation)

RPC 的原理及應用

RPC 就是調用方採用參數傳遞的方式,通過調用本機器上的一個函數或方法,去執行遠程機器上的函數或方法(可以統稱爲服務),並返回結果。在整個過程中,RPC 會隱藏具體的通信細節。

如下圖所示,以電商購物平臺的“支付”操作爲例,來了解 RPC 調用的完整流程:
在這裏插入圖片描述

  1. 本地服務器也就是機器 A 中的訂單系統,調用本地服務器上的支付系統中的支付操作 Pay(Order),該方法會直接調用 ClientStub(其中,Stub 是用於轉換 RPC 過程中在訂單系統和支付系統所在機器之間傳遞的參數),這是一次正常的本地調用。
  2. Client Stub 將方法 Pay、參數 Order 等打包成一個適合網絡傳輸的消息,通過執行一次系統調用(也就是調用操作系統中的函數)來發送消息。
  3. 訂單系統所在機器 A 的本地操作系統通過底層網絡通信,將打包好的消息根據支付系統所在機器 B 的地址發送出去。
  4. 機器 B 上的操作系統接收到消息後,將消息傳遞給 Server Stub。
  5. 機器 B 上的 Server Stub 將接收到的消息進行解包,獲得裏面的參數,然後調用本地的支付訂單的操作 Pay(Order)。
  6. 機器 B 上的支付操作 Pay(Order) 完成後,將結果發送給 Server Stub,其中結果可使用 XDR(External Data Representation,一種可以在不同計算機系統間傳輸的數據格式)語言表示。
  7. 機器 B 上的 Server Stub 將結果數據打包成適合網絡傳輸的消息,然後進行一次系統調用發送消息。
  8. 機器 B 的本地操作系統通過底層網絡將打包好的消息發送回機器 A。
  9. 機器 A 的操作系統接收到來自機器 B 的消息,並將消息發送給本地的 Client Stub。
    10.本地的 Client Stub 將消息解包,然後將解包得到的結果返回給本地的訂單系統。

至此整個 RPC 過程結束。

從整個流程可以看出,機器 A 上的 Pay(Order)、 Client Stub 和網絡調用之間的交互屬於本地調用,機器 B 上的 Pay(Order)、Server Stub 和網絡調用之間的交互也屬於本地調用。

而機器 A 和機器 B 之間的遠程調用的核心是,發生在機器 A 上的網絡調用和機器 B 上的網絡調用。RPC 的目的,其實就是要將第 2 到第 8 步的幾個過程封裝起來,讓用戶看不到這些細節。使得從用戶的角度看,訂單系統的進程只是做了一次普通的本地調用,然後就得到了結果。
訂單系統進程並不需要知道底層是如何傳輸的,在用戶眼裏,遠程過程調用和調用一次本地服務沒什麼不同。這,就是 RPC 的核心

RPC 與本地調用(進程內函數調用)的區別

在 RPC 中,由於不同進程內存空間無法共享,且涉及網絡傳輸,所以不像本地調用那麼簡單。RPC 與本地調用主要有三點不同:
第一個區別是,調用 ID 和函數的映射。
在本地調用中,進程內可共享內存地址空間,因此程序可直接通過函數名來調用函數。而函數名的本質就是一個函數指針,可以看成函數在內存中的地址。比如,調用函數 f(),編譯器會幫我們找到函數 f() 相應的內存地址。

但在 RPC 中,只通過函數名是不行的,因爲不同進程的地址空間是不一樣的

所以在 RPC 中,所有的函數必須要有一個調用 ID 來唯一標識。一個機器上運行的進程在做遠程過程調用時,必須附上這個調用 ID

另外,我們還需要在通信的兩臺機器間,分別維護一個函數與調用 ID 的映射表。兩臺機器維護的表中,相同的函數對應的調用 ID 必須保持一致。

當一臺機器 A 上運行的進程 P 需要遠程調用時,它就先查一下機器 A 維護的映射表,找出對應的調用 ID,然後把它傳到另一臺機器 B 上,機器 B 通過查看它維護的映射表,從而確定進程 P 需要調用的函數,然後執行對應的代碼,最後將執行結果返回到進程 P。

第二個區別是,序列化和反序列化。
我們知道了調用方調用遠程服務時,需要向被調用方傳輸調用 ID 和對應的函數參數,那調用方究竟是怎麼把這些數據傳給被調用方的呢?

在本地調用中,進程之間共享內存等,因此我們只需要把參數壓到棧裏,然後進程自己去棧裏讀取就行。但是在 RPC 中,兩個進程分佈在不同的機器上,使用的是不同機器的內存,因此不可能通過內存來傳遞參數。

網絡協議傳輸的內容是二進制流,無法直接傳輸參數的類型,因此這就需要調用方把參數先轉成一個二進制流,傳到被調用方後,被調用方再把二進制流轉換成自己能讀取的格式。這個過程,就叫作序列化和反序列化

同理,被調用方返回的結果也需要有序列化和反序列化的過程,不然調用方無法獲取到結果。也就是說,RPC 與本地調用相比,參數的傳遞需要進行序列化和反序列化操作

第三個區別是,網絡傳輸協議。
序列化和反序列化解決了調用方和被調用方之間的數據傳輸格式問題,但要想序列化後的數據能在網絡中順利傳輸,還需要有相應的網絡協議,比如 TCP、UDP 等,因此就需要有一個底層通信層。

調用方通過該通信層把調用 ID 和序列化後的參數傳給被調用方,被調用方同樣需要該通信層將序列化後的調用結果返回到調用方。

也就是說,只要調用方和被調用方可以互傳數據,就可以作爲這個底層通信層。因此,它所使用的網絡協議可以有很多,只要能完成網絡傳輸即可。目前來看,大部分 RPC 框架採用的是 TCP 協議。

RPC 框架 Apache Dubbo

Dubbo 引入了服務註冊中心的基礎上,又加入了監控中心組件(用來監控服務的調用情況,以方便進行服務治理),從而實現了一個 RPC 框架。

如下圖所示,Dubbo 的架構主要包括 4 部分:

  • 服務提供方:服務提供方會向服務註冊中心註冊自己提供的服務。
  • 服務註冊中心:服務註冊與發現中心,負責存儲和管理服務提供方註冊的服務信息和服務調用方訂閱的服務類型等。
  • 服務調用方:根據服務註冊中心返回的服務所在的地址列表,通過遠程調用訪問遠程服務。
  • 監控中心:統計服務的調用次數和調用時間等信息的監控中心,以方便進行服務管理或服務失敗分析等。

在這裏插入圖片描述
可以看到,Dubbo 的大致工作流程如下:

  1. 服務提供方需要向服務註冊中心註冊自己提供的服務;
  2. 服務調用方需要向註冊中心預訂調用服務的提供方地址列表;
  3. 服務註冊中心將服務對應的提供方地址列表返回給調用方;
  4. 服務調用方根據服務地址信息進行遠程服務調用;
  5. 服務調用方和服務提供方定時向監控中心發送服務調用次數及調用時間等信息。

RMI 的原理及應用

RMI 是一個基於 Java 環境的應用編程接口,能夠讓本地 Java 虛擬機上運行的對象,像調用本地對象一樣調用遠程 Java 虛擬機上的對象。

RMI 的具體原理如下圖所示:

RMI 可以說是 RPC 的一種具體形式,其原理與 RPC 基本一致,唯一不同的是 RMI 是基於對象的,充分利用了面向對象的思想去實現整個過程,其本質就是一種基於對象的 RPC 實現。
RMI 的具體原理如下圖所示:
在這裏插入圖片描述
RMI 的實現中,客戶端的訂單系統中的 Stub 是客戶端的一個輔助對象,用於與服務端實現遠程調用;服務端的支付系統中 Skeleton 是服務端的一個輔助對象,用於與客戶端實現遠程調用。

RPC 與 RMI 對比分析

在這裏插入圖片描述

總結

在這裏插入圖片描述

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