剥掉层层外衣后的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 + ...
随机推荐
- Python中安装模块的方法
1.*nix系统上有一个地方专门有一个地方来放置安装的Python模块 比如在Mac上,这个目录的路径为: /usr/lib/python2.7 将要安装的文件拷贝到这里即可 2.下载模块包,解压后, ...
- 仿京东树形菜单插件hovertree
hovertree是一个仿京东的树形菜单jquery插件,暂时有银色和绿色两种. 官方网址:http://keleyi.com/jq/hovertree/欢迎下载使用 查看绿色效果:http://ke ...
- ReentrantLock 与 AQS 源码分析
ReentrantLock 与 AQS 源码分析 1. 基本结构 重入锁 ReetrantLock,JDK 1.5新增的类,作用与synchronized关键字相当,但比synchronized ...
- 1013团队Beta冲刺day2
项目进展 李明皇 今天解决的进度 优化了信息详情页的布局:日期显示,添加举报按钮等 优化了程序的数据传递逻辑 明天安排 程序运行逻辑的完善 林翔 今天解决的进度 实现微信端消息发布的插入数据库 明天安 ...
- 单向链表在O(1)时间内删除一个节点
说删链表节点,第一时间想到就是遍历整个链表,找到删除节点的前驱,改变节点指向,删除节点,但是,这样删除单链表的某一节点,时间复杂度就是O(n),不符合要求: 时间复杂度是O(n)的做法就不说了,看看O ...
- string类的简洁版实现
说是原创,差不多算是转载了,我也是看了好多大牛的写法,大牛的建议,自己加一总结,形成代码: 实现一个简洁版的string类,我觉得,下面的也够了:另外需要参见另外的写法: http://blog.cs ...
- 《高级软件测试》Linux平台Jira的安装与配置
现在大部分的程序开发都是在linux下进行的,jira更多的时候是安装在linux上,那么,如何在linux下安装配置jira呢?本文将以Ubuntu 17.10和jira7.5.2为例,对linux ...
- xShell终端下中文乱码问题
今天,可能是因为不小心中途打断了xShell更新,结果打开xShell发现里面的中文全成了乱码.于是去网上查了一下原因. 更新xshell(xshell5)以及其他终端中文乱码的原因无非有三种 (1 ...
- Syabse数据库无法启动的解决方案
在探讨本问题之前,首先要为大家解释一下Syabse数据库本身.Syabse数据库应用和本身的架构相对而言都相对比较复杂,多数技术人员及公司对Sybase数据库底层结构和运行机制也处于并非完全了解的阶段 ...
- nyoj 寻找最大数
寻找最大数 时间限制:1000 ms | 内存限制:65535 KB 难度:2 描述 请在整数 n 中删除m个数字, 使得余下的数字按原次序组成的新数最大, 比如当n=920813467185 ...