实现一个简易RPC

RPC的主要目标

RPC即远程过程调用,主要是为了解决分布式系统服务之间的调用问题,在远程调用时就像调用本地服务一样,调用者不用感知远程调用的逻辑。接下来将会使用JDK动态代理以及Socket来实现一个简单的RPC服务。

RPC简易实现

使用动态代理技术将远程调用逻辑封装起来,返回一个代理对象,这样就使得调用者完全不用理会远程调用的逻辑,就像调用本地方法一样。具体看代码

先看客户端代码

/**
 * 该类主要用来封装请求参数
 */
public class RPCRequest implements Serializable {

    private static final long serialVersionUID = 6301788037460580636L;

    /**
     * 请求的服务类
     */
    private String serviceClass;

    /**
     * 请求的方法名
     */
    private String methodName;

    /**
     * 请求方法的参数类型
     */
    private Class<?>[] argTypes;

    /**
     * 请求参数
     */
    private Object[] args;

    public RPCRequest(String serviceName) {
        this.serviceClass = serviceName;
    }

    public String getServiceClass() {
        return serviceClass;
    }

    public void setServiceClass(String serviceClass) {
        this.serviceClass = serviceClass;
    }

    public String getMethodName() {
        return methodName;
    }

    public void setMethodName(String methodName) {
        this.methodName = methodName;
    }

    public void setArgs(Object[] args) {
        this.args = args;
    }

    public Object[] getArgs() {
        return args;
    }

    public Class<?>[] getArgTypes() {
        return argTypes;
    }

    public void setArgTypes(Class<?>[] argTypes) {
        this.argTypes = argTypes;
    }
}
public class RPCInvocation implements InvocationHandler {

    private static final String REMOTE_HOST = "127.0.0.1";

    private static final int PORT = 8081;

    private RPCRequest request;

    /** 请求接口的具体实现类完全限定名 **/
    public RPCInvocation(String serviceName) {
        this.request = new RPCRequest(serviceName);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        InetAddress address = InetAddress.getByName(REMOTE_HOST);
        try (Socket socket = new Socket(address, PORT);
             ObjectOutputStream os = new ObjectOutputStream(socket.getOutputStream());
             ObjectInputStream is = new ObjectInputStream(socket.getInputStream())) {
            // 发送请求参数(服务类, 方法名字, 方法参数)
            request.setArgs(args);
            request.setArgTypes(method.getParameterTypes());
            request.setMethodName(method.getName());
            os.writeObject(request);
            // 读取结果并返回
            return is.readObject();
        }
    }
}
public class RPCUtils {

    private RPCUtils() {

    }

    /**
     * @param clazz 请求的接口
     * @param implClassName 调用远程的具体实现类完全限定名
     * @return 返回指定接口的代理实现
     */
    @SuppressWarnings("unchecked")
    public static <T> T getRemoteInstance(Class<T> clazz, String implClassName) {
        return (T) Proxy.newProxyInstance(RPCUtils.class.getClassLoader(), new Class<?>[]			{clazz}, new RPCInvocation(implClassName));
    }
}

再看服务端代码

public class RPCServer {
    
    public static void main(String[] args) throws Exception{
        new RPCServer().run();
    }

    public void run() throws Exception {
        try (ServerSocket serverSocket = new ServerSocket(8081)) {
            System.out.println("RPC服务已启动, 监听8081端口");
            for (; ; ) {
                try (Socket socket = serverSocket.accept();
                     ObjectOutputStream os = new ObjectOutputStream(
                         socket.getOutputStream());
                     ObjectInputStream is = new ObjectInputStream(
                         socket.getInputStream())) {
                    System.out.println("开始处理来自主机-" + socket.getInetAddress().
                                       getHostName() + "的请求!");
                    Object temp = is.readObject();
                    RPCRequest request = (RPCRequest) temp;
                    // 请求类
                    Class<?> serviceClass = Class.forName(request.getServiceClass());
                    Object serviceObj = serviceClass.getConstructor().newInstance();
                    // 方法参数以及参数类型
                    Class<?>[] argTypes = request.getArgTypes();
                    Object[] args = request.getArgs();
                    // 请求方法
                    Method method = serviceClass.getMethod(
                        request.getMethodName(), argTypes);
                    Object result = method.invoke(serviceObj, args);
                    os.writeObject(result);
                    System.out.println("主机-" + socket.getInetAddress().
                                       getHostName() + "的请求处理完毕!");

                } catch (Exception e) {
                    throw new RuntimeException("远程过程调用失败", e);
                }
            }
        }
    }
}

客户端如何调用?

假设系统A,系统B都依赖了Caculator接口,其中B系统有一个具体实现类CaculatorImpl实现了此接口,现在A系统想要通过远程调用B系统中CaculatorImpl来使用计算器功能。

/** A、B系统共同依赖 **/
public interface Caculator {

    Integer add(Integer a, Integer b);

    int minus(int a, int b);
}

/** Caculator接口的一个具体实现,位于B系统 **/
public class CaculatorImpl implements Caculator {

    @Override
    public Integer add(Integer a, Integer b) {
        return a + b;
    }

    @Override
    public int minus(int a, int b) {
        return a - b;
    }
}

A系统客户端调用

public class Client {
    public static void main(String[] args) {
        Caculator caculator = RPCUtils.getRemoteInstance(Caculator.class, "com.wangtao.b.service.impl.CaculatorImpl");
        System.out.println(caculator.add(1, 2));
        System.out.println(caculator.minus(2, 1));
    }
}
posted on 2019-05-11 23:30  wastonl  阅读(144)  评论(0编辑  收藏  举报