rpc 原理和简单实现

来源:互联网 发布:ansible.cfg mac 编辑:程序博客网 时间:2024/06/05 02:19


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

结构:(图片来源:cnblogs.com/shown)

  1. 1. RpcServer  
  2.    负责导出(export)远程接口  
  3. 2. RpcClient  
  4.    负责导入(import)远程接口的代理实现  
  5. 3. RpcProxy  
  6.    远程接口的代理实现  
  7. 4. RpcInvoker  
  8.    客户方实现:负责编码调用信息和发送调用请求到服务方并等待调用结果返回  
  9.    服务方实现:负责调用服务端接口的具体实现并返回调用结果  
  10. 5. RpcProtocol  
  11.    负责协议编/解码  
  12. 6. RpcConnector  
  13.    负责维持客户方和服务方的连接通道和发送数据到服务方  
  14. 7. RpcAcceptor  
  15.    负责接收客户方请求并返回请求结果  
  16. 8. RpcProcessor  
  17.    负责在服务方控制调用过程,包括管理调用线程池、超时时间等  
  18. 9. RpcChannel  
  19.    数据传输通道  

对比本地JAVA 和 RPC代码:
1)本地模式
a)本地的接口:
package example1.day20170625;/** * Created by chenfenggang on 2017/7/17. */public interface Calculator {     int sum(int a,int b);}
b)本地的实现
package day20170625;/** * Created by chenfenggang on 2017/7/17. */public class MyCalculator implements  Calculator{    public int sum(int a, int b) {        return a+b;    }}
c)本地的测试
package day20170625;/** * Created by chenfenggang on 2017/7/17. */public class TestMyCalculator {    public static void main(String[] args) {        Calculator calculator = new MyCalculator();        System.out.println(calculator.sum(1,2));    }}
2) 加代理。不实现方法

package day20170625;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;/** * Created by chenfenggang on 2017/7/17. */public class MyInvocatationHandler<T> implements InvocationHandler {    private Class target;    MyInvocatationHandler(Class target) {        super();        this.target = target;    }    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {        System.out.println(target);        System.out.println(method.getName());        System.out.println(args.length);        for(Object object:args){            System.out.println(object);        }        return 11;    }    public <T>T getObject(){      return  (T)   Proxy.newProxyInstance(target.getClassLoader(),              new Class<?>[]{target}, new MyInvocatationHandler(target)) ;    }    public static void main(String[] args) {        Calculator   calculator  = (Calculator) new MyInvocatationHandler<Calculator>(Calculator.class).getObject();        System.out.println(calculator.sum(1,2));    }}
这样不管调用什么都会调invoke方法。如果将这部分变成一个基于io或者nio的东西,就能读取远程的对象。

3)客户端加入Socket
package day20170625;import java.io.ObjectInputStream;import java.io.ObjectOutputStream;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;import java.net.Socket;/** * Created by chenfenggang on 2017/7/17. */public class MyRpcClient<T> implements InvocationHandler {    private Class target;    MyRpcClient(Class target) {        super();        this.target = target;    }    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {        Socket socket = new Socket("localhost",8080);        ObjectOutputStream objectOutputStream =  new ObjectOutputStream(socket.getOutputStream());        objectOutputStream.writeObject(target.getName());        objectOutputStream.writeObject(method.getName());        objectOutputStream.writeObject(method.getParameterTypes());        objectOutputStream.writeObject(args);        return new ObjectInputStream(socket.getInputStream()).readObject();    }    public <T>T getObject(){      return  (T)   Proxy.newProxyInstance(target.getClassLoader(),              new Class<?>[]{target}, new MyRpcClient(target)) ;    }    public static void main(String[] args) {        Calculator   calculator  = (Calculator) new MyRpcClient<Calculator>(Calculator.class).getObject();        System.out.println(calculator.sum(123,10));        System.out.println(calculator.delete(123,10));    }}
4)服务端读取客户端请求,反射对象
package day20170625;import java.io.IOException;import java.io.ObjectInputStream;import java.io.ObjectOutputStream;import java.lang.reflect.InvocationTargetException;import java.lang.reflect.Method;import java.net.ServerSocket;import java.net.Socket;import java.util.HashMap;import java.util.Map;/** * Created by chenfenggang on 2017/7/17. */public class RpcService  {    //存储接口与实现类的关系,当然也可以通过客户端传送实现类的类名    static Map<String ,Class>  classImpMap = new HashMap();    public static  void put(String name,Class clazz){        classImpMap.put(name,clazz);    }    public static void run() throws IOException, ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException {        ServerSocket serverSocket = new ServerSocket(8080);        while (true){            Socket socket = serverSocket.accept();            ObjectInputStream inputStream =new ObjectInputStream( socket.getInputStream());            ObjectOutputStream objectOutputStream = new ObjectOutputStream(socket.getOutputStream());            String calssName = (String)inputStream.readObject();            String methodName = (String)inputStream.readObject();            Class<?>[] parameterTypes = (Class<?>[]) inputStream.readObject();            Object[] arguments = (Object[]) inputStream.readObject();            Class clazz = null;            if(calssName!=null) {                //需要获取接口的实现类              clazz = classImpMap.get(calssName);            }            if(clazz==null){                continue;            }            Method method = clazz.getMethod(methodName,parameterTypes);            Object result = method.invoke(clazz.newInstance(), arguments);            objectOutputStream.writeObject(result);        }    }    public static void main(String[] args) {        try {            RpcService.put(Calculator.class.getName(),MyCalculator.class);            RpcService.run();        } catch (Exception e) {            e.printStackTrace();        }    }}

5)总结:
RPC优点:
1)客户端只需要接口就像,不用管复杂的实现,
2) 实现代码在服务端运行,可以充分利用服务端代码优势。