PRC演化DEMO

一 简介

RPC(Remote Procedure Call)远程过程调用协议,一种通过网络从远程计算机上请求服务,而不需要了解底层网络技术的协议。RPC它假定某些协议的存在,例如TPC/UDP等,为通信程序之间携带信息数据。在OSI网络七层模型中,RPC跨越了传输层和应用层,RPC使得开发,包括网络分布式多程序在内的应用程序更加容易。

客户端在不知道调用细节的情况下,调用存在于远程计算机上的某个对象,就像调用本地应用程序中的对象一样。

1.1 哪些框架支持 RPC?

RPC 框架可以分为以下几类:

  • 基于 TCP/IP 的 RPC 框架:如 gRPC、Apache Thrift、Dubbo 等。
  • 基于 HTTP 的 RPC 框架:如 RESTful API、gRPC-Web 等。
  • 基于消息队列的 RPC 框架:如 RabbitMQ、ZeroMQ 等。
  1. Thrift
    thrift 是一个软件框架,用来进行可扩展且跨语言的服务的开发,包括C++, Java, Python等多种编程语言。

  2. Dubbo
    是一个分布式服务框架,以及 SOA 治理方案。其功能主要包括:高性能 NIO 通讯及多协议集成,服务动态寻址与路由,软负载均衡与容错,依赖分析与降级等

  3. Spring Cloud
    提供了搭建分布式系统及微服务常用的工具,满足了构建微服务所需的所有解决方案。

  4. gRPC
    一开始由 google 开发,是一款语言中立、平台中立、开源的远程过程调用( RPC )系统。

二、手撸一个RPC

接下来,我们一步一步开始RPC的演化过程。

2.1 准备工作

首先准备一个用户类

package org.example.common;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;

/**
 * 用户
 * @author tanzhipeng
 * @date 2025/05/14 12:03
 **/
@Data
@AllArgsConstructor
@EqualsAndHashCode
public class User {

    private int id;

    private String name;

    private int age;
}

给用户类提供一个查询接口:

package org.example.common;

/**
 * 用户接口
 */
public interface IUserService {

    /**
     * 获取用户
     */
    User getUser(int id);
}

2.2 最原始的二进制调用

  1. 实现用户类
package org.example.rpc01;

import org.example.common.IUserService;
import org.example.common.User;

/**
 * 用户实现
 * @author tanzhipeng
 * @date 2025/05/14 12:08
 **/
public class UserServiceImpl implements IUserService {

    @Override
    public User getUser(int id) {
        return new User(1,"LikeCat",18);
    }
}
  1. 实现服务端
package org.example.rpc01;

import org.example.common.User;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * 服务端
 * @author tanzhipeng
 * @date 2025/05/14 12:09
 **/
public class Server {
    private static final int PORT = 8888;
    private static final boolean RUNNING = Boolean.TRUE;

    public static void main(String[] args) throws IOException {
        ServerSocket serverSocket = new ServerSocket(PORT);
        while (RUNNING) {
            Socket socket = serverSocket.accept();
            process(socket);
            socket.close();
        }
        serverSocket.close();
    }

    private static void process(Socket socket) throws IOException {
        InputStream in = socket.getInputStream();
        OutputStream out = socket.getOutputStream();

        DataInputStream dis = new DataInputStream(in);
        DataOutputStream dos = new DataOutputStream(out);

        int id = dis.readInt();
        User user = new UserServiceImpl().getUser(id);
        dos.writeInt(user.getId());
        dos.writeUTF(user.getName());
        dos.writeInt(user.getAge());

        dos.flush();
    }
}
  1. 实现客户端
package org.example.rpc01;

import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;

/**
 * 客户端
 * @author tanzhipeng
 * @date 2025/05/14 12:09
 **/
public class Client {
    private static final int PORT = 8888;

    public static void main(String[] args) throws IOException {
        Socket socket = new Socket("localhost", PORT);
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        DataOutputStream dos = new DataOutputStream(bos);

        int id = 1;
        dos.writeInt(id);

        socket.getOutputStream().write(bos.toByteArray());
        socket.getOutputStream().flush();

        DataInputStream dis = new DataInputStream(socket.getInputStream());
        int userId = dis.readInt();
        String name = dis.readUTF();
        int age = dis.readInt();

        System.out.println(userId);
        System.out.println(name);
        System.out.println(age);

        dos.close();
        socket.close();
    }
}

