Remote Procedure Call — 远程过程调用
RPC 就是让程序员像调用本地函数一样,透明地调用远程服务器上的函数。
函数在同一个进程内,直接通过内存地址调用,速度极快。
// 本地调用 int result = calculate(a, b); // 直接执行,返回结果
函数在另一台机器上,需要网络通信,但调用方式完全一样。
// 远程调用 - 代码完全相同 int result = rpc.calculate(a, b); // 网络传输,返回结果
| 特性 | RPC | REST API | 消息队列 |
|---|---|---|---|
| 调用模式 | 请求-响应 | 请求-响应 | 异步发送 |
| 耦合度 | 低(接口抽象) | 中(HTTP协议) | 极低(解耦) |
| 性能 | 高(二进制) | 中(JSON/HTTP) | 看队列实现 |
| 适用场景 | 微服务间调用 | 前后端分离 | 异步任务、解耦 |
点击下方步骤查看详细说明:
客户端像调用本地函数一样发起调用。这个调用实际上被拦截了,由 Client Stub(客户端存根)接收。
// 伪代码示例 // 看起来像本地调用 int userId = getUserByName("张三"); // 实际上调用的是 Stub // Client Stub: 我收到了调用请求,准备打包参数
Client Stub 将方法名和参数打包成二进制数据。这个过程叫序列化(Serialize/Marshal)。
// 原始调用 getUserByName("张三") // 序列化后变成二进制 \x00\x00\x00\x03 // 字符串长度 E5 BC A0 E4 B8 89 // "张三"的UTF-8编码
通过 TCP/UDP 将二进制数据发送到远程服务器。这一步涉及协议栈、路由、防火墙等。
// 传输层协议选择 TCP: 可靠连接 - gRPC 默认使用 UDP: 高效但不可靠 - 某些高性能场景
服务器端 Server Stub 收到数据后,将二进制数据还原为原始参数。这个过程叫反序列化(Deserialize/Unmarshal)。
// 二进制数据 \x00\x00\x00\x03 E5 BC A0 E4 B8 89 // 反序列化为 getUserByName("张三")
Server Stub 调用真正的服务端函数(实际业务逻辑)并获取返回值。
// 真正的服务端函数 int getUserByName(String name) { // 查数据库、缓存、计算... return database.findUserId(name); }
结果按原路返回:服务端序列化 → 网络传输 → 客户端反序列化,最终返回给调用方。
// 完整往返流程 客户端 ---调用---> 序列化 ---网络---> Server Stub 反序列化 ---网络---> 序列化 ---返回---> 结果 Server
用接口定义语言(如 Protocol Buffers、Thrift)声明服务接口,自动生成客户端/服务端代码。
客户端代理,负责将本地调用转换为远程请求,处理序列化、网络通信细节。
服务端代理,负责接收请求、反序列化、调用实际服务、返回结果。
基于 TCP/UDP/HTTP/QUIC 等协议,负责数据的可靠或高效传输。
将对象转换为字节流(序列化)和反向转换(反序列化),是性能关键。
让客户端能找到服务端地址,如 Consul、Zookeeper、Nacos 等组件。
Google 出品,二进制格式
文本格式,人类可读
二进制 JSON
Facebook 出品
从业务代码到底层网络,每一层都有明确的职责边界
// 1. 定义 IDL 接口 syntax = "proto3"; service UserService { rpc GetUser(UserRequest) returns (UserResponse); rpc ListUsers(ListRequest) returns (stream UserResponse); } message UserRequest { string name = 1; } message UserResponse { int32 id = 1; string name = 2; string email = 3; }
// 2. 客户端调用 - 和本地函数调用几乎一样! import grpc import user_pb2 as pb2 import user_pb2_grpc as pb2_grpc # 连接服务端 channel = grpc.insecure_channel('localhost:50051') stub = pb2_grpc.UserServiceStub(channel) # 调用远程方法 - 就像本地函数一样 request = pb2.UserRequest(name="张三") response = stub.GetUser(request) print(f"用户ID: {response.id}, 邮箱: {response.email}")
RPC 的核心价值在于透明性:让分布式调用像本地调用一样简单。 通过序列化、网络传输、服务治理等机制, 实现了高性能、跨语言、跨平台的远程通信能力。