Fork me on GitHub

剥掉层层外衣后的RPC是什么样子的?

RPC,全称为Remote Procedure Call(远程过程调用)。通俗一点讲就是在本地调用远程服务器上的功能。实现远程调用至少需要满足以下几个条件:

1.网络通信

2.序列化与反序列化

3.反射

远程通信是远程调用的前题,只有经过序列化后的数据才能在网络上传输,传输到服务器端后需要反序列化成对象,然后通过反射机制调用服务器上客户端指定的服务,再将结果返回给客户端,用一张图表示:

 

服务器每收到一次客户端的请求,就启动一个线程处理,调用具体的服务对象相应的方法,并将返回结果返回给客户端。

基于此原理,下面写一个示例实现RPC功能:

项目结构图如下:分别有客户端、服务器、服务以及请求与响应。

 

 首先抽象出请求类与响应类:

 请求类:属性有请求的类名、方法名、输入参数类型、输入数据。

package cn.yang.common;

import java.io.Serializable;

//请求
public class Request implements Serializable {
    private String className;//类名
    private String methodName;//方法名

    private Class[] inType;//输入参数类型
    private Object[] inData;//输入数据

    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[] getInType() {
        return inType;
    }

    public void setInType(Class[] inType) {
        this.inType = inType;
    }

    public Object[] getInData() {
        return inData;
    }

    public void setInData(Object[] inData) {
        this.inData = inData;
    }
}

 

响应类:属性有返回对象

package cn.yang.common;

import java.io.Serializable;

//响应
public class Response implements Serializable {
    private Object obj;//返回对象

    public Object getObj() {
        return obj;
    }

    public void setObj(Object obj) {
        this.obj = obj;
    }
}

 

服务器类以及服务处理类:

服务器类负责接收客户端的请求,并将请求分配给服务处理类进行处理。

服务器类:

package cn.yang.server;

import cn.yang.server.handler.ServerHandler;

import java.io.BufferedReader;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;

//服务器类,负责接收任务,分配任务
public class Server {
    public static void main(String args[]){
        try {
            ServerSocket socket = new ServerSocket();
            socket.bind(new InetSocketAddress(1234));
            BufferedReader in;
            Socket accept;
            while (true){
                accept = socket.accept();
                System.out.println("accept a request from"+accept.getRemoteSocketAddress());
                new Thread(new ServerHandler(accept)).start();
            }
        }catch(IOException e){
            e.printStackTrace();
        }

    }
}

 

服务处理类:

package cn.yang.server.handler;

import cn.yang.common.Request;
import cn.yang.common.Response;

import java.io.*;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.Socket;

//服务处理类
public class ServerHandler implements Runnable {
    private Socket socket;
    private ObjectInputStream in;
    private ObjectOutputStream out;

    public ServerHandler(Socket socket){
        this.socket=socket;
    }

    @Override
    @SuppressWarnings("unchecked")
    public void run() {
        try {
            in = new ObjectInputStream(socket.getInputStream());
            Request request = (Request)in.readObject();
            
            //类名
            String className=request.getClassName();
            //方法名
            String methodNmae=request.getMethodName();
            //参数类型
            Class[] inType=request.getInType();
            //参数值
            Object[] inDate=request.getInData();
            
            //根据类名导入类的字节码
            Class cls=Class.forName(className);
            
            //根据方法名得到方法
            Method method = cls.getMethod(methodNmae, inType);
            
            //创建类对象
            Object obj = cls.newInstance();
            
            //执行方法得到结果
            Object result = method.invoke(obj, inDate);

            //得到socket输出流
            out=new ObjectOutputStream(socket.getOutputStream());

            //构造Response对象并输出
            Response response=new Response();
            response.setObj(result);
            out.writeObject(response);
        } catch (InstantiationException | InvocationTargetException
                | IllegalAccessException | NoSuchMethodException |
                ClassNotFoundException | IOException e) {
            e.printStackTrace();
        }finally {
            if(in!=null){
                try {
                    in.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }finally {
                    in=null;
                }
            }

            if(out!=null){
                try {
                    out.close();
                } catch (IOException e2) {
                    e2.printStackTrace();
                }finally {
                    out=null;
                }
            }

            if(socket!=null){
                try {
                    socket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }finally {
                    socket=null;
                }
            }
        }
    }
}

 