2.3 封装代理类

在查询用户的时候,在客户端每次我们都需要进行Socket相关的编程,其实我们可以把整个流程封装起来,客户端无需关心网络以及业务处理,只需要调用相应方法就能取得对应结果,而对于网络细节则交给其他类。

package org.example.rpc02;

import org.example.common.User;

import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;

/**
 * 代理类
 * @author tanzhipeng
 * @date 2025/05/14 12:42
 **/
public class Stub {

    public User findUser(Integer id,String host ,Integer port) throws IOException {
        Socket socket = new Socket(host, port);
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        DataOutputStream dos = new DataOutputStream(bos);

        dos.writeInt(id);

        socket.getOutputStream().write(bos.toByteArray());
        socket.getOutputStream().flush();

        DataInputStream dis = new DataInputStream(socket.getInputStream());
        int userId = dis.readInt();
        String name = dis.readUTF();
        int age = dis.readInt();

        System.out.println(userId);
        System.out.println(name);
        System.out.println(age);

        dos.close();
        socket.close();
        
        return new User(userId, name, age);
    }
}

在客户端调用就只需要关注对应方法就行了。

package org.example.rpc02;

import org.example.common.User;

import java.io.IOException;

/**
 * 客户端2
 * @author tanzhipeng
 * @date 2025/05/14 12:45
 **/
public class Client {

    public static void main(String[] args) throws IOException {

        Stub stub = new Stub();
        User user = stub.findUser(1, "localhost", 8888);
        System.out.println(user);
    }
}

2.4 增强代理类

通过上述代理我们发现一个问题,Stub里面只有一个方法,如果此时我们再新增一个findByName方法,又要重写写一套Socket,这显然是不合理的。

为此,我们有了如下的改进:

package org.example.rpc03;

import org.example.common.IUserService;
import org.example.common.User;

import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.net.Socket;

/**
 * 代理类
 * @author tanzhipeng
 * @date 2025/05/14 12:42
 **/
public class Stub {

    public static IUserService getStub(){
        InvocationHandler h = new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                Socket socket = new Socket("localhost", 8888);
                ByteArrayOutputStream bos = new ByteArrayOutputStream();
                DataOutputStream dos = new DataOutputStream(bos);

                dos.writeInt(1);

                socket.getOutputStream().write(bos.toByteArray());
                socket.getOutputStream().flush();

                DataInputStream dis = new DataInputStream(socket.getInputStream());
                int userId = dis.readInt();
                String name = dis.readUTF();
                int age = dis.readInt();

                System.out.println(userId);
                System.out.println(name);
                System.out.println(age);

                dos.close();
                socket.close();

                return new User(userId, name, age);
            }
        };

        Object o = Proxy.newProxyInstance(IUserService.class.getClassLoader(), new Class[]{IUserService.class}, h);
        return (IUserService) o;
    }
}

此时,我们只需专注业务,并不需要关心网络相关的逻辑,此时客户端的流程变成了:

package org.example.rpc03;

import org.example.common.IUserService;
import org.example.common.User;

import java.io.IOException;

/**
 * 客户端2
 * @author tanzhipeng
 * @date 2025/05/14 12:45
 **/
public class Client {

    public static void main(String[] args) throws IOException {

        IUserService i = Stub.getStub();

        User user = i.getUser(1);
        System.out.println(user);
    }
}

2.4 二次增强代理类

此时,又有新的问题产生了,我们知道,每次代理对象的方法调用,最终都会执行到InvocationHandler当中去,这个时候我们会发现,其实每次执行的都是同一个方法同一套逻辑,拿到的也行相同的结果,这显然是不可取的。

要如何改进,首先至少在InvocationHandler当中,我们需要一个通用版本的实现。

package org.example.rpc04;

import org.example.common.IUserService;
import org.example.common.User;

import java.io.DataInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.net.Socket;

/**
 * 代理类  提供服务器端需要调用哪个方法以及传入对应参数
 * @author tanzhipeng
 * @date 2025/05/14 12:42
 **/
public class Stub {

    public static IUserService getStub(){
        InvocationHandler h = new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                Socket socket = new Socket("localhost", 8888);

                ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());

