RPC 实现的简单雏形

基本介绍

RPC,远程过程调用,顾名思义,在远方有个服务,你该怎么调用它?

这个问题的解决核心是先解决网络通讯问题,我得能访问到,其次解决调用问题。

网络通讯的方案

这么多年以来,网络通讯的实现方案有很多,简单列举如下:

  • 最古老也是最有效,并且永不过时的,TCP/UDP的二进制传输。事实上所有的通信方式归根结底都是 TCP/UDP
  • CORBA (Common Object Request Broker Architecture)。古老而复杂的,支持面向对象的通信协议
  • Web Service (SOA SOAP RDDI WSDL •••) 基于http + xml的标准化Web API
  • RestFul (Representational State Transfer) 回归简单化本源的Web API的事实标准 http + json
  • RMI (Remote Method Invocation )Java内部的分布式通信协议
  • JMS (Java Message Service) JavaEE中的消息框架标准,为很多MQ所支持
  • RPC (Remote Procedure Call) 远程过程调用,这只是一个统称,重点在于方法调用(不支持对象的概念).具体实现甚至可以用RMI RestFul 等去实现,但一般不用,因为RMI不能跨语言,而RestFul效率太低。 多用力服务器集群间的通信,因此常使用更加高效短小精悍的传输模式以提高效率。

目前来说,主流的还是RestFul 和 RPC,各有各的优缺点,比如能否跨越防火墙,性能高低啊,当然TCP虽然写着麻烦,但是优点多多

序列化框架

网络通讯势必避免不了序列化问题,那么常见的序列化框架如下:

  1. java.io.Serializable
  2. Hessian
  3. google protobuf
  4. facebook Thrift
  5. kyro
  6. fst
  7. json序列化框架
    1. Jackson
    2. google Gson
    3. Ali FastJson
  8. xmlrpc(xstream)

基于性能体积等考虑,比较推荐的是google protobuf和facebook Thrift

简单的案例演示

下面是一个最简单的RPC实现,里面的序列化方案使用最原始的JDK的序列化,实际使用过程中更换

//Server
public class Server {
    public static boolean running = true;

    public static void main(String[] args) throws Exception {
        ServerSocket ss = new ServerSocket(9000);
        while(running){
            Socket s = ss.accept();
            process(s);
            s.close();
        }
        ss.close();
    }

    private static void process(Socket s) throws Exception {
        InputStream in = s.getInputStream();
        OutputStream out = s.getOutputStream();
        ObjectInputStream ois = new ObjectInputStream(in);
        ObjectOutputStream oos = new ObjectOutputStream(out);

        //接收客户端发送过来的方法详情,通过反射调用相关方法,并返回客户端数据
        String className = ois.readUTF();
        String methodName = ois.readUTF();
        Class[] parameterTypes = (Class[]) ois.readObject();
        Object[] args = (Object[]) ois.readObject();

        //根据接口类名找到接口同目录下实现该接口的所有类,这里也可以通过Spring @Autowired 去注入一个具体类对象
        Class clazz = Class.forName(className);
        List<Class<?>> allClassByInterface = ClassUtils.getAllClassByInterface(clazz);

        Class clazz2 = allClassByInterface.get(0);
        Method method = clazz2.getMethod(methodName, parameterTypes);
        Object o = method.invoke(clazz2.newInstance(), args);

        oos.writeObject(o);
        oos.flush();
    }
}
//client
public class Client {
    public static void main(String[] args) throws IOException {
        System.out.println("我是demo06 的client");
        IUserService service = (IUserService)Stub.getStub(IUserService.class);
        System.out.println(service.findUserById(123));
    }
}
//stub
public class Stub {
    public static Object getStub(final Class clazz){
        /*
            proxy: 代理的对象
            method: 代理的对象正在执行的方法
            args: 方法的参数
         */
        InvocationHandler h = new InvocationHandler() {
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                //1.建立连接
                Socket socket = new Socket("localhost", 9000);

                //2.往Server端写数据
                ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());

                String clazzName = clazz.getName();
                String methodName = method.getName();

                //为了避免方法重载,把方法的参数类型传递给server
                Class<?>[] parameterTypes = method.getParameterTypes();
                oos.writeUTF(clazzName);
                oos.writeUTF(methodName);
                oos.writeObject(parameterTypes);
                oos.writeObject(args);
                oos.flush();

                //3.从Server端读数据
                ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());
                Object o = ois.readObject();

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

        /*
            Proxy.newProxyInstance :new出来一个代理类对象
            IUserService.class.getClassLoader() :产生代理类的class loader
            new Class[]{IUserService.class} : 这个代理类实现了哪些接口
            h;调用动态代理类方法的处理器,h 实例化的时候(上面代码),invoke方法在动态代理类的所有方法执行的时候都会执行
        */
        Object o = Proxy.newProxyInstance(clazz.getClassLoader(), new Class[]{clazz}, h);
        //通过反射可以看看这个代理类的相关信息
        System.out.println(o.getClass().getName());
        System.out.println(o.getClass().getInterfaces()[0]);
        return o;
    }
}
posted @ 2021-04-12 17:04  镇魂帆-张  阅读(58)  评论(0编辑  收藏  举报