什么是RPC:  

  RPC(Remote Procedure Call,远程过程调用),一般用来实现部署在不同机器上的系统之间的方法调用,使得程序能够像访问本地系统资源一样,通过网络传输去访问远端系统资源;对于客户端来说, 传输层使用什么协议,序列化、反序列化都是透明的。

  在分布式架构中,难免会涉及多个独立的服务之间的通讯,比如一个简单的电商系统中,按照业务领域拆分成三个独立的应用,用户,订单,商品三者之间,当商品模块需要访问用户模块,获取该用户是否购买该商品等等的业务场景,就会设计独立服务之间的通讯。这个时候需要用到远程的服务调用。其中阿里的 dubbo框架就是目前最主流的RPC框架。

  要实现一个简单的RPC需要考虑到什么呢? 两个服务之间的通讯,基于 TCP/IP通讯的基础之上,进行远程服务调用需要进行序列化,通过动态代码获得服务的代理对象,还有传输的安全性,服务管理,然后进行传输,在被调用方需要进行反序列化解析,通过反射去调用方法,并返回请求结果。

服务端:

  服务端需要发布一个服务,那么需要一个服务接口及实现:

public interface IGpHello {
String sayHello(String msg);
}

  实现:

public class GpHelloImpl implements IGpHello{
@Override
public String sayHello(String msg) {
return " RPC ,Hello , "+msg;
}
}

  这里的服务接口需要在客户端进行调用,不过客户端不必进行实现,下面就不再重复编写了。那么对于服务端如何去监听请求?以及对于客户端请求对象的序列化就必须要有个实现序列化接口的传输对象,这个我们等到客户端发送请求的时候再进行编写。那么对于服务端监听请求可以通过Socket来实现,然后对于监听到的请求进行处理,我这里采用线程池去处理,既然通过线程池去处理,通过类的单一职责原则这里可以写一个类专门去处理请求,我这里这个类是ProcessorHandler:

public class RpcServer {
//创建一个线程池
private static final ExecutorService executorService=Executors.newCachedThreadPool(); public void publisher(final Object service,int port){
ServerSocket serverSocket=null;
try{
serverSocket=new ServerSocket(port); //启动一个服务监听 while(true){ //循环监听
Socket socket=serverSocket.accept(); //监听服务
//通过线程池去处理请求
executorService.execute(new ProcessorHandler(socket,service));
}
} catch (IOException e) {
e.printStackTrace();
}finally {
if(serverSocket!=null){
try {
serverSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
} }
}
}

  ProcessorHandler:

public class ProcessorHandler implements Runnable{

