目錄
1、概念
2、RPC架構
2.1 RPC的四個核心組件
2.2 訪問流程
3、關鍵概念
3.1 接口定義語言?(IDL - Interface Definition Language)
3.2?序列化與反序列化 (Serialization & Deserialization - Marshalling/Unmarshalling)
3.3?網絡傳輸 (Transport)
3.4?服務發現 (Service Discovery)?
3.5?容錯與重試 (Fault Tolerance & Retry):
3.6?安全 (Security)
3.7?異步與流式 (Asynchronous & Streaming)
4、RPC優缺點分析
5、常見RPC框架及其特點
6、現代趨勢
1、概念
背景:分布式系統的組件分散在不同的虛擬機器上,需通過網絡進行交互協作以完成共同的任務。
網絡遠程通信的基本原理:基于傳輸協議和網絡IO來實現。其中傳輸協議有tcp、udp等,基于socket概念上擴展的協議。網絡IO主要有bio、nio、aio三種方式。
RPC概念:遠程過程調用的機制,不是一個具體的技術,RPC的核心目標,是讓開發者調用遠程機器上的函數(或方法)就像調用本地函數一樣簡單。開發者只關注于業務邏輯,而不是處理底層的Socket連接、數據序列化、反序列化、錯誤處理等繁瑣細節。
2、RPC架構
2.1 RPC的四個核心組件
-
客戶端 (Client):?發起遠程調用的應用程序。
-
客戶端存根 (Client Stub / Proxy):RPC 框架在客戶端生成的代碼(或動態代理),存放服務端的地址消息,將客戶端的請求打包成網絡消息(序列化),然后通過網絡遠程發送給服務方。
-
服務端存根 (Server Stub / Skeleton):?RPC 框架在服務端生成的代碼(或動態代理),接收客戶端發送過來的消息,將消息解包(反序列化),并調用本地的方法。
-
服務端(Server):真正的服務提供者。
2.2 訪問流程
1. 客戶端(client)以本地調用方式(即以接口的方式)調用服務;
2. 客戶端存根(client stub)接收到調用后,負責將方法、參數等組裝成能夠進行網絡傳輸的消息體(將消息體對象序列化為二進制);
3. 客戶端通過socket將消息發送到服務端;
4. 服務端存根( server stub)收到消息后進行解碼(將消息對象反序列化);
5. 服務端存根( server stub)根據解碼結果調用本地的服務;
6. 服務處理
7. 本地服務執行并將結果返回給服務端存根( server stub);
8. 服務端存根( server stub)將返回結果打包成消息(將結果消息對象序列化);
9. 服務端(server)通過socket將消息發送到客戶端;
10. 客戶端存根(client stub)接收到結果消息,并進行解碼(將結果消息反序列化);
11. 客戶端(client)得到最終結果。
RPC的目標就是將黃色部分的步驟封裝起來。
java中的RPC框架:常見的有Hessian、gRPC、Dubbo等
3、關鍵概念
3.1 接口定義語言?(IDL - Interface Definition Language)
-
核心概念:?一種與具體編程語言無關的語言,用于嚴格定義服務接口(方法、參數、返回值、數據結構、甚至異常)。
-
作用:
-
跨語言支持:?不同語言的客戶端和服務器可以通過 IDL 文件生成各自語言的 Stub 和 Skeleton 代碼。
-
明確契約:?作為服務提供者和消費者之間的明確、可共享的合同。
-
代碼生成:?RPC 框架提供編譯器/工具,根據 IDL 文件自動生成客戶端代理代碼和服務器端骨架代碼,減少手寫網絡通信代碼的工作量和錯誤。
-
-
常見 IDL:?Protocol Buffers (
.proto
), Apache Thrift (.thrift
), Avro IDL (.avdl
), CORBA IDL, Web Services Description Language (WSDL - 用于 SOAP)。 - 示例:Apache Thrift--Facebook的跨語言RPC框架,支持多種傳輸協議(TCP/HTTP等)。
// 定義服務 (calculator.thrift)
service Calculator {i32 add(1:i32 num1, 2:i32 num2),i32 subtract(1:i32 num1, 2:i32 num2)
}
Java使用步驟:
1>?用Thrift編譯器生成代碼:
thrift --gen java calculator.thrift
2>?實現接口:
public class CalculatorHandler implements Calculator.Iface {public int add(int num1, int num2) { return num1 + num2; }public int subtract(int num1, int num2) { return num1 - num2; }
}
3>?啟動Thrift服務端,客戶端通過Client類調用。
3.2?序列化與反序列化 (Serialization & Deserialization - Marshalling/Unmarshalling)
-
核心概念:?將內存中的數據結構或對象轉換為適合網絡傳輸或存儲的字節流的過程稱為序列化。將接收到的字節流還原為內存中對象的過程稱為反序列化。
-
關鍵要求:
-
高效性:?編碼/解碼速度快,產生的數據體積小(減少網絡帶寬和延遲)。
-
跨語言性:?序列化后的數據能被不同語言編寫的程序正確解析(IDL 是基礎)。
-
兼容性:?支持數據模式的演進(向前/向后兼容),允許服務接口和數據結構逐步升級而不破壞現有客戶端或服務器。
-
安全性:?防止惡意構造的數據導致反序列化漏洞。
-
-
常見技術:
-
二進制協議:?Protocol Buffers (Protobuf), Apache Thrift (Binary), Apache Avro, MessagePack, Hessian。通常體積小、速度快。
-
文本協議:?JSON, XML。可讀性好,但體積較大、解析較慢。常用于 RESTful API 和調試。
-
語言內置:?Java Serializable, Python Pickle。通常缺乏跨語言支持和良好的兼容性控制,不推薦用于生產環境 RPC。
-
-
示例:?Apache Thrift (Binary Protocol),支持多種傳輸協議(Binary/Compact/JSON)
// person.thrift
struct Person {1: string name,2: i32 id,3: list<string> emails
}
// 序列化
Person person = new Person("Alice", 123, Arrays.asList("alice@example.com"));
ByteArrayOutputStream baos = new ByteArrayOutputStream();
TBinaryProtocol protocol = new TBinaryProtocol(new TIOStreamTransport(baos));
person.write(protocol); // 序列化
byte[] bytes = baos.toByteArray();// 反序列化
ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
TBinaryProtocol inProto = new TBinaryProtocol(new TIOStreamTransport(bais));
Person decoded = new Person();
decoded.read(inProto); // 反序列化
System.out.println(decoded.getName()); // 輸出: Alice
3.3?網絡傳輸 (Transport)
-
核心概念:?負責在客戶端 Stub 和服務器 Skeleton 之間可靠地傳輸序列化后的請求和響應數據。
-
協議選擇:
-
TCP:?最常用,提供可靠的、面向連接的字節流傳輸。是大多數 RPC 框架的基礎。
-
HTTP/1.1:?基于 TCP,無狀態,文本協議(雖然 Body 可以是二進制)。通用性強,易于通過防火墻和代理。但性能開銷相對大,有隊頭阻塞問題。
-
HTTP/2:?解決了 HTTP/1.1 的隊頭阻塞問題,支持多路復用、頭部壓縮、服務器推送等特性,顯著提升性能,已成為現代 RPC 框架(如 gRPC)的首選傳輸層。
-
HTTP/3 (QUIC):?基于 UDP,旨在進一步減少延遲,解決 TCP 隊頭阻塞問題,提供更好的移動網絡體驗,是未來趨勢。
-
自定義協議:?一些高性能框架(如 Dubbo)可能使用自定義的二進制協議直接在 TCP 上傳輸。
-
3.4?服務發現 (Service Discovery)?
-
核心概念:?客戶端如何找到它想調用的服務實例的網絡位置(IP + Port)?尤其是在服務實例動態變化(擴縮容、故障重啟)的分布式環境中。
-
機制:
-
靜態配置:?直接在客戶端配置服務端地址列表。簡單但缺乏彈性,不適合動態環境。
-
動態發現:
-
注冊中心 (Registry):?服務實例啟動時向一個中心化的注冊中心(如 ZooKeeper, etcd, Consul, Nacos, Eureka)注冊自己的元數據(服務名、IP、Port、健康狀態等)。客戶端通過查詢注冊中心獲取可用服務實例列表。
-
DNS:?通過 DNS SRV 記錄或自定義域名解析來發現服務。更新可能不夠實時。
-
-
-
負載均衡 (Load Balancing):?通常與服務發現緊密結合。客戶端從注冊中心獲取服務實例列表后,需要選擇一個實例來發送請求。策略包括:輪詢 (Round Robin)、隨機 (Random)、加權輪詢/隨機、最少連接數 (Least Connections)、一致性哈希 (Consistent Hashing - 保證相同請求路由到相同實例,利于緩存)。
3.5?容錯與重試 (Fault Tolerance & Retry):
-
網絡是不可靠的,服務實例可能臨時故障。RPC 框架通常提供容錯機制:
-
超時 (Timeout):客戶端設置一個等待響應的最大時間。防止因服務器掛起或網絡問題導致客戶端無限期等待,釋放資源。
-
重試 (Retry):?在遇到可重試錯誤(如網絡瞬時故障、目標實例暫時不可用)時自動重試請求。需要謹慎設計(重試次數上限、重試間隔(固定、指數退避 - Exponential Backoff)、可重試錯誤類型判斷),避免造成服務雪崩。
-
熔斷 (Circuit Breaking):?當某個服務失敗率達到閾值時,暫時停止對其的請求(熔斷器打開),直接返回失敗或降級結果,給故障服務恢復的時間。過一段時間后嘗試放行少量請求(半開狀態),如果成功則關閉熔斷器。
-
降級 (Fallback):?當調用失敗或熔斷時,提供一種備選方案(如返回緩存數據、默認值、簡化結果)以保證核心功能的可用性。
-
3.6?安全 (Security)
-
認證 (Authentication):?驗證調用者的身份(你是誰?)。常用方式:API Key/Secret, OAuth 2.0, JWT (JSON Web Tokens), mTLS (雙向 TLS)。
-
授權 (Authorization):?驗證調用者是否有權限執行該操作(你能做什么?)。常用方式:基于角色 (RBAC) 或基于屬性 (ABAC) 的訪問控制。
-
傳輸加密 (Encryption):?使用 TLS/SSL 對網絡傳輸的數據進行加密,防止竊聽和篡改。
3.7?異步與流式 (Asynchronous & Streaming)
-
異步調用:?客戶端發起調用后不阻塞等待結果,而是立即返回一個 Future/Promise 對象或注冊一個回調函數。當結果準備好時,通過 Future/Promise 獲取或調用回調函數通知客戶端。提高資源利用率和吞吐量。
-
流式 RPC:?允許在單個連接上建立雙向的、持續的數據流。客戶端可以發送一個請求流,服務器可以返回一個響應流,或者建立雙向流。
-
優勢:?非常適合傳輸大量數據集(如文件上傳/下載)、實時消息推送、聊天等場景。
-
技術基礎:?HTTP/2 或 HTTP/3 的多路復用特性是實現高效流式 RPC 的關鍵(如 gRPC Stream)。
-
4、RPC優缺點分析
優點:
-
開發效率高:?屏蔽了底層網絡復雜性,開發者像調用本地函數一樣開發分布式調用,降低心智負擔,代碼更簡潔。
-
抽象清晰:?IDL 明確定義了服務接口,強類型約束,減少接口不一致的錯誤。
-
跨語言能力:?基于 IDL 的代碼生成天然支持多語言互操作,方便構建異構系統。
-
性能潛力:?使用高效的二進制序列化協議(如 Protobuf, Thrift)和基于 TCP 的傳輸(或 HTTP/2),通常比基于文本的 RESTful API(尤其是 JSON over HTTP/1.1)具有更高的性能和更低的延遲。
-
適合內部通信:?在微服務架構內部,服務之間需要頻繁、高效、結構化的通信,RPC 是非常自然和高效的選擇。
缺點:
-
耦合性增加:?服務接口一旦通過 IDL 發布并生成代碼,修改就需要同步更新客戶端和服務端代碼(雖然 Schema Evolution 可以緩解,但仍有成本)。相比 REST 的松散耦合(通過 URI 和 HTTP 動詞),RPC 的接口耦合更緊密。
-
調試復雜性:?跨進程、跨機器的調用使得調試比本地調用更困難。需要分布式追蹤工具(如 Jaeger, Zipkin)來跟蹤請求鏈路。
-
技術棧綁定:?客戶端和服務端需要支持相同的 RPC 框架和 IDL 生成器。雖然框架本身支持多語言,但選擇了一個框架,整個系統通常就綁定在這個框架的生態上。
-
防火墻穿透:?RPC 框架通常使用非 HTTP 標準端口或自定義協議,可能在企業防火墻環境中遇到阻礙。基于 HTTP/2 的 RPC(如 gRPC)在這方面有優勢。
-
復雜性下沉:?雖然對業務開發者隱藏了網絡復雜性,但 RPC 框架本身的實現、部署、監控、治理(服務發現、負載均衡、熔斷限流)帶來了額外的運維復雜性。需要一個成熟的微服務基礎設施來支撐。
RPC vs REST:
特性 | RPC | REST (Representational State Transfer) |
---|---|---|
核心范式 | 動作/過程?(調用遠程函數) | 資源?(操作資源的狀態) |
接口定義 | 強契約 (IDL),嚴格定義方法簽名 | 弱契約,通過 URI 和 HTTP 動詞隱含定義 |
耦合性 | 較高?(客戶端依賴特定服務接口) | 較低?(客戶端只依賴資源 URI 和 HTTP 語義) |
通信協議 | 自定義協議或基于 HTTP/1.1/HTTP/2 | 幾乎總是基于 HTTP/1.1 |
數據格式 | 高效二進制 (Protobuf, Thrift) 或文本 | 文本為主 (JSON, XML) |
性能 | 通常更高?(二進制 + 高效協議) | 通常較低 (文本解析 + HTTP/1.1 開銷) |
適用場景 | 內部服務間高效、結構化通信?(微服務) | 對公 API、需要松散耦合、更易調試/理解的場景 |
可發現性 | 弱 (通常需要文檔或 IDL) | 強?(利用 HTTP OPTIONS, HATEOAS 理念) |
技術棧綁定 | 強 (需要特定 RPC 框架支持) | 弱 (任何能發 HTTP 請求的語言/工具) |
例子 | gRPC, Thrift, Dubbo 方法調用 | GET /users/123 ,?POST /orders |
5、常見RPC框架及其特點
-
gRPC (Google):?基于 HTTP/2 和 Protocol Buffers (IDL & 序列化)。高性能,跨語言支持極好,原生支持流式調用。云原生領域事實標準。
-
Apache Dubbo (Alibaba):?Java 生態高性能框架。提供豐富的服務治理能力(注冊中心、配置中心、監控等),插件化擴展能力強。
-
Apache Thrift (Facebook):?跨語言 RPC 框架,有自己的 IDL 和多種序列化/傳輸協議選擇。相對成熟穩定。
-
JSON-RPC / XML-RPC:?簡單、輕量級、基于文本(JSON/XML),易于調試和集成,但性能通常不如二進制協議。
-
.NET Remoting / WCF:?微軟 .NET 平臺的 RPC 技術(WCF 更強大,支持多種通信協議和模式)。
6、現代趨勢
-
云原生集成:?與 Kubernetes、Service Mesh (Istio, Linkerd) 深度集成,利用其服務發現、負載均衡、安全、可觀測性能力。
-
Protocol Buffers / gRPC 主導:?因其高效性、強類型契約和良好的跨語言支持,成為新項目的主流選擇。
-
異步非阻塞 & 流式優先:?高性能框架普遍采用 NIO/協程模型,并加強流式處理能力。
-
可觀測性內置:?指標、日志、追蹤成為現代 RPC 框架的標配或易集成項。
-
多協議支持:?一些框架(如 Dubbo)支持同時暴露多種協議(如 Dubbo, gRPC, REST)的端點,方便不同消費者接入。