简陋的RPC

发布于:2025-09-06 ⋅ 阅读:(16) ⋅ 点赞:(0)

使用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 + b

def 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:
            continue
        
        print(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_ADD

class SimpleRPCClient:
    def __init__(self, host='localhost', port=9999):
        self.host = host
        self.port = port

    def 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就搞定一切。


网站公告

今日签到

点亮在社区的每一天
去签到