简陋的RPC
使用Socket实现客户端,服务端,通信
1.客户端。调用"远程"的add(a,b)方法,就像调用本地方法一样。
2. 服务端。在另一个进程里,真正实现这个add方法。
3. 通信:设计一个简陋的协议,调用TCP Socket传输调用信息和结果】
通信
# common.py
# 这个文件定义了客户端和服务端都知道的“约定”(这就是API接口!)# 这是我们想要远程调用的方法名
METHOD_ADD = "add"# 这是我们自己设计的极其简单的“协议”
# 客户端发送的请求格式: method_name,arg1,arg2,...(用逗号分隔的字符串)
# 例如: "add,3,5"
# 服务端返回的响应格式: 直接返回结果,如 "8"
服务端
服务端监听端口,收到请求后,解析协议,调用本地方法,返回结果。服务端监听端口,收到请求后,解析协议,调用本地方法,返回结果。
# server.py
import socket
from common import METHOD_ADD# 1. 真正实现“远程”方法(服务端本地的函数)
def add(a, b):"""这就是服务端真正要执行的函数"""return a + bdef run_server(host='localhost', port=9999):# 2. 创建一个TCP Socket(搞个座机)server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)server_socket.bind((host, port)) # 告诉座机电话号码server_socket.listen(5) # 开始接听,最多5个排队print(f"✅ RPC 服务端启动在 {host}:{port},等待调用...")# 3. 死循环,一直处理客户端打来的电话while True:client_socket, addr = server_socket.accept() # 接电话print(f"📞 来自 {addr} 的连接")# 4. 读取客户端发来的请求(对方在电话里说的话)request_data = client_socket.recv(1024).decode('utf-8').strip()if not request_data:continueprint(f"📨 收到原始请求: {request_data}")# 5. 【解析协议】按照我们的约定,解析对方的话parts = request_data.split(',')method_name = parts[0]args = parts[1:]result = None# 6. 根据方法名,调用本地对应的函数if method_name == METHOD_ADD:if len(args) == 2:a, b = int(args[0]), int(args[1])result = add(a, b) # 真正调用本地函数!else:result = "Error: add方法需要2个参数"else:result = f"Error: 未知方法 {method_name}"# 7. 把函数执行结果返回给客户端print(f"📤 返回结果: {result}")client_socket.sendall(f"{result}".encode('utf-8'))# 8. 挂电话client_socket.close()if __name__ == '__main__':run_server()
客户端
# client.py
import socket
from common import METHOD_ADDclass SimpleRPCClient:def __init__(self, host='localhost', port=9999):self.host = hostself.port = portdef call(self, method_name, *args):"""真正的远程调用核心方法"""# 1. 建立到服务端的TCP连接(拨号)sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)sock.connect((self.host, self.port))# 2. 【封装协议】按照我们的约定,组装请求消息# 格式: "method_name,arg1,arg2,..."request_data = f"{method_name},{','.join(str(arg) for arg in args)}"# 3. 发送请求(对着电话说话)print(f"📤 发送请求: {request_data}")sock.sendall(request_data.encode('utf-8'))# 4. 等待并接收服务端的响应(听对方回复)response_data = sock.recv(1024).decode('utf-8')print(f"📨 收到响应: {response_data}")# 5. 关闭连接(挂电话)sock.close()# 6. 返回结果return response_data# 提供一个“本地方法”一样的假象
def add(a, b):"""客户端版本的add方法,它内部是远程调用"""client = SimpleRPCClient()result = client.call(METHOD_ADD, a, b)return int(result) # 将字符串响应转换为整数if __name__ == '__main__':# 使用方式1:直接使用底层call方法print("=== 方式1: 直接调用call方法 ===")client = SimpleRPCClient()result = client.call(METHOD_ADD, 3, 5)print(f"计算结果: {result}\n")# 使用方式2:调用包装好的函数,像本地方法一样!print("=== 方式2: 调用像本地方法一样的函数 ===")sum_result = add(10, 20)print(f"计算结果: {sum_result}")
✅ RPC 服务端启动在 localhost:9999,等待调用...
📞 来自 ('127.0.0.1', 54437) 的连接
📨 收到原始请求: add,3,5
📤 返回结果: 8
📞 来自 ('127.0.0.1', 54438) 的连接
📨 收到原始请求: add,10,20
📤 返回结果: 30
=== 方式1: 直接调用call方法 ===
📤 发送请求: add,3,5
📨 收到响应: 8
计算结果: 8=== 方式2: 调用像本地方法一样的函数 ===
📤 发送请求: add,10,20
📨 收到响应: 30
计算结果: 30
总结
1. RPC的本质:客户端代理+网络传输+服务端分发。客户端调用的add函数根本不在本地,而是通过网络告诉服务端“帮我执行一个add函数”,然后把结果给我
2.协议的作用:客户端和服务端必须有一个共同的约定(Protocol),才知道怎么打包和解析数据。这里用的是简单的逗号分隔字符串。现实中用的是Protobuf,Thrift等更高效的协议。
3. TCP的作用:提供了可靠的、双向的字节流通道。这里的“协议”字符串就是通过这个通道传输的
4. 框架的作用:Dubbo、gRPC就是把这个过程抽象化、优化。它们帮自动生成代理、管理连接池、处理序列化、负责服务器发现。让你用一行@Reference就搞定一切。