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; } void setClassName(String className) { this.className = String getMethodName() { methodName; } setMethodName(String methodName) { this.methodName = Class[] getInType() { inType; } setInType(Class[] inType) { this.inType = Object[] getInData() { inData; } setInData(Object[] inData) { this.inData = inData; } }
响应类:属性有返回对象
响应 class Response private Object obj;返回对象 Object getObj() { obj; } setObj(Object obj) { this.obj = obj; } }
服务器类以及服务处理类:
服务器类负责接收客户端的请求,并将请求分配给服务处理类进行处理。
服务器类:
cn.yang.server; cn.yang.server.handler.ServerHandler; java.io.BufferedReader; java.io.IOException; java.net.InetSocketAddress; java.net.ServerSocket; java.net.Socket; 服务器类,负责接收任务,分配任务 class Server { static 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( ServerHandler(accept)).start(); } }catch(IOException e){ e.printStackTrace(); } } }
服务处理类:
cn.yang.server.handler; cn.yang.common.Request; cn.yang.common.Response; import java.io.*; java.lang.reflect.InvocationTargetException; java.lang.reflect.Method; 服务处理类 class ServerHandler Runnable { private Socket socket; ObjectInputStream in; ObjectOutputStream out; ServerHandler(Socket socket){ this.socket=socket; } @Override @SuppressWarnings("unchecked") run() { { in = 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= ObjectOutputStream(socket.getOutputStream()); 构造Response对象并输出 Response response= Response(); response.setObj(result); out.writeObject(response); } catch (InstantiationException | InvocationTargetException | IllegalAccessException | NoSuchMethodException | ClassNotFoundException | IOException e) { e.printStackTrace(); }finally { if(in!=null){ { in.close(); } (IOException e) { e.printStackTrace(); } { in=; } } if(out!= { out.close(); } (IOException e2) { e2.printStackTrace(); } { out=if(socket!= { socket.close(); } { socket=; } } } } }
服务类:
cn.yang.service; java.util.Arrays; java.util.List; 服务类 Hello{ sayHello服务方法 String sayHello(String name){ return "hello:"+name; } list服务方法 public List<String> list(){ List<String> list= Arrays.asList("1","2","3"); list; } }
最后是客户端类:
cn.yang.client; java.io.ObjectInputStream; java.io.ObjectOutputStream; 客户端 client { main(String args[]){ Socket socket = Socket(); ObjectOutputStream objectOutputStream=; ObjectInputStream objectInputStream=; { socket.connect()); 封装请求类 Request request= 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 = ObjectOutputStream(socket.getOutputStream()); 写入Request对象 objectOutputStream.writeObject(request); 打开socket输入流 objectInputStream = ObjectInputStream(socket.getInputStream()); 读取Response对象 Response response=(Response)objectInputStream.readObject(); Object obj = response.getObj(); System.out.println("result:"+obj); } catch (IOException | ClassNotFoundException e) { e.printStackTrace(); }if(objectOutputStream!= { objectOutputStream.close(); } (IOException e) { e.printStackTrace(); } } if(objectInputStream!= { objectInputStream.close(); } (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基础原理之后,走上更高的台阶!
参考:
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。