通用技术 以最简方式描述 RPC

Hugo · 2022年08月13日 · 2860 次阅读

RPC 是什么

RPC( Remote Procedure Call),RPC 是一种技术思想,并不是某种技术架构。我的理解是,基于 RPC 思想产生了很多不同类型的框架,例如 Zookeeper,Dubbo,RocketMQ 应用内部的通信方式,都是用到了 RPC 的思想。RPC 意思是远程方法调用,即通过网络调用远程服务的过程

RPC 的基本组成部分

协议

  • 理论上,只要符合一定规范的网络传输协议,都可实现 RPC 方法调用的过程,比如传输层的 TCP/UDP,应用层的 HTTP/FTP/SMTP 等

序列化

  • 既然是通过网络传输,必定离不开序列化和反序列化的过程。在底层的传输,系统不认识什么对象,json 等玩意,无论你传什么,都要给老子字节序列
  • 序列化有很多对应的框架,比如 java 的 Serializable 接口,这个接口提供的序列化方法,产生的字节长度会有很大优化的空间

动态代理

  • 为什么要用动态代理?在远程调用方法前,需要做很多准备工作准备,我们在调用 RPC 时,希望跟普通接口调用方式一样,我不关心是怎么准备的,这个时候只要代理对象帮你把事情全部给干完就好了,对于使用者来说无感

为什么说的那么简单?

  • 我打一堆字,你愿意看吗?

实现 Demo 千千万,不如自己写一个感受一下

  • 准备工作(JAVA)
    • InvocationHandler 接口(动态代理)
    • Serializable 接口(序列化)
    • Socket 编程(数据传输)
    • 反射

先看图

POM 相关依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.rpc</groupId>
    <artifactId>rpc</artifactId>
    <version>1.0-SNAPSHOT</version>

    <dependencies>
        <dependency>
            <groupId>org.reflections</groupId>
            <artifactId>reflections</artifactId>
            <version>0.9.10</version>
        </dependency>
    </dependencies>
</project>

Request 对象

package com.client.user.pojo;
import java.io.Serializable;

// 序列化请求对象
public class RpcRequest implements Serializable {
    private static final long  serialVersionUID = 1L;

    // 包名
    private String className;

    // 方法名
    private String methodName;

    // 请求方法类型
    private Class<?>[] paramTypes;

    // 请求的参数值
    private Object[] params;

    public String getClassName() {
        return className;
    }
    public void setClassName(String className) {
        this.className = className;
    }
    public String getMethodName() {
        return methodName;
    }
    public void setMethodName(String methodName) {
        this.methodName = methodName;
    }
    public Class<?>[] getParamTypes() {
        return paramTypes;
    }
    public void setParamTypes(Class<?>[] paramTypes) {
        this.paramTypes = paramTypes;
    }
    public Object[] getParams() {
        return params;
    }
    public void setParams(Object[] params) {
        this.params = params;
    }

}

Client

  • 接口
package com.client.user;

public interface UserInfo {
    void getUser();
}
  • 实现类
package com.client.user;

public class UserInfoImpl implements UserInfo{


    public void getUser() {
        System.out.println("调用成功");
    }
}
  • 代理类
package com.client.util;

import com.client.user.UserInfo;
import com.client.user.pojo.RpcRequest;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class ApiProxy implements InvocationHandler{

    public <T> T getProxy(Class<T> clazz){
        // clazz 不是接口不能使用JDK动态代理
        return (T) Proxy.newProxyInstance(clazz.getClassLoader(), new Class<?>[]{ clazz }, this);
    }

    /**
     * proxy 代理类代理真是代理对象com.sun.proxy.%Proxy0
     * method 要调用的方法
     * args 代理对象要传递的参数
     * */
    public Object invoke(Object proxy, Method method, Object[] args)  {

        // 序列化class,method,类型,参数
        RpcRequest rpcRequest = new RpcRequest();
        rpcRequest.setClassName(method.getDeclaringClass().getName());
        rpcRequest.setMethodName(method.getName());
        rpcRequest.setParamTypes(method.getParameterTypes());
        rpcRequest.setParams(args);

        // 发送socket
        Client client = new Client();
        int port = 12345;
        String host = "127.0.0.1";
        client.sendSocket(rpcRequest, host, port);
        return null;
    }

    public static void main(String[] args) {
        ApiProxy apiProxy = new ApiProxy();
        UserInfo proxy = apiProxy.getProxy(UserInfo.class);
        proxy.getUser();
    }
}
  • 客户端
package com.client.util;

import com.client.user.pojo.RpcRequest;

import java.io.IOException;
import java.io.ObjectOutputStream;
import java.net.Socket;

public class Client {

    public void sendSocket(RpcRequest rpcRequest,String host, int port){

        Socket socket = null;
        try {
            socket = new Socket(host, port);
            ObjectOutputStream objectOutputStream = new ObjectOutputStream(socket.getOutputStream());
            objectOutputStream.writeObject(rpcRequest);

            objectOutputStream.flush();
//            objectOutputStream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

}

Server

package com.server;

import com.client.user.pojo.RpcRequest;
import org.reflections.Reflections;

import java.io.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.*;

public class Server {

    public Socket clientSocket;

    public void runServer() throws Exception {
        ServerSocket serverSocket = new ServerSocket(12345);
        RpcRequest  request;

        while (true){
            Socket accept = serverSocket.accept();
            ObjectInputStream objectInputStream = new ObjectInputStream(accept.getInputStream());
            ObjectOutputStream objectOutputStream = new ObjectOutputStream(accept.getOutputStream());
            Object param = objectInputStream.readObject();
            request = (RpcRequest)param;


            Class clazz = Class.forName(request.getClassName());
            Reflections reflections = new Reflections("com.client.user");

            Set<Class> implClassSet = reflections.getSubTypesOf(clazz);

            Class[] classeses = implClassSet.toArray(new Class[0]);
            String classImpl = classeses[0].getName();

            Class<?> aClass = Class.forName(classImpl);
            Constructor<?> constructor = aClass.getConstructor();
            Object o = constructor.newInstance();
            Method method = o.getClass().getMethod(request.getMethodName(),request.getParamTypes());
            method.invoke(o,request.getParams());

            accept.close();
            objectInputStream.close();
            objectOutputStream.close();
        }

    }

    public static void main(String[] args) throws Exception {
        Server server = new Server();
        server.runServer();
    }
}

结果

小结一下

  • RPC 是一种技术思想,不是框架
  • RPC 是可以实现远程方法调用,就如同调用本地方法一样
  • RPC 设计的每一个环节,每一步都有说法,并不是一篇文章可以承载的。实现千千万,合适最重要
  • 了解 RPC 的基本过程,对于一些测试方法的设计或者快速对一个新的应用进行认识是不可或缺的基本要素
暂无回复。
需要 登录 后方可回复, 如果你还没有账号请点击这里 注册