RPC技术


1、RPC技术介绍

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

​ RPC采用客户机/服务器模式。请求程序就是一个客户机,而服务提供程序就是一个服务器。首先,客户机调用进程发送一个有进程参数的调用信息到服务进程, 然后等待应答信息。在服务器端,进程保持睡眠状态直到调用信息到达为止。当一个调用信息到达,服务器获得进程参数,计算结果,发送答复信息,然后等待下一个调用信息,最后,客户端调用进程接收答复信息,获得进程结果,然后调用执行继续进行。

RPC流程图

​ RPC 调用分类RPC 调用的分类方式有很多种。

从通信协议层面可以分为:

​ 基于 HTTP 协议的 RPC;

​ 基于二进制协议的 RPC;

​ 基于 TCP 协议的 RPC。

从是否跨平台可分为:

​ 单语言 RPC,如 RMI, Remoting;

​ 跨平台 RPC,如 google protobuffer, restful json,http XML。

从调用过程来看, 可以分为同步通信RPC和异步通信RPC:

​ 同步 RPC:指的是客户端发起调用后,必须等待调用执行完成并返回结果;

​ 异步 RPC:指客户方调用后不关心执行结果返回,如果客户端需要结果,可用

​ 通过提供异步 callback 回调获取返回信息。大部分 RPC 框架都同时支持这两种方式的

调用。

2、实现

客户端的中间代理类:

/**
 * 客户端接口代理
 * 当客户端接口方法被调用的时候,把方法名,方法参数作为参数。
 * 传送给远程服务执行,然后获取返回值
 * 
 *  DynDemo       $Proxy0 重新方法,方法里调用dynDemo.invoke
 */
