RPC(Remote Procedure Call,远程过程调用)是一种计算机通信协议,允许程序调用另一个地址空间(通常是远程服务器)的函数或方法,就像调用本地函数一样。RPC 抽象了底层网络通信细节,使开发者能专注于业务逻辑。
1. RPC 核心概念
(1) 基本流程
-
客户端调用本地存根(Stub)方法。
-
存根将调用信息序列化为网络消息(如JSON、Protobuf)。
-
消息通过网络传输到服务端。
-
服务端存根反序列化消息并调用实际方法。
-
结果沿相反路径返回给客户端。
(2) 关键组件
组件 | 作用 |
---|---|
Client | 调用远程服务的程序 |
Server | 提供远程服务的程序 |
Stub | 代理对象,隐藏网络细节(客户端存根发起请求,服务端存根处理请求) |
序列化协议 | 将数据转换为网络传输格式(如JSON、XML、Protobuf、MessagePack) |
传输协议 | 通信底层协议(如HTTP/1.1、HTTP/2、TCP、WebSocket) |
2. RPC 工作原理
3. RPC 主要实现方式
基于 HTTP 的 RPC
-
特点:使用HTTP协议,兼容性强。
-
示例:
-
RESTful API(严格来说不是RPC,但可通过POST模拟)。
-
XML-RPC(使用XML格式)。
-
JSON-RPC(轻量级,适合Web)。
-
JSON-RPC 示例(Python)
# 服务端
from jsonrpcserver import method, serve
@method
def add(a, b):
return a + b
serve('localhost', 5000)
# 客户端
import requests
response = requests.post(
"http://localhost:5000",
json={"jsonrpc": "2.0", "method": "add", "params": [10, 20], "id": 1}
)
print(response.json()) # 输出: {"result": 30, "id": 1}
gRPC
(Google RPC)
gRPC 是一个高性能、跨语言的RPC框架,基于HTTP/2和Protocol Buffers(protobuf)。
安装
pip install grpcio grpcio-tools
示例
(1) 定义服务(.proto
文件)
创建 calculator.proto
:
syntax = "proto3";
service Calculator {
rpc Add (AddRequest) returns (AddResponse);
}
message AddRequest {
int32 a = 1;
int32 b = 2;
}
message AddResponse {
int32 result = 1;
}
(2) 生成Python代码
python -m grpc_tools.protoc -I. --python_out=. --grpc_python_out=. calculator.proto
生成 calculator_pb2.py
和 calculator_pb2_grpc.py
。
(3) 实现服务端
# server.py
from concurrent import futures
import grpc
import calculator_pb2
import calculator_pb2_grpc
class CalculatorServicer(calculator_pb2_grpc.CalculatorServicer):
def Add(self, request, context):
result = request.a + request.b
return calculator_pb2.AddResponse(result=result)
def serve():
server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
calculator_pb2_grpc.add_CalculatorServicer_to_server(CalculatorServicer(), server)
server.add_insecure_port('[::]:50051')
server.start()
server.wait_for_termination()
if __name__ == '__main__':
serve()
(4) 实现客户端
# client.py
import grpc
import calculator_pb2
import calculator_pb2_grpc
def run():
channel = grpc.insecure_channel('localhost:50051')
stub = calculator_pb2_grpc.CalculatorStub(channel)
response = stub.Add(calculator_pb2.AddRequest(a=10, b=20))
print("Result:", response.result)
if __name__ == '__main__':
run()
(5) 运行
# 启动服务端
python server.py
# 在另一个终端运行客户端
python client.py
# 输出: Result: 30
2. 使用 XML-RPC
(内置库)
Python标准库内置了xmlrpc
,适合简单场景。
服务端
# xmlrpc_server.py
from xmlrpc.server import SimpleXMLRPCServer
def add(a, b):
return a + b
server = SimpleXMLRPCServer(('localhost', 8000))
server.register_function(add, 'add')
print("Server running on port 8000...")
server.serve_forever()
客户端
# xmlrpc_client.py
import xmlrpc.client
proxy = xmlrpc.client.ServerProxy('http://localhost:8000/')
result = proxy.add(10, 20)
print("Result:", result) # 输出: 30
3. 使用 Pyro5
(纯Python RPC)
Pyro5 是一个纯Python的RPC库,支持对象传递和复杂数据类型。
安装
pip install Pyro5
服务端
# pyro_server.py
import Pyro5.api
@Pyro5.api.expose
class Calculator:
def add(self, a, b):
return a + b
daemon = Pyro5.api.Daemon()
uri = daemon.register(Calculator)
print("URI:", uri) # 例如: PYRO:obj_12345@localhost:9090
daemon.requestLoop()
客户端
# pyro_client.py
import Pyro5.api
uri = "PYRO:obj_12345@localhost:9090" # 替换为服务端输出的URI
calculator = Pyro5.api.Proxy(uri)
result = calculator.add(10, 20)
print("Result:", result) # 输出: 30
4. 使用 ZeroMQ
(消息队列 + RPC)
ZeroMQ 不是严格的RPC框架,但可以通过消息模式实现类似功能。
安装
pip install zmq
服务端
# zmq_server.py
import zmq
context = zmq.Context()
socket = context.socket(zmq.REP)
socket.bind("tcp://*:5555")
while True:
a, b = socket.recv_json()
result = a + b
socket.send_json({"result": result})
客户端
# zmq_client.py
import zmq
context = zmq.Context()
socket = context.socket(zmq.REQ)
socket.connect("tcp://localhost:5555")
socket.send_json({"a": 10, "b": 20})
response = socket.recv_json()
print("Result:", response["result"]) # 输出: 30
4. RPC 的核心问题
(1) 网络可靠性
-
问题:网络延迟、丢包、超时。
-
解决方案:
-
超时机制(如gRPC默认5秒超时)。
-
重试策略(指数退避)。
-
(2) 序列化效率
-
问题:JSON/XML效率低,占用带宽大。
-
解决方案:
-
使用二进制协议(如Protobuf、MessagePack)。
-
压缩数据(如gzip)。
-
(3) 服务发现
-
问题:如何找到服务端地址?
-
解决方案:
-
静态配置:直接写死IP(仅测试用)。
-
动态注册中心:如Zookeeper、Consul、ETCD。
-
(4) 安全性
-
问题:数据明文传输可能被窃听。
-
解决方案:
-
TLS/SSL加密(如gRPC的
secure_channel
)。 -
认证机制(如OAuth2、JWT)。
-
5. 性能对比
方法 | 协议 | 性能 | 跨语言支持 | 适用场景 |
---|---|---|---|---|
gRPC | HTTP/2 | ⭐⭐⭐⭐ | ✅ | 高性能微服务 |
XML-RPC | HTTP/XML | ⭐ | ✅ | 简单遗留系统 |
Pyro5 | 自定义 | ⭐⭐ | ❌ | 纯Python项目 |
ZeroMQ | TCP/自定义 | ⭐⭐⭐ | ✅ | 低延迟分布式系统 |
6. RPC 与 REST 对比
特性 | RPC | REST |
---|---|---|
通信模式 | 函数调用 | 资源操作(CRUD) |
协议 | 自定义或二进制 | HTTP/HTTPS |
性能 | 高(二进制序列化) | 中(文本序列化) |
灵活性 | 低(强耦合接口) | 高(无状态,松耦合) |
适用场景 | 内部服务调用 | 公开API |
总结
-
需要高性能和跨语言支持 → 选择 gRPC。
-
快速原型开发 → 使用 XML-RPC 或 Pyro5。
-
灵活消息通信 → 尝试 ZeroMQ。
-
RPC 本质:像调用本地函数一样调用远程服务。
-
核心挑战:网络不可靠、序列化效率、服务发现、安全。
根据项目需求选择合适的方法,大多数现代分布式系统会优先考虑gRPC。