                String methodName = method.getName();
                // 获取方法的参数类型
                Class<?>[] parameterTypes = method.getParameterTypes();
                oos.writeUTF(methodName);
                oos.writeObject(parameterTypes);
                oos.writeObject(args);
                oos.flush();

                DataInputStream dis = new DataInputStream(socket.getInputStream());
                int userId = dis.readInt();
                String name = dis.readUTF();
                int age = dis.readInt();

                System.out.println(userId);
                System.out.println(name);
                System.out.println(age);

                socket.close();
                oos.close();
                return new User(userId, name, age);
            }
        };

        Object o = Proxy.newProxyInstance(IUserService.class.getClassLoader(), new Class[]{IUserService.class}, h);
        return (IUserService) o;
    }
}

这时候我们给服务端传送了我们想调用的方法和参数,所以相应的服务端我们也需要改变一下。

package org.example.rpc04;

import java.io.DataOutputStream;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.OutputStream;
import java.lang.reflect.Method;
import java.net.ServerSocket;
import java.net.Socket;

import org.example.common.IUserService;
import org.example.common.User;
import org.example.rpc01.UserServiceImpl;

/**
 * 服务端
 * 
 * @author tanzhipeng
 * @date 2025/05/14 12:09
 **/
public class Server {
    private static final int PORT = 8888;
    private static final boolean RUNNING = Boolean.TRUE;

    public static void main(String[] args) throws Exception {
        ServerSocket serverSocket = new ServerSocket(PORT);
        while (RUNNING) {
            Socket socket = serverSocket.accept();
            process(socket);
            socket.close();
        }
        serverSocket.close();
    }

    private static void process(Socket socket) throws Exception {
        InputStream in = socket.getInputStream();
        OutputStream out = socket.getOutputStream();

        ObjectInputStream ois = new ObjectInputStream(in);
        DataOutputStream dos = new DataOutputStream(out);

        String methodName = ois.readUTF();
        Class[] parameterTypes = (Class[])ois.readObject();
        Object[] args = (Object[])ois.readObject();

        // 通过反射调用
        IUserService service = new UserServiceImpl();
        Method method = service.getClass().getMethod(methodName, parameterTypes);
        User user = (User)method.invoke(service, args);

        dos.writeInt(user.getId());
        dos.writeUTF(user.getName());
        dos.writeInt(user.getAge());

        dos.flush();
    }
}

客户端直接调用:

package org.example.rpc04;

import java.io.IOException;

import org.example.common.IUserService;
import org.example.common.User;

/**
 * 客户端4
 * @author tanzhipeng
 * @date 2025/05/14 12:45
 **/
public class Client {

    public static void main(String[] args) throws IOException {

        IUserService i = Stub.getStub();

        User user = i.getUser(1);
        System.out.println(user);
    }
}

2.5 封装返回值

在上面的实现中,对于返回值User对象,我们是拆解成一个个属性了,其实可以直接返回一个user对象,这样可以支持任意类型的对象。

package org.example.rpc05;

import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.lang.reflect.Method;
import java.net.ServerSocket;
import java.net.Socket;

import org.example.common.IUserService;
import org.example.common.User;
import org.example.rpc01.UserServiceImpl;

/**
 * 服务端
 **/
public class Server {
    private static final int PORT = 8888;
    private static final boolean RUNNING = Boolean.TRUE;

    public static void main(String[] args) throws Exception {
        ServerSocket serverSocket = new ServerSocket(PORT);
        while (RUNNING) {
            Socket socket = serverSocket.accept();
            process(socket);
            socket.close();
        }
        serverSocket.close();
    }

    private static void process(Socket socket) throws Exception {
        InputStream in = socket.getInputStream();
        OutputStream out = socket.getOutputStream();

        ObjectInputStream ois = new ObjectInputStream(in);
        ObjectOutputStream dos = new ObjectOutputStream(out);

        String methodName = ois.readUTF();
        Class[] parameterTypes = (Class[])ois.readObject();
        Object[] args = (Object[])ois.readObject();

        // 通过反射调用
        IUserService service = new UserServiceImpl();
        Method method = service.getClass().getMethod(methodName, parameterTypes);
        User user = (User)method.invoke(service, args);

        dos.writeObject(user);

        dos.flush();
    }
}
package org.example.rpc05;