public class RpcProxy implements InvocationHandler, 
   Serializable{

    private String ip;
    private int port;
   // private Class<?> c;
    private String class4Name;

    private static final long serialVersionUID = 1L;

    public RpcProxy(String ip, int port, String c) &#123;
        this.ip = ip;
        this.port = port;
        this.class4Name = c;
    &#125;

    /**
     * 动态代理类,当调用接口方法的时候转为调用此方法
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args
            )throws Throwable &#123;

        // 用作返回值
        Object o = null;        
        // 通过socket调用远程服务
        Socket s = new Socket(ip, port);

        // 组装为一个保留了要调用的类,方法名及参数的对象,然后序列化之后传给远程
        RpcObject rpcObject = new RpcObject(class4Name, method.getName(), args);

        ObjectOutputStream os = null;
        ObjectInputStream is = null;

        try&#123;
            //在socket输入流建立对象流
            os = new ObjectOutputStream(s.getOutputStream());
            os.writeObject(rpcObject);// 把对象序列化,发送到服务端;
            os.flush();// 马上发送

            // 从远程得到返回结果
          //在socket输入流建立对象流
            is = new ObjectInputStream(s.getInputStream());
            o = is.readObject(); // 反序列化,获取结果

        &#125; catch (Exception e) &#123;
            e.printStackTrace();

        &#125; finally&#123;

            if(os!=null)&#123;
                os.close();
            &#125;

            if(is!=null)&#123;
                is.close();
            &#125;

        &#125;
        return o;
    &#125;
&#125;

服务端线程:

public class RpcThread extends Thread &#123;

    private Socket s;

    public RpcThread(Socket s) &#123;
        this.s = s;
    &#125;

    @Override
    public void run() &#123;

        ObjectInputStream is = null;
        ObjectOutputStream os = null;

        try &#123;

            //从TCP报文获取数据
            is = new ObjectInputStream(s.getInputStream());

            // 得到远程调用参数,包含了接口名,调用方法,方法参数
            RpcObject rpcObject = (RpcObject) is.readObject();

            System.out.println("Method:"+rpcObject.getMethodName());

            // 构建接口的实现类,然后通过反射调用方法
            Object o = getObject(rpcObject.getClass4Name());
            System.out.println("class:"+rpcObject.getClass4Name());


            Object reO = executeMethod(o, rpcObject.getMethodName(), rpcObject.getArgs());

            // 输出返回值
            os = new ObjectOutputStream(s.getOutputStream());
            os.writeObject(reO);
            os.flush();
        &#125; catch (IOException e) &#123;
            e.printStackTrace();
        &#125; catch (ClassNotFoundException e) &#123;
            e.printStackTrace();
        &#125; finally &#123;
            try &#123;
                if(is!=null)&#123;
                    is.close();
                &#125;

                if(os!=null)&#123;
                    os.close();
                &#125;                
            &#125; catch (IOException e) &#123;
                e.printStackTrace();
            &#125;
        &#125;
    &#125;

    /**
     * 通过反射技术执行方法,并返回返回值
     * @param o  // 方法作用在该对象上的
     * @param methodName
     * @param args
     * @return
     */
    private Object executeMethod(Object o, String methodName, Object[] args) &#123;

        Object objR = null;

        Class<?>[] cs = new Class[args.length];

        for (int i = 0; i < args.length; i++) &#123;
            Object arg = args[i];
            cs[i] = arg.getClass();
        &#125;
        try &#123;
            //获取类的方法;
            Method m = o.getClass().getMethod(methodName, cs);
             //动态调用该方法
            objR = m.invoke(o, args);
        &#125; catch (SecurityException e) &#123;
            e.printStackTrace();
        &#125; catch (NoSuchMethodException e) &#123;
            e.printStackTrace();
        &#125; catch (IllegalArgumentException e) &#123;
            e.printStackTrace();
        &#125; catch (IllegalAccessException e) &#123;
            e.printStackTrace();
        &#125; catch (InvocationTargetException e) &#123;
            e.printStackTrace();
        &#125;
        return objR;
    &#125;

    /**
     * 根据接口名得到实例
     * @param c
     * @return
     */
    private Object getObject(String c) &#123;
        Object o = null;
        try &#123;
            Class clz=Class.forName(c);

            o=clz.newInstance();
        &#125; catch (InstantiationException e) &#123;
            e.printStackTrace();
        &#125; catch (IllegalAccessException e) &#123;
            e.printStackTrace();
        &#125;catch(Exception e)
        &#123;
            e.printStackTrace();
        &#125;
        return o;
    &#125;
&#125;

3、总结

​ RPC技术就是远程调用函数。

​ 客户端首先把需要调用的函数信息发送给代理类,代理类序列化封装参数,通过Socket网络传输发送给服务端,服务端接收到信息后反序列化,调用函数。

​ 然后再将结果序列化,发送给客户端,客户端反序列化网络数据,返回方法调用结果。


​ 客户端怎么进行工作的?

  • 把客户端接口代理。
  • 当客户端接口方法被调用的时候,把方法名,方法参数作为参数。
  • 调用中间代理类的invoke函数,序列化。
  • 传送给远程服务执行,然后获取返回值。

​ 服务端怎么进行工作的?

  1. 接受到网络数据后,先反序列化得到远程调用参数(类的全限定名,调用方法,请求参数)。
  2. 通过类的全限定名构建接口的实现类,然后反射机制调用方法。
  3. 输出返回值。

4、负载均衡

​ 使用集群时,当一个服务有多个请求地址的时候,会返回多个地址。

​ 所以需要负载均衡来控制我们想要请求哪台机器来获得请求。


文章作者: kilig
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 kilig !
 上一篇
学习笔记 学习笔记
1、Session​ 讲一讲session。 ​ 因为HTTP是无状态协议,当需要前面请求的信息时,必须重传。这时就引入了session。 ​ session是服务端创建的一个容器。当请求过来时,如果请求没有session
2020-08-07
下一篇 
基于AOP实现分页 基于AOP实现分页
1、实现思路​ 之前做的分页只是session级别的,在同一个session中才可以取分页信息。 ​ 基于AOP技术实现的分页功能是application级别,所有访问web项目的请求,只要满足条件,就可以从se
  目录