跳转至

极简 RPC 骨架实现

一、RPC 要解决什么

让本地调用远程方法像调用本地方法一样。核心步骤:

  1. 代理:客户端生成接口代理对象。
  2. 序列化:方法名、参数序列化成字节。
  3. 网络传输:把字节发给服务端。
  4. 反序列化 + 反射调用:服务端根据字节调用真实方法。
  5. 返回结果序列化发回客户端。

二、最小实现

1. 公共接口

public interface HelloService {
    String sayHello(String name);
}

2. 服务端实现

public class HelloServiceImpl implements HelloService {
    public String sayHello(String name) {
        return "Hello, " + name;
    }
}

3. 服务端骨架

用 JDK HttpServer 监听端口,反射调用:

public class RpcServer {
    public void start(int port, Object service) throws Exception {
        HttpServer server = HttpServer.create(new InetSocketAddress(port), 0);
        server.createContext("/rpc", exchange -> {
            byte[] req = exchange.getRequestBody().readAllBytes();
            // 反序列化:得到 className, methodName, paramTypes, args
            RpcRequest rpcReq = JSON.parseObject(req, RpcRequest.class);

            Method m = service.getClass()
                .getMethod(rpcReq.getMethodName(), rpcReq.getParamTypes());
            Object result = m.invoke(service, rpcReq.getArgs());

            byte[] resp = JSON.toJSONBytes(result);
            exchange.sendResponseHeaders(200, resp.length);
            try (OutputStream os = exchange.getResponseBody()) {
                os.write(resp);
            }
        });
        server.start();
    }
}

4. 客户端代理

public class RpcClient {
    @SuppressWarnings("unchecked")
    public static <T> T proxy(Class<T> interfaceClass, String host, int port) {
        return (T) Proxy.newProxyInstance(
            interfaceClass.getClassLoader(),
            new Class<?>[]{interfaceClass},
            (proxy, method, args) -> {
                RpcRequest req = new RpcRequest(
                    interfaceClass.getName(),
                    method.getName(),
                    method.getParameterTypes(),
                    args
                );
                // HTTP 发请求
                HttpURLConnection conn = (HttpURLConnection)
                    new URL("http://" + host + ":" + port + "/rpc").openConnection();
                conn.setRequestMethod("POST");
                conn.getOutputStream().write(JSON.toJSONBytes(req));
                // 读响应
                byte[] resp = conn.getInputStream().readAllBytes();
                return JSON.parseObject(resp, method.getReturnType());
            }
        );
    }
}

5. 使用

// 服务端
new RpcServer().start(8080, new HelloServiceImpl());

// 客户端
HelloService service = RpcClient.proxy(HelloService.class, "127.0.0.1", 8080);
System.out.println(service.sayHello("World"));

三、生产级 RPC 要加什么

能力 作用
服务注册发现 不用写死 IP
负载均衡 多提供者选一个
序列化优化 Protobuf / Kryo
网络框架 Netty,不用 HTTP
超时重试 容错
监控埋点 耗时、错误率
压缩 gzip

面试加分

  • 手写 RPC 核心考:动态代理、序列化、网络通信、反射调用
  • 升级版:用 Netty + Hessian2 + Zookeeper/Nacos 注册中心。