import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.net.Socket;

import org.example.common.IUserService;
import org.example.common.User;

/**
 * 代理类
 **/
public class Stub {

    public static IUserService getStub(){
        InvocationHandler h = new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                Socket socket = new Socket("localhost", 8888);

                ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());

                String methodName = method.getName();
                // 获取方法的参数类型
                Class<?>[] parameterTypes = method.getParameterTypes();
                oos.writeUTF(methodName);
                oos.writeObject(parameterTypes);
                oos.writeObject(args);
                oos.flush();

                ObjectInputStream dis = new ObjectInputStream(socket.getInputStream());
                User user = (User) dis.readObject();

                System.out.println(user.toString());

                socket.close();
                oos.close();
                return user;
            }
        };

        Object o = Proxy.newProxyInstance(IUserService.class.getClassLoader(), new Class[]{IUserService.class}, h);
        return (IUserService) o;
    }
}

到此,我们实现了对于服务端UserService的代理调用,无论UserService怎么新增方法和修改User类,对客户端都没影响。

2.6 提供更好的代理类型服务

随着业务的发展,又出现了新的问题,此时可能有新的类产生,不局限于UserService,此时上面的代理方法能力已经不够了。

这时应该怎么处理?

首先想到的是在getStub方法中,不能只返回IUserService,而是应该返回一个Object。所以我们只需要提供给服务端对应的class。

代码如下:

package org.example.rpc06;

import java.io.IOException;

import org.example.common.IUserService;

/**
 * 客户端6
 **/
public class Client {

    public static void main(String[] args) throws IOException {
        IUserService i = Stub.getStub(IUserService.class);
        System.out.println(i.getUser(1));
    }
}

在Stub中传入class给服务端

package org.example.rpc06;

import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.net.Socket;

import org.example.common.IUserService;
import org.example.common.User;

/**
 * 代理类
 **/
public class Stub {

    public static IUserService getStub(Class classz){
        InvocationHandler h = new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                Socket socket = new Socket("localhost", 8888);

                ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());

                String classzName = classz.getName();
                String methodName = method.getName();
                // 获取方法的参数类型
                Class<?>[] parameterTypes = method.getParameterTypes();
                oos.writeUTF(classzName);
                oos.writeUTF(methodName);
                oos.writeObject(parameterTypes);
                oos.writeObject(args);
                oos.flush();

                ObjectInputStream dis = new ObjectInputStream(socket.getInputStream());
                User user = (User) dis.readObject();

                System.out.println(user.toString());

                socket.close();
                oos.close();
                return user;
            }
        };

        Object o = Proxy.newProxyInstance(classz.getClassLoader(), new Class[]{IUserService.class}, h);
        return (IUserService) o;
    }
}

对应的在服务端通过传入的class找到相应的类,并执行方法:

package org.example.rpc06;

import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.lang.reflect.Method;
import java.net.ServerSocket;
import java.net.Socket;

import org.example.common.User;
import org.example.rpc01.UserServiceImpl;

/**
 * 服务端
 **/
public class Server {
    private static final int PORT = 8888;
    private static final boolean RUNNING = Boolean.TRUE;

    public static void main(String[] args) throws Exception {
        ServerSocket serverSocket = new ServerSocket(PORT);
        while (RUNNING) {
            Socket socket = serverSocket.accept();
            process(socket);
            socket.close();
        }
        serverSocket.close();
    }

    private static void process(Socket socket) throws Exception {
        InputStream in = socket.getInputStream();
        OutputStream out = socket.getOutputStream();

        ObjectInputStream ois = new ObjectInputStream(in);
        ObjectOutputStream dos = new ObjectOutputStream(out);

        String className = ois.readUTF();
        String methodName = ois.readUTF();
        Class[] parameterTypes = (Class[])ois.readObject();
        Object[] args = (Object[])ois.readObject();

        //从服务注册表找到具体的类,在spring中可以通过spring容器找到对应的实现
        Class classzz = UserServiceImpl.class;
        Method method = classzz.getMethod(methodName, parameterTypes);

        User user = (User)method.invoke(classzz.newInstance(), args);

        dos.writeObject(user);

        dos.flush();
    }
}

上述代码中的实现可以通过Spring容器去找到最终的实现类并执行方法。