服务类:

package cn.yang.service;

import java.util.Arrays;
import java.util.List;

//服务类
public class Hello{
    //sayHello服务方法
    public String sayHello(String name){
        return "hello:"+name;
    }

    //list服务方法
    public List<String> list(){
        List<String> list= Arrays.asList("1","2","3");
        return list;
    }
}

 

最后是客户端类:

package cn.yang.client;

import cn.yang.common.Request;
import cn.yang.common.Response;

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

//客户端
public class client {
    public static void main(String args[]){
        Socket socket = new Socket();
        ObjectOutputStream objectOutputStream=null;
        ObjectInputStream objectInputStream=null;
        try {
            socket.connect(new InetSocketAddress(1234));

            //封装请求类
            Request request=new Request();
            request.setClassName("cn.yang.service.Hello");//全类名
            request.setMethodName("sayHello");//方法
            request.setInType(new Class[]{String.class});//参数类型
            request.setInData(new Object[]{"LiMing"});//参数值

            /*Request request=new Request();
            request.setClassName("cn.yang.service.Hello");
            request.setMethodName("list");
            request.setInType(new Class[]{});
            request.setInData(new Object[]{});*/

            //打开socket输出流
            objectOutputStream = new ObjectOutputStream(socket.getOutputStream());
            //写入Request对象
            objectOutputStream.writeObject(request);

            //打开socket输入流
            objectInputStream = new ObjectInputStream(socket.getInputStream());

            //读取Response对象
            Response response=(Response)objectInputStream.readObject();
            Object obj = response.getObj();
            System.out.println("result:"+obj);

        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }finally {
            if(objectOutputStream!=null){
                try {
                    objectOutputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

            if(objectInputStream!=null){
                try {
                    objectInputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

            if(socket!=null){
                try {
                    socket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

    }
}

 

运行Server,然后再运行Client,得到结果:

 

总结:RPC实际上是在网络通信基础之上,运用反射技术达到远程功能调用的目的。Request和Response类必须是可序列化类,以便在网络上传输,并且客户端和服务器都必须有这两个类。这个示例只是以最简单的方式说明RPC本质是什么。

现在有许多RPC框架,如Thrift,Hessian,JsonRPC,Dubbo,rPcx,gRPC等,他们就是在此基础之上进行再封装,优化,使用户更简便的使用,达到调用远程功能,就如同调用本地功能一样方便。

上面所说的网络通信、序列化与反序例化以及反射的实现各个框架都有所不同。

网络通信有的选择TCP,有的选择HTTP。TCP 是传输层协议,HTTP 是应用层协议,而传输层较应用层更加底层,在数据传输方面,越底层越快,因此,在一般情况下,TCP 一定比 HTTP 快。

就序列化而言,Java 提供了默认的序列化方式,但在高并发的情况下,这种方式将会带来一些性能上的瓶颈,于是市面上出现了一系列优秀的序列化框架,比如:Protobuf、Kryo、Hessian、Jackson 等,它们可以取代 Java 默认的序列化,从而提供更高效的性能。

反射技术有Java自带的反射技术,Objenesis以及CGLIB。

实际应用中,服务不会直接是一个具体的类,而是一个接口,也称为协议,即客户端和服务端达成的一种共识。客户端请求时,传输接口名字,服务端通过接口名字找到接口的实现类,然后创建对象,并执行方法返回结果或者客户端通过服务注册中心找到服务实现类名,传送给服务器,这涉及到服务注册与发现,不同的框架实现的方式也有所不同,甚至一些框架还有流量监控与控制,服务故障转换,负载均衡等。

这一篇文章只是执砖引玉,希望大家在了解RPC基础原理之后,走上更高的台阶!

参考:

轻量级分布式 RPC 框架

阿里Dubbo疯狂更新,关Spring Cloud什么事

posted @ 2018-04-22 15:23  单行线的旋律  阅读(576)  评论(0编辑  收藏  举报