剥掉层层外衣后的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是什么样子的?的更多相关文章
- nginx获取经过层层代理后的客户端真实IP(使用正则匹配)
今天帮兄弟项目搞了一个获取客户端真实IP的问题,网上这种问题很多,但是对于我们的场景都不太合用,现把我的解决方案share给大家,如有问题,请及时指出. 场景: 在请求到达后端服务之前,会经过层层代理 ...
- 「坐上时光机,查找编译压缩后的文件最初的样子」gulp-sourcemaps 使用说明
一般我们调试的 js/css 文件都是编译压缩后的,一旦出错很难定位原始的位置,gulp-sourcemaps 的出现帮助我们解决了这个问题. 首先我们看下目录结构: css js a.js b.js ...
- PPTP协议握手流程分析
一 PPTP概述 PPTP(Point to Point Tunneling Protocol),即点对点隧道协议.该协议是在PPP协议的基础上开发的一种新的增强型安全协议,支持多协议虚拟专用网,可 ...
- C++系列总结——构造与析构
前言 在使用资源前,我们需要做一些准备工作保证资源能正常使用,在使用完资源后,我们需要做一些扫尾工作保证资源没有泄露,这就是构造与析构了,这和编程语言是无关的,而是使用资源的一种方式.C++只不过是把 ...
- PPTP协议握手流程分析--转载
一 PPTP概述 PPTP(Point to Point Tunneling Protocol),即点对点隧道协议.该协议是在PPP协议的基础上开发的一种新的增强型安全协议,支持多协议虚拟专用网 ...
- 大括号之谜:C++的列表初始化语法解析
有朋友在使用std::array时发现一个奇怪的问题:当元素类型是复合类型时,编译通不过. struct S { int x; int y; }; int main() { int a1[3]{1, ...
- CentOS7设置SVN自启动,提交报错,无权限.手动kill掉后重启,成功.
参考文档:http://tieba.baidu.com/p/5174054662 最近想尝试在CentOS7上搭建SVN服务.遇到的问题大致如题,我这边再详细描述一下. 虚拟机:VMware® Wor ...
- TCP、UDP数据包大小的限制(UDP数据包一次发送多大为好)——数据帧的物理特性决定的,每层都有一个自己的数据头,层层递减
1.概述 首先要看TCP/IP协议,涉及到四层:链路层,网络层,传输层,应用层. 其中以太网(Ethernet)的数据帧在链路层 IP包在网络层 TCP或UDP包在传输层 TCP或UDP中的数据(Da ...
- ZBUS = MQ + RPC
http://git.oschina.net/rushmore/zbus http://my.oschina.net/sbz/blog Readme.md 18.02 KB ZBUS = MQ + ...
随机推荐
- Access数据库自动生成设计文档
在做Access数据库设计时,常常直接在access文件中建表,建字段,然后写设计文档时,又得重新再写一遍字段和表间关系.其实access数据库自己就支持自动生成数据库文档. 操作方法如下: 数据库工 ...
- 如何查看与更改python的工作目录?
在编写<机器学习实战>第二章kNN代码时遇到问题,即在自己编写好模块后,使用ipython进行import时,出现以下错误: 可知若想找到该模块,需将工作目录改变到当前文件(模块py文件) ...
- Fluent Interface(流式接口)
我最初接触这个概念是读自<<模式-工程化实现及扩展>>,另外有Martin fowler大师 所写http://martinfowler.com/bliki/FluentInt ...
- JAVA_SE基础——66.StringBuffer类 ③
如果需要频繁修改字符串 的内容,建议使用字符串缓冲 类(StringBuffer). StringBuffer 其实就是一个存储字符 的容器. 容器的具备 的行为 常用方法 String 增加 ap ...
- webapi 使用Autofac 开发经历
2018/4/6 号 早上五点..被手机震动吵醒. 之后直接打开电脑,打算再加强下我自己的webapi这套东西. 虽然三年的工作经验接触了N多框架和各种风格的开发方式,但是让我自己来搞一套实在不会搞, ...
- 电梯模拟C++
1.问题描述与要求 模拟某校九层教学楼的电梯系统.该楼有一个自动电梯,能在每层停留,其中第一层是大楼的进出层,即是电梯的"本垒层",电梯"空闲"时,将来到该层候 ...
- guava-19.0和google-collections-1.0 的 ImmutableSet 类冲突
guava-19.0 google-collections-1.0 都有 ImmutableSet 类,包路径也一致,前者有 copyOf(Collection)? 一.应用报错: 二.解决办法 co ...
- 开源软件:NoSql数据库 - 图数据库 Cassandra
转载原文:http://www.cnblogs.com/loveis715/p/5299495.html Cassandra简介 在前面的一篇文章<图形数据库Neo4J简介>中,我们介绍了 ...
- python tornado TCPserver异步协程实例
项目所用知识点 tornado socket tcpserver 协程 异步 tornado tcpserver源码抛析 在tornado的tcpserver文件中,实现了TCPServer这个类,他 ...
- MySQL/上
MySQL操作/上 一.视图 视图表是一个虚拟表(非真实存在),其本质是[根据sql语句获取动态的数据集,并为其命名],用户使用表只需使用(名称)即可获取结果集,并可以将其当做表来使用. 1.创建视图 ...