天天看點

深入淺出 RPC - 淺出篇

近幾年的項目中,服務化和微服務化漸漸成為中大型分布式系統架構的主流方式,而 RPC 在其中扮演着關鍵的作用。在平時的日常開發中我們都在隐式或顯式的使用 RPC,一些剛入行的程式員會感覺 RPC 比較神秘,而一些有多年使用 RPC 經驗的程式員雖然使用經驗豐富,但有些對其原理也不甚了了。缺乏對原理層面的了解,往往也會造成開發中的一些誤用。

本文分上下兩篇《淺出篇》和《深入篇》,其目标就是想嘗試深入淺出的分析下 RPC 本質,我總是這麼認為了解了本質才能更好的應用。

RPC 是什麼?

RPC 的全稱是 Remote Procedure Call 是一種程序間通信方式。它允許程式調用另一個位址空間(通常是共享網絡的另一台機器上)的過程或函數,而不用程式員顯式編碼這個遠端調用的細節。即程式員無論是調用本地的還是遠端的,本質上編寫的調用代碼基本相同。

RPC 起源

RPC 這個概念術語在上世紀 80 年代由 Bruce Jay Nelson 提出。這裡我們追溯下當初開發 RPC 的原動機是什麼?在 Nelson 的論文 "Implementing Remote Procedure Calls" 中他提到了幾點:

1. 簡單:RPC 概念的語義十厘清晰和簡單,這樣建立分布式計算就更容易。

2. 高效:過程調用看起來十分簡單而且高效。

3. 通用:在單機計算中過程往往是不同算法部分間最重要的通信機制。 

通俗一點說,就是一般程式員對于本地的過程調用很熟悉,那麼我們把 RPC 作成和本地調用完全類似,那麼就更容易被接受,使用起來毫無障礙。Nelson 的論文發表于 30 年前,其觀點今天看來确實高瞻遠矚,今天我們使用的 RPC 架構基本就是按這個目标來實作的。

RPC 結構

Nelson 的論文中指出實作 RPC 的程式包括 5 個部分:

1. User

2. User-stub

3. RPCRuntime

4. Server-stub

5. Server

這 5 個部分的關系如下圖所示

這裡 user 就是 client 端,當 user 想發起一個遠端調用時,它實際是通過本地調用 user-stub。user-stub 負責将調用的接口、方法和參數通過約定的協定規範進行編碼并通過本地的 RPCRuntime 執行個體傳輸到遠端的執行個體。遠端 RPCRuntime 執行個體收到請求後交給 server-stub 進行解碼後發起本地端調用,調用結果再傳回給 user 端。

RPC 實作

Nelson 論文中給出的這個實作結構也成為後來大家參考的标準範本。大約 10 年前,我最早接觸分布式計算時使用的 CORBAR 實作結構基本與此類似。CORBAR 為了解決異構平台的 RPC,使用了 IDL(Interface Definition Language)來定義遠端接口,并将其映射到特定的平台語言中。後來大部分的跨語言平台 RPC 基本都采用了此類方式,比如我們熟悉的 Web Service(SOAP),近年開源的 Thrift 等。他們大部分都通過 IDL 定義,并提供工具來映射生成不同語言平台的 user-stub 和 server-stub,并通過架構庫來提供 RPCRuntime 的支援。不過貌似每個不同的 RPC 架構都定義了各自不同的 IDL 格式,導緻程式員的學習成本進一步上升(苦逼啊),Web Service 嘗試建立業界标準,無賴标準規範複雜而效率偏低,否則 Thrift 等更高效的 RPC 架構就沒必要出現了。

IDL 是為了跨平台語言實作 RPC 不得已的選擇,要解決更廣泛的問題自然導緻了更複雜的方案。而對于同一平台内的 RPC 而言顯然沒必要搞個中間語言出來,例如 java 原生的 RMI,這樣對于 java 程式員而言顯得更直接簡單,降低使用的學習成本。目前市面上提供的 RPC 架構已經可算是五花八門,百家争鳴了。需要根據實際使用場景謹慎選型,需要考慮的選型因素我覺得至少包括下面幾點:

1. 性能名額

2. 是否需要跨語言平台

3. 内網開放還是公網開放

4. 開源 RPC 架構本身的品質、社群活躍度

總結

rpc