
刚学分布式时,我被“远程过程调用(RPC)”这个名字唬住了——听起来像魔法:在A机器上调用B机器上的一个函数,就像调用本地函数一样。
但拆开来看,本质上就三件事:
明白了这个,我决定自己写一个最简版本,不依赖任何现成RPC框架。
我选择用JSON,因为人类可读、调试方便。通信协议定义为一个字典:
# 请求格式
request = {
"method": "add", # 要调用的函数名
"params": [3, 5], # 参数列表
"id": 1 # 请求编号,用于匹配响应
}
# 响应格式
response = {
"result": 8, # 执行结果
"error": None, # 错误信息
"id": 1 # 对应请求的编号
}这是RPC框架中最核心的设计决策——双方必须看懂同一套“语言”。
服务端要做的事情很简单:启动一个Socket监听,收到请求后解析JSON,找到对应的函数执行,再返回结果。
import json
import socket
# 注册可调用的函数
functions = {
"add": lambda a, b: a + b,
"sub": lambda a, b: a - b
}
def handle_request(data):
req = json.loads(data)
func = functions.get(req["method"])
if not func:
return {"error": "method not found", "id": req["id"]}
result = func(*req["params"])
return {"result": result, "id": req["id"]}
# 启动TCP服务
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind(("localhost", 8888))
server.listen(1)
while True:
conn, _ = server.accept()
data = conn.recv(1024).decode()
resp = handle_request(data)
conn.send(json.dumps(resp).encode())
conn.close()服务端代码不到30行,核心逻辑就是查表->执行->返回。
客户端的职责是:让程序员感觉像在调用普通函数,实际上背后发起了网络请求。
我用了一个代理类来实现“魔法”:
import json
import socket
class RPCClient:
def __init__(self, host, port):
self.host = host
self.port = port
self.id = 0
def __getattr__(self, method):
# 动态拦截属性访问,返回一个可调用的函数
def caller(*args):
self.id += 1
req = {"method": method, "params": args, "id": self.id}
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect((self.host, self.port))
sock.send(json.dumps(req).encode())
resp = json.loads(sock.recv(1024).decode())
sock.close()
if resp.get("error"):
raise Exception(resp["error"])
return resp["result"]
return caller使用的时候,代码简洁得不像网络编程:
client = RPCClient("localhost", 8888)
# 看起来就像在调用本地函数
print(client.add(3, 5)) # 输出: 8
print(client.sub(10, 4)) # 输出: 6Python的__getattr__魔法方法在这里发挥了关键作用——它拦截了client.add这样的访问,动态构造网络请求。
我故意省略了很多生产级RPC框架的必要组件:
但这些“缺失”恰恰是我想展示的——RPC的核心只有那几十行代码,其余全是工程优化。
写完这个玩具后,再看Dubbo、gRPC的文档,我不再感到陌生。因为我知道它们在解决什么问题:
当你的服务有100个节点、每秒处理10万请求时,连接管理、序列化效率、服务发现才成为难题。但如果你只是调用另一个进程的函数,一个TCP端口+JSON就足够了。
技术框架往往被包装得很复杂,但剥开外壳,底层原理往往朴素得惊人。如果你也想理解RPC,别急着读源码——先花半小时写一个最小的版本,胜过看三天文档。
后记:这个框架后来被我用来做跨语言通信(Python服务端调用Go客户端函数),只需要两边约定相同的JSON协议即可。协议是RPC的“宪法”,比代码更重要。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。