2.7 序列化

RPC 能帮助我们的应用透明地完成远程调用,即调用其他服务器的函数就像调用本地方法一样。发起调用请求的那一方叫做调用方,被调用的一方叫做服务提供方。

调用方和服务提供方一般是不同的服务器,所以就需要通过网络来传输数据,并且 RPC 常用于业务系统之间的数据交互,需要保证其可靠性,所以 RPC 一般默认采用 TCP 协议来传输。同时, HTTP 协议也是建立在 TCP 之上的。

网络传输的数据必须是二进制数据,但调用方请求的出入参数都是对象,而对象是肯定没法直接在网络中传输的,需要提前把「对象转成二进制数据」进行网络传输,这个转换过程就做序列化。相反,服务提供方收到网络数据后,需要将「二进制数据转成对象」,这个转换过程就叫做反序列化。

那么常用的序列化方式有哪些呢?

  1. JDK原生序列化
  2. Hessian
  3. Protobuf
  4. Thrift
  5. Json
  6. ...

这么多种序列化方式,如何去选型呢?

首先我们需要考虑的是,对于序列化来说,我们考虑的优先级如下:

安全性 > 通用性 > 兼容性 > 性能 > 效率 > 空间开销

首选Hessian与Protobuf,性能、时间开销、空间开销、通用性、兼容性和安全性上,都满足要求:

  1. Hessian使用更方便,在对象的兼容性上更好
  2. Protobuf则更加高效,更通用

2.7.2 JDK

package org.example.rpc07;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

import org.example.common.User;

/**
 * JDK
 **/
public class HelloJDK {
    public static void main(String[] args) throws Exception {
        User user = new User(1, "LikeCat", 18);
        byte[] bytes = serialize(user);
        System.out.println(bytes.length);

        User user1 = (User) deserialize(bytes);
        System.out.println(user1.toString());
    }

    /**
     * 序列化
     */
    private static byte[] serialize(Object o) throws Exception {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ObjectOutputStream output = new ObjectOutputStream(baos);

        output.writeObject(o);
        output.flush();

        byte[] bytes = baos.toByteArray();
        baos.close();
        output.close();
        return bytes;
    }

    /**
     * 反序列化
     */
    private static Object deserialize(byte[] bytes) throws Exception {
        ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
        ObjectInputStream input = new ObjectInputStream(bais);

        Object o = input.readObject();
        bais.close();
        input.close();
        return o;
    }
}

2.7.2 Hessian

package org.example.rpc07;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;

import org.example.common.User;

import com.caucho.hessian.io.Hessian2Input;
import com.caucho.hessian.io.Hessian2Output;

/**
 * Hessian
 **/
public class HelloHessian {
    public static void main(String[] args) throws Exception {
        User user = new User(1, "LikeCat", 18);
        byte[] bytes = serialize(user);
        System.out.println(bytes.length);

        User user1 = (User) deserialize(bytes);
        System.out.println(user1.toString());
    }

    /**
     * 序列化
     */
    private static byte[] serialize(Object o) throws Exception {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        Hessian2Output output = new Hessian2Output(baos);

        output.writeObject(o);
        output.flush();

        byte[] bytes = baos.toByteArray();
        baos.close();
        output.close();
        return bytes;
    }

    /**
     * 反序列化
     */
    private static Object deserialize(byte[] bytes) throws Exception {
        ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
        Hessian2Input input = new Hessian2Input(bais);

        Object o = input.readObject();
        bais.close();
        input.close();
        return o;
    }
}

2.8 把序列化加入到Demo中

首先抽象一个Hessian工具类:

package org.example.rpc09;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;

import com.caucho.hessian.io.Hessian2Input;
import com.caucho.hessian.io.Hessian2Output;

/**
 * Hessian工具类
 **/
public class HessianUtil {
    /**
     * 序列化
     */
    public static byte[] serialize(Object o) throws Exception {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        Hessian2Output output = new Hessian2Output(baos);

        output.writeObject(o);
        output.flush();

        byte[] bytes = baos.toByteArray();
        baos.close();
        output.close();
        return bytes;
    }

    /**
     * 反序列化
     */
    public static Object deserialize(byte[] bytes) throws Exception {
        ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
        Hessian2Input input = new Hessian2Input(bais);

        Object o = input.readObject();
        bais.close();
        input.close();
        return o;
    }
    
}