    private Socket socket;
private Object service; //服务端发布的服务 public ProcessorHandler(Socket socket, Object service) {
this.socket = socket;
this.service = service;
} @Override
public void run() {
//处理请求
ObjectInputStream inputStream=null;
try {
//获取客户端的输入流
inputStream=new ObjectInputStream(socket.getInputStream());
//反序列化远程传输的对象RpcRequest
RpcRequest request=(RpcRequest) inputStream.readObject();
Object result=invoke(request); //通过反射去调用本地的方法 //通过输出流讲结果输出给客户端
ObjectOutputStream outputStream=new ObjectOutputStream(socket.getOutputStream());
outputStream.writeObject(result);
outputStream.flush();
inputStream.close();
outputStream.close();
} catch (Exception e) {
e.printStackTrace();
}finally {
if(inputStream!=null){
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
private Object invoke(RpcRequest request) throws InvocationTargetException, IllegalAccessException, NoSuchMethodException {
//一下均为反射操作,目的是通过反射调用服务
Object[] args=request.getParameters();
Class<?>[] types=new Class[args.length];
for(int i=0;i<args.length;i++){
types[i]=args[i].getClass();
}
Method method=service.getClass().getMethod(request.getMethodName(),types);
return method.invoke(service,args);
}
}

  上面提及的 RpcRequest 就是我们的传输对象,这个对象稍后在客户端代码中会有体现。接着我们去把这个服务发布出来:

public static void main(String[] args) {
IGpHello iGpHello=new GpHelloImpl();
RpcServer rpcServer=new RpcServer();
rpcServer.publisher(iGpHello,8080);
}

客户端:

  客户端需要服务端的接口但是不需要实现,就把上面的接口复制到客户端的项目里就可以。下面我们需要去编写这个进行序列化传输的对象,必须实现序列化接口,而且这个 serialVersionUID 是唯一的,然后在服务端也有一个同样的类进行反序列化。 serialVersionUID要一致,不然会导致反序列化失败:

public class RpcRequest implements Serializable {

    private static final long serialVersionUID = -9100893052391757993L;
private String className;
private String methodName;
private Object[] parameters; 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 Object[] getParameters() {
return parameters;
} public void setParameters(Object[] parameters) {
this.parameters = parameters;
}
}

  接着我们需要获得远程服务的代理对象,然后通过这个代理对象去调用远程方法,所以这里需要有个获取代理对象的类:

  newProxyInstance方法用来返回一个代理对象,这个方法总共有3个参数,ClassLoader loader用来指明生成代理对象使用哪个类装载器,Class<?>[] interfaces用来指明生成哪个对象的代理对象,通过接口指定,InvocationHandler h用来指明产生的这个代理对象要做什么事情。我们这边需要实现自己的 InvocationHandler,需要便写一个类去实现InvocationHandler接口,我这里是RemoteInvocationHandler, 所以我们只需要调用newProxyInstance方法就可以得到某一个对象的代理对象了。

public class RpcClientProxy {

	/**
* 创建客户端的远程代理。通过远程代理进行访问
* static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
* @param interfaceCls
* @param host
* @param port
* @param <T>
* @return
*/
public <T> T clientProxy(final Class<T> interfaceCls, final String host, final int port) {
// 使用到了动态代理。
return (T) Proxy.newProxyInstance(interfaceCls.getClassLoader(), new Class[] { interfaceCls },
new RemoteInvocationHandler(host, port));
}
}

  RemoteInvocationHandler,调用远程的服务,获取代理对象,这里也是用Socket去实现,然后我这边是写了单独的一个类去连接远程的服务,进行流的传输这个类是TCPTransport:

public class RemoteInvocationHandler implements InvocationHandler {
private String host;
private int port; public RemoteInvocationHandler(String host, int port) {
this.host = host;
this.port = port;
} @Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//组装请求
RpcRequest request=new RpcRequest();
request.setClassName(method.getDeclaringClass().getName());
request.setMethodName(method.getName());
request.setParameters(args);
//通过tcp传输协议进行传输
TCPTransport tcpTransport=new TCPTransport(this.host,this.port);
//发送请求
return tcpTransport.send(request);
}
}

  TCPTransport:

public class TCPTransport {

    private String host;

    private int port;

    public TCPTransport(String host, int port) {
this.host = host;
this.port = port;
} //创建一个socket连接
private Socket newSocket(){
System.out.println("创建一个新的连接");
Socket socket;
try{
socket=new Socket(host,port);
return socket;
}catch (Exception e){
throw new RuntimeException("连接建立失败");
}
} public Object send(RpcRequest request){
Socket socket=null;
try {
socket = newSocket();
//获取输出流,将客户端需要调用的远程方法参数request发送给
ObjectOutputStream outputStream=new ObjectOutputStream
(socket.getOutputStream());
outputStream.writeObject(request);
outputStream.flush();
//获取输入流,得到服务端的返回结果
ObjectInputStream inputStream=new ObjectInputStream
(socket.getInputStream());
Object result=inputStream.readObject();
inputStream.close();
outputStream.close();
return result; }catch (Exception e ){
throw new RuntimeException("发起远程调用异常:",e);
}finally {
if(socket!=null){
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}

  就这样客户端的代码编写完毕,可以发起远程调用了:

public static void main(String[] args) {
RpcClientProxy rpcClientProxy=new RpcClientProxy(); IGpHello hello=rpcClientProxy.clientProxy
(IGpHello.class,"localhost",8080);
System.out.println(hello.sayHello("wuzz"));
}

  结果输出

创建一个新的连接
RPC ,Hello , wuzz

  RPC 远程服务调用到此结束,RPC远程服务调用是分布式项目的核心,接下去我们可以一步步的去展开分布式服务的学习拉。。。

分布式通讯架构RPC简单实现的更多相关文章

  1. alluxio源码解析-rpc调用概述-client和worker之间的block模块的通讯架构(netty版本)(3)

    (1.8版本)client和worker之间的block模块的通讯架构 block作为alluxio文件读取或者存储的最小基本单位,都是通过BlockOutStream和BlockInputtream ...

  2. Java[2] 分布式服务架构之java远程调用技术浅析(转http://www.uml.org.cn/zjjs/201208011.asp)

    转自:http://www.uml.org.cn/zjjs/201208011.asp 在分布式服务框架中,一个最基础的问题就是远程服务是怎么通讯的,在Java领域中有很多可实现远程通讯的技术,例如: ...

  3. 【转】RPC简单介绍

    RPC简单介绍 RPC 1. RPC是什么 RPC(Remote Procedure Call Protocol)——远程过程调用协议,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络 ...

  4. 分布式服务(RPC)+分布式消息队列(MQ)面试题精选

    ​ 分布式系统(distributed system)是建立在网络之上的软件系统.正是因为软件的特性,所以分布式系统具有高度的内聚性和透明性.因此,网络和分布式系统之间的区别更多的在于高层软件(特别是 ...

  5. .net 大型分布式电子商务架构说明

    .net大型分布式电子商务架构说明 背景 构建具备高可用,高扩展性,高性能,能承载高并发,大流量的分布式电子商务平台,支持用户,订单,采购,物流,配送,财务等多个项目的协作,便于后续运营报表,分析,便 ...

  6. 分布式网站架构后续:zookeeper技术浅析

    Zookeeper是hadoop的一个子项目,虽然源自hadoop,但是我发现zookeeper脱离hadoop的范畴开发分布式框架的运用 越来越多.今天我想谈谈zookeeper,本文不谈如何使用z ...

  7. net大型分布式电子商务架构

    net大型分布式电子商务架构 背景 构建具备高可用,高扩展性,高性能,能承载高并发,大流量的分布式电子商务平台,支持用户,订单,采购,物流,配送,财务等多个项目的协作,便于后续运营报表,分析,便于运维 ...

  8. Atitit.分布式远程调用  rpc  rmi  CORBA的关系

    Atitit.分布式远程调用  rpc  rmi  CORBA的关系 1. 远程调用(包括rpc,rmi,rest)1 2. 分布式调用大体上就分为两类,RPC式的,REST式的1 3. RPC(远程 ...

  9. zz《分布式服务架构 原理、设计与实战》综合

    这书以分布式微服务系统为主线,讲解了微服务架构设计.分布式一致性.性能优化等内容,并介绍了与微服务系统紧密联系的日志系统.全局调用链.容器化等. 还是一样,每一章摘抄一些自己觉得有用的内容,归纳整理, ...

随机推荐

  1. 【css】正确的让文本换行

    通常文本换行我们会使用word-break属性,有两个值供我们选择 word-break: break-all; word-break: break-world; 那么如何区分这两个值呢,我们看图说话 ...

  2. 如何快速上手一个新技术之vue学习经验

    碰到紧急项目挪别人的vue项目过来直接改,但是vue是18年初看过一遍,18年底再来用,早就忘到九霄云外了,结果丢脸的从打开vue开始学,虽然之前在有道云笔记做了很多记录,然后没有系统整理.所以借这次 ...

  3. day 10 - 2 函数练习

    1.写函数 接收 n 个数字  求这些参数数字的和 def sum_func(*args): total = 0 for i in args: total += i return total prin ...

  4. 定时刷新页面SetInterval 和setTimeout -时间间隔可以动态设定

    JS里设定延时: 使用SetInterval和设定延时函数setTimeout 很类似.setTimeout 运用在延迟一段时间,再进行某项操作. setTimeout("function& ...

  5. 关于Sublime Text 3的几个问题总结

    问题1:Sublime Text 3的注册码. 注册码网上搜有很多,所以可以去网上找.我现在给的可能以后就不能用了,而且我也是网上找的... 这个现在最新版本是可用的. —– BEGIN LICENS ...

  6. wx小程序-列表详细页点击跳转!

    1.因为template 只是单纯的占位符,所以事件要写在外层view上面 2.通过自定义属性来判断 跳转的是那篇文章  自定义属性    (data-自定义名称 ) 3. 执行 onpostTap方 ...

  7. Javascript - ExtJs - TreePanel组件

    TreePanel组件(Ext.tree.TreePanel)logogram:Ext.tree.Panel | xtype:treepanel 树与节点 树面板组件的根是源头,从根上拓展出其它的子节 ...

  8. shell编程 之 流程控制(条件语句和循环语句)

    1 if ...else... 基本格式: if condition then commend else commend fi 当然也可以写到一行,用[ ]表明边界,用:表示分行.比如: if [ $ ...

  9. 论文笔记系列-Multi-Fidelity Automatic Hyper-Parameter Tuning via Transfer Series Expansion

    论文: Multi-Fidelity Automatic Hyper-Parameter Tuning via Transfer Series Expansion 我们都知道实现AutoML的基本思路 ...

  10. OVS-----CentOS7上搭建基于Open vSwitch的VxLAN隧道实验

    一.关于VXLAN VXLAN 是 Virtual eXtensible LANs 的缩写,它是对 VLAN 的一个扩展,是非常新的一个 tunnel 技术,在Open vSwitch中应用也非常多. ...