封装一个RPC请求:

package org.example.rpc09;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.ToString;

import java.io.Serializable;

/**
 * 传输对象
 **/
@Data
@AllArgsConstructor
@ToString
public class RpcRequest implements Serializable {

    private String className;
    private String methodName;
    private Class[] parameterTypes;
    private Object[] args;
}

服务端:

package org.example.rpc09;

import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Method;
import java.net.ServerSocket;
import java.net.Socket;

import org.example.rpc01.UserServiceImpl;

/**
 * 服务端
 **/
public class Server {
    private static final int PORT = 8888;
    private static final boolean RUNNING = Boolean.TRUE;

    public static void main(String[] args) throws Exception {
        ServerSocket serverSocket = new ServerSocket(PORT);
        while (RUNNING) {
            Socket socket = serverSocket.accept();
            process(socket);
            socket.close();
        }
        serverSocket.close();
    }

    private static void process(Socket socket) throws Exception {
        InputStream in = socket.getInputStream();
        OutputStream out = socket.getOutputStream();

        //反序列化对象
        byte[] bytes = new byte[1024];
        in.read(bytes);
        RpcRequest rpcRequest = (RpcRequest) HessianUtil.deserialize(bytes);
        System.out.println("服务端RPC:" + rpcRequest.toString());

        String className = rpcRequest.getClassName();
        String methodName = rpcRequest.getMethodName();
        Class[] parameterTypes = rpcRequest.getParameterTypes();
        Object[] args = rpcRequest.getArgs();

        //从服务注册表找到具体的类,在spring中可以通过spring容器找到对应的实现
        Class classzz = UserServiceImpl.class;
        Method method = classzz.getMethod(methodName, parameterTypes);

        //序列化
        out.write(HessianUtil.serialize(method.invoke(classzz.newInstance(), args)));
        out.flush();
    }
}

Stub:

package org.example.rpc09;

import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.net.Socket;

import org.example.common.IUserService;

/**
 * 代理类
 **/
public class Stub {

    public static Object getStub(Class classz){
        InvocationHandler h = new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                Socket socket = new Socket("localhost", 8888);
                OutputStream outputStream = socket.getOutputStream();

                String classzName = classz.getName();
                String methodName = method.getName();
                RpcRequest rpcRequest = new RpcRequest(classzName, methodName, method.getParameterTypes(), args);

                //序列化
                outputStream.write(HessianUtil.serialize(rpcRequest));
                outputStream.flush();

                //反序列化
                byte[] bytes = new byte[1024];
                InputStream inputStream = socket.getInputStream();
                inputStream.read(bytes);
                Object o = HessianUtil.deserialize(bytes);
                System.out.println(o.toString());
                socket.close();
                return o;
            }
        };

        return Proxy.newProxyInstance(classz.getClassLoader(), new Class[]{IUserService.class}, h);
    }

}
package org.example.rpc09;

import java.io.IOException;

import org.example.common.IUserService;

/**
 * 客户端
 **/
public class Client {

    public static void main(String[] args) throws IOException {
        IUserService i = (IUserService) Stub.getStub(IUserService.class);
        System.out.println(i.getUser(1));
    }
}

2.9 网络协议选择

RPC网络协议也不是固定的,主要有TCP、UDP、HTTP协议。

  • 基于TCP协议

    客户端和服务端建立Socket连接。
    客户端通过Socket将需要调用的接口名称、方法名称及参数序列化后传递给服务端。
    服务端反序列化后再利用反射调用对应的方法,将结果返回给客户端。

  • 基于HTTP协议

    客户端向服务端发送请求,如GET、POST、PUT、DELETE等请求。
    服务端根据不同的请求参数和请求URL进行方法调用,返回JSON或者XML数据结果。

  • TCP和HTTP对比

    基于TCP协议实现的RPC调用,由于是底层协议栈,更佳灵活的对协议字段进行定制,可减少网络开销,提高性能,实现更大的吞吐量和并发数。但,底层复杂,实现代价高。
    基于HTTP协议实现的RPC调用,已封装实现序列化,但HTTP属于应用层协议,HTTP传输所占用的字节数比TCP更高,传输效率对比TCP较低。

2.10

一条小咸鱼