【Java】RPC与RMI框架

  • 概念
    • RMI概念
    • RPC概念
  • RMI框架
  • 接口方法及接口实现对象的注册及其方法的调用
  • RPCFactory
  • RPCDefinition
  • 注解Scanning
  • 服务器端
  • 客户端
  • 总结

概念

RMI概念

RMI(Remote Method Invocation)远程方法调用是一种计算机之间利用远程对象互相调用实现双方通讯的一种通讯机制。使用这种机制,某一台计算机上的对象可以调用另外 一台计算机上的对象来获取远程数据。RMI是Enterprise JavaBeans的支柱,是建立分布式Java应用程序的方便途径。在过去,TCP/IP套接字通讯是远程通讯的主要手段,但此开发方式没有使用面向对 象的方式实现开发,在开发一个如此的通讯机制时往往令程序员感觉到乏味,对此RPC(Remote Procedure Call)应运而生,它使程序员更容易地调用远程程序,但在面对复杂的信息传讯时,RPC依然未能很好的支持,而且RPC未能做到面向对象调用的开发模 式。针对RPC服务遗留的问题,RMI出现在世人面前,它被设计成一种面向对象的通讯方式,允许程序员使用远程对象来实现通信,并且支持多线程的服务,这 是一次远程通讯的革命,为远程通信开辟新的里程碑。

RPC概念

RPC(Remote Procedure Call)—远程过程调用,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底>层网络技术的协议。RPC协议假定某些传输协议的存在,如TCP或UDP,为通信程序之间携带信息数据。在OSI网络>通信模型中,RPC跨越了传输层和应用层。RPC使得开发包括网络分布式多程序在内的应用程序更加容易。
RPC采用客户机/服务器模式。请求程序就是一个客户机,而服务提供程序就是一个服务器。首先,客户机调用进程发>送一个有进程参数的调用信息到服务进程,然后等待应答信息。在服务器端,进程保持睡眠状态直到调用信息到达为>止。当一个调用信息到达,服务器获得进程参数,计算结果,发送答复信息,然后等待下一个调用信息,最后,客户>端调用进程接收答复信息,获得进程结果,然后调用执行继续进行。
有多种 RPC模式和执行。最初由 Sun 公司提出。IETF ONC 宪章重新修订了 Sun 版本,使得 ONC RPC 协议成为 >IETF 标准协议。现在使用最普遍的模式和执行是开放式软件基础的分布式计算环境(DCE)。

RMI框架

网络上很多RMI框架的实现都不同,而博主这个使用的是大家较少用的使用json技术来实现的RMI框架;在服务器端保存接口和接口实现类,是通过注解的方式将实现类的对象和方法注册在容器中,当客户端远程调用方法时,将执行的结果返回给客户端。在客户端只需要保存接口方法而不需要接口的实现类,通过代理机制得到接口的实现类对象,在方法拦截中实现远程服务器的连接、参数的传递及其结果的接收,当接收完成后断开与服务器的连接。

接口方法及接口实现对象的注册及其方法的调用

服务器端接口实现类和方法的注册是RMI框架形成的前提条件,注册方式可以采用注解或者文件配置,而本框架当中博主采用的是注解扫描的方式,当服务器端启动时,先采用包扫描的方式对带有注解的接口实现类进行扫描,注解是该类所实现的接口类型的数组,然后将数组中的每个接口方法进行扫描,找到每个接口方法的实现方法,形成一一映射的关系,将每个映射关系形成一个RPCDefinition,以这个形成的类为value,以该接口方法的完整名的hashCode为key保存在一个Map当中。
本框架当中博主在客户端采用的是直接将接口方法的hashCode发送给服务器端,然后服务器端通过在Map中查找对应的的RPCDefintion,而RPCDefintion由class、object和method组成,直接可以进行方法的调用,为远程调用大大节省了时间,也就是刚开始进行包扫描进行注册时用的时间长一点

RPCFactory

package com.wh.server.core;import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;import com.mec.util.PackageScanner;
import com.wh.annotation.Scanning;/*** RPC加工类,用于生成以接口方法的hashCode为键,RPCDefinition为值的Map* ,并从中获得指定的RPCDefinition* @author 闹闹小丫头**/
public class RPCFactory {// 以接口方法的hashCode为键,RPCDefinition为值的静态Mappublic static final Map<String, RPCDefinition> rpcMap;static{rpcMap = new HashMap<>();}/*** 无参构造*/public RPCFactory() {}/*** 扫描指定包下的所有类* @param packagePath*/public void scanerBeanByPackage(String packagePath) {PackageScanner scanner = new PackageScanner() {// 需要覆盖的方法dealClass,对类进行判断处理@Overridepublic void dealClass(Class<?> klass) {if(klass.isPrimitive()|| klass.isInterface()|| klass.isAnnotation()|| klass.isEnum()|| klass.isArray()|| !klass.isAnnotationPresent(Scanning.class)) {return;}Scanning scann = klass.getAnnotation(Scanning.class);Class<?>[] interfaces = scann.klass();for(Class<?> aInterface : interfaces) {try {register(klass, aInterface);} catch (Exception e) {e.printStackTrace();}}}};scanner.scannerPackage(packagePath);}/*** 根据指定类,指定接口和指定对象进行注册,将正确的放入Map中* @param klass 指定类 * @param interfaces 指定接口* @param object 指定对象* @return 返回这个类实例* @throws Exception*/public RPCFactory register(Class<?> klass, Class<?> interfaces, Object object) throws Exception{if(!interfaces.isInterface()) {System.out.println("[" + interfaces.getName() + "]不是接口!");}if(!interfaces.isAssignableFrom(klass)) {System.out.println("类[" + klass.getName() + "]不是接口[" + interfaces.getName() + "]的实现类!");}Object obj = object == null ? klass.newInstance() : object;Method[] interfacesMethods = interfaces.getDeclaredMethods();for(Method interfaceMethod : interfacesMethods) {String methodName = interfaceMethod.getName();Class<?>[] parasType = interfaceMethod.getParameterTypes();Method method = klass.getMethod(methodName, parasType);String rpcName = String.valueOf(interfaceMethod.toString().hashCode());RPCDefinition rpcData = new RPCDefinition();rpcData.setKlass(klass);rpcData.setMethod(method);rpcData.setObject(obj);rpcMap.put(rpcName, rpcData);}return this;} /*** 根据指定类和接口将其进行注册* @param klass 指定类* @param interfaces 指定接口* @return 返回这个类的实例* @throws Exception*/public RPCFactory register(Class<?> klass, Class<?> interfaces) throws Exception {return register(klass, interfaces, null);}/*** 根据指定接口和指定对象进行注册* @param interfaces 指定接口* @param object 指定对象* @return 返回这个类的实例* @throws Exception*/public RPCFactory register(Class<?> interfaces, Object object) throws Exception {return register(object.getClass(), interfaces, object);}/*** 从Map中获得以指定id为键的RPCDefinition* @param id 指定id* @return*/RPCDefinition getRPCDefinition(String id) {return rpcMap.get(id);}}

RPCDefinition

package com.wh.server.core;import java.lang.reflect.Method;/*** RPC定义基本结构类,用于RPCFactory的组装* 包含类类型,类的对象以及方法* @author 闹闹小丫头**/
public class RPCDefinition {private Class<?> klass;private Object object;private Method method;public RPCDefinition() {}Class<?> getKlass() {return klass;}void setKlass(Class<?> klass) {this.klass = klass;}Object getObject() {return object;}void setObject(Object object) {this.object = object;}Method getMethod() {return method;}void setMethod(Method method) {this.method = method;}@Overridepublic String toString() {return "类:" + klass + "\n对象:" + object + "\n方法:" + method + "";}
}

注解Scanning

package com.wh.annotation;import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.TYPE;/*** 注解,进行扫描时使用* @author 闹闹小丫头**/
@Retention(RetentionPolicy.RUNTIME)
@Target(TYPE)
public @interface Scanning {// 接口Class<?>[] klass();
}

服务器端

package com.wh.server.core;import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.net.Socket;
import java.util.Map;import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;/*** RPC执行类,继承了Runnable* @author 闹闹小丫头**/
public class RPCInvoker implements Runnable{private static final Type type = new TypeToken<Map<String, String>>() {}.getType();private Socket socket;private DataInputStream dis;private DataOutputStream dos;private Gson gson;/*** 双参构造* @param socket 指定socket* @param gson 指定gson*/public RPCInvoker(Socket socket, Gson gson) {this.socket = socket;this.gson = gson;try {this.dis = new DataInputStream(socket.getInputStream());this.dos = new DataOutputStream(socket.getOutputStream());} catch (IOException e) {e.printStackTrace();}}public void setSocket(Socket socket) {this.socket = socket;}public void setGson(Gson gson) {this.gson = gson;}/*** 将指定参数字符串根据指定参数类型数组转化为指定参数对象* @param paraString 指定参数字符串* @param paraTypes 指定参数类型数组* @return*/private Object[] getParas(String paraString, Class<?>[] paraTypes) {// 将指定参数字符串转化为MapMap<String, String> paraStringMap = gson.fromJson(paraString, type);int paraCount = paraStringMap.size();if(paraCount <= 0) {return new Object[] {};}Object[] paras = new Object[paraCount];// 遍历整个Map,将Map中的参数一个一个解析出来for(int index = 0; index < paraStringMap.size(); index++) {String key = "arg" + index;String value = paraStringMap.get(key);paras[index] = gson.fromJson(value, paraTypes[index]);}return paras;}@Overridepublic void run() {try {// 获得方法的hashCode值String methodId = dis.readUTF();// 获得参数字符串String paraString =dis.readUTF();// 从RPC工厂中获得指定方法的RPCDefinitionRPCDefinition rpcDefinition = new RPCFactory().getRPCDefinition(methodId);if(rpcDefinition == null) {System.out.println(methodId + "未找到匹配的类!");}Object object = rpcDefinition.getObject();Method method = rpcDefinition.getMethod();Class<?>[] paraTypes = method.getParameterTypes();// 通过获得的参数字符串得到指定的参数类型Object[] paras = getParas(paraString, paraTypes);// 执行该方法并获得返回值Object result = method.invoke(object, paras);// 将返回值通过输出通信信道发送给对端服务器dos.writeUTF(gson.toJson(result));} catch (IOException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {e.printStackTrace();} finally {try {// 无论如何最后关闭socketsocket.close();} catch (IOException e) {e.printStackTrace();}}}
}
package com.wh.server.core;import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.channels.ReadableByteChannel;
import java.util.concurrent.ThreadPoolExecutor;import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.mec.util.PropertiesParser;
import com.wh.threadpool.MThreadPool;/*** RPC服务器类,继承了Runnable* @author 闹闹小丫头**/
public class RPCServer implements Runnable{// 服务器默认的端口private static final int DEFAULT_SERER_PORT = 51778;// gsonprivate static final Gson gson = new GsonBuilder().create();// RPC服务器private ServerSocket serverSocket;// RPC服务器端口private int port;private volatile boolean goon;// 线程池类private MThreadPool mThreadPool;private ThreadPoolExecutor threadPool;public RPCServer() {this.port = DEFAULT_SERER_PORT;readConfig("/RPCUserConfig.properties");}public void setPort(int port) {this.port = port;}/*** 读取指定路径下的配置文件* @param configPath  配置文件的指定路线*/public void readConfig(String configPath)  {try {PropertiesParser.loadProperties(configPath);String portStr = PropertiesParser.value("RPCPort");if(portStr != null && !portStr.equals("")) {int port = Integer.valueOf(portStr);if (port > 0 && port < 65536) {this.port = port;}}} catch (Exception e) {// TODO 自动生成的 catch 块e.printStackTrace();}}/*** 启动RPC服务器*/public void startup() {try {if(serverSocket != null) {return;}serverSocket = new ServerSocket(port);goon = true;mThreadPool = new MThreadPool();threadPool = mThreadPool.newInstance(false);new Thread(this, "RPC_SERVER").start();} catch (IOException e) {e.printStackTrace();}}/*** 强制关闭RPC服务器*/public void shutdown() {if(goon == false) {return;}goon =false;if(serverSocket != null) {try {if(!serverSocket.isClosed()) {serverSocket.close();threadPool.shutdown();}} catch (IOException e) {e.printStackTrace();} finally {serverSocket = null;}}}/*** 判断服务器是否已启动* @return*/public boolean isStartup() {return goon;}@Overridepublic void run() {while(goon) {try {Socket client = serverSocket.accept();// 将侦听到的客户端放入线程池中threadPool.execute(new RPCInvoker(client, gson));} catch (IOException e) {if(goon == true) {goon = false;}}}}}
package com.mec.util;import java.util.HashMap;
import java.util.Map;import com.google.gson.Gson;
import com.google.gson.GsonBuilder;public class ArgumentMaker {private Map<String, String> paraMap;private static final Gson gson = new GsonBuilder().create();private int paraIndex;public ArgumentMaker() {this.paraIndex = 0;paraMap = new HashMap<>();}public ArgumentMaker addArg(String paraName, Object paraValue) {paraMap.put(paraName, gson.toJson(paraValue));return this;}public ArgumentMaker addArg(Object paraValue) {paraMap.put("arg" + paraIndex++, gson.toJson(paraValue));return this;}@Overridepublic String toString() {return gson.toJson(paraMap);}}

客户端

package com.wh.client.core;/*** 客户端代理类* @author 闹闹小丫头**/
public class ClientProxy {// 短连接客户端private RPCClient client;/*** 无参构造*/public ClientProxy() {client = new RPCClient();}public void setRPCClient(RPCClient rpcClient) {this.client = rpcClient;}public ClientProxy(String serverIp, int serverPort) {client = new RPCClient(serverIp, serverPort);}public ClientProxy(RPCClient client) { this.client = client;}/*** 获得指定接口的代理* @param klass 指定接口* @return 返回指定接口的代理*/public <T> T jdkProxy(Class<?> klass) {return client.getProxy(klass);}}
package com.wh.client.core;import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.net.Socket;
import java.net.UnknownHostException;import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.mec.util.ArgumentMaker;/*** 短连接客户端* @author 闹闹小丫头**/
public class RPCClient {public static final String DEFAULT_SERVER_IP = "192.168.1.29";public static final int DEFAULT_SERVER_PORT = 51778;private static final Gson gson = new GsonBuilder().create();private String serverIp;private int serverPort;private DataInputStream dis;private DataOutputStream dos;/*** 无参构造,需要连接的注册中心服务器使用默认ip和port*/public RPCClient() {this.serverIp = DEFAULT_SERVER_IP;this.serverPort = DEFAULT_SERVER_PORT;}/*** 双参构造,需要连接的注册中心服务器使用指定IP和port* @param serverIp 指定IP* @param serverPort 指定port*/public RPCClient(String serverIp, int serverPort) {this.serverIp = serverIp;this.serverPort = serverPort;}public void setServerIp(String serverIp) {this.serverIp = serverIp;}public void setServerPort(int serverPort) {this.serverPort = serverPort;}/*** 将指定数组参数转化为gson字符串进行返回* @param args 指定数组参数* @return 返回gson字符串*/private String parasToGson(Object args[]) {ArgumentMaker am = new ArgumentMaker();for(Object arg : args) {am.addArg(arg);}return am.toString();}/*** 远程短连接调用方法* @param interfaces 接口类型* @param method 方法名称* @param args 数组参数* @return 返回执行结果,返回值类型为方法返回值类型*/private Object invoker(Class<?> interfaces, Method method, Object args[]) {Object result = null;try {Socket socket = new Socket(serverIp, serverPort);dis = new DataInputStream(socket.getInputStream());dos = new DataOutputStream(socket.getOutputStream());dos.writeUTF(String.valueOf(method.toString().hashCode()));dos.writeUTF(parasToGson(args));String resString =  dis.readUTF();result = gson.fromJson(resString, method.getReturnType());socket.close();} catch (UnknownHostException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}return result;}/*** 获得指定接口的代理* @param interfaces 指定接口* @return 返回指定接口的代理*/@SuppressWarnings("unchecked")public <T> T getProxy(Class<?> interfaces) {return (T)Proxy.newProxyInstance(interfaces.getClassLoader(), new Class<?>[] {interfaces}, new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {// 进行方法拦截return invoker(interfaces, method, args);}});}}

总结

整套RPC与RMI框架用户在使用的过程当中只需要在要使用的接口实现类上加上注解,就可以远程调用方法,无需注意內部的逻辑问题,就可以直接使用里面的服务,这就是面向对象的设计过程。短连接的优点在于使用时建立连接,不用时断开连接,不过多的占用服务器的通信信道,大大减少了服务器的服务时间;但他的弊端在于如果短时间内客户端需要大量的远程调用方法就需要不断地与服务器连接断开,这样重复的操作无疑浪费了大量的时间。

【Java】RPC与RMI框架相关推荐

  1. Java RMI 框架(远程方法调用)

    RMI(即Remote Method Invoke 远程方法调用).在Java中,只要一个类extends了java.rmi.Remote接口,即可成为存在于服务器端的远程对象,供客户端访问并提供一定 ...

  2. JAVA RPC:从上手到爱不释手

    2019独角兽企业重金招聘Python工程师标准>>> 文首,思考一个问题:为什么需要 RPC 服务? 在传统的开发模式中,我们通常将系统的各个服务部署在单台机器,随着服务的扩展,这 ...

  3. 什么是rmi?为什么要使用rmi框架?

    大家好,我是IT修真院北京分院第31期的学员,一枚正直纯洁善良的JAVA程序员.今天给大家分享一下,修真院官网JAVA任务8的深度思考--什么是rmi?为什么要使用rmi框架? 1.背景介绍 什么是R ...

  4. Dubbo——初识RPC、Dubbo框架、使用直连方式实现Dubbo

    文章目录: 1.RPC & 软件架构 1.1 单一应用架构 1.2 分布式微服务架构 1.3 RPC 2.Dubbo概述 2.1基本架构 2.2 dubbo支持的协议 3.直连方式实现dubb ...

  5. java restful netty_Java RESTful 框架的性能比较

    来源:鸟窝 , colobu.com/2015/11/17/Jax-RS-Performance-Comparison/在微服务流行的今天,我们会从纵向和横向分解代码的逻辑,将一些独立的无状态的代码单 ...

  6. ava RMI 框架(远程方法调用)

    2019独角兽企业重金招聘Python工程师标准>>> RMI(即Remote Method Invoke 远程方法调用).在Java中,只要一个类extends了java.rmi. ...

  7. zookeeper 密码_「附源码」Dubbo+Zookeeper 的 RPC 远程调用框架

    技术博文,及时送达 作者 | 码农云帆哥 链接 | blog.csdn.net/sinat_27933301 上一篇:从零搭建创业公司后台技术栈 这是一个基于Dubbo+Zookeeper 的 RPC ...

  8. java分布式对象RMI应用测试用例

    [0]README 0.1)本文旨在对http://blog.csdn.net/PacosonSWJTU/article/details/50705192  中的代码进行实践(如何部署一个使用RMI框 ...

  9. RPC、RMI与MOM与组播 通信原理 .

    远程过程调用(RPC): 即对远程站点机上的过程进行调用.当站点机A上的一个进程调用另一个站点机上的过程时,A上的调用进程挂起,B上的被调用过程执行,并将结果返回给调用进程,使调用进程继续执行[B上的 ...

最新文章

  1. 用JavaScript实现在网页中显示时间表
  2. 暴涨!BTC忠实粉丝转向BCH为BCH网络添砖加瓦
  3. Linux下装完mysql后默认密码是多少
  4. android 标准字体,文字规范标准(IOS/Android)
  5. linux 更改父进程名称,[Linux进程]在父进程和子进程中分别修改变量
  6. python3-pandas DataFrame 索引、bool索引、pandas 字符串方法
  7. ArcGIS Engine代码共享-可以直接移植到你的工程中
  8. 【Windows】XShell中使用小键盘和ALT键(作Meta键),使BackSpace正常
  9. 网络摄像头 java_在Java中从网络摄像头捕获图像?
  10. VAE-变分自编码器-Auto-Encoding Variational Bayes-详细推导
  11. shell 监控判断进程是否存在,如果不存在就重新启动脚本。
  12. JAVA_JDK下载与安装教程(小白)
  13. 计算机论文指导记录卡,毕业论文指导记录表范文.doc
  14. 迅雷7界面引擎XLUI_Bolt_SDK的调用例子
  15. Windows Server 2012 R2 安装密钥
  16. 计算机网络第8版课后习题答案整理
  17. 《财富》2022全球500强榜单,沃尔玛九连冠,沙特阿美最赚钱,中国公司上榜最多 | 美通社头条...
  18. 解题:POI 2008 Plot purchase
  19. RK平台 MPP 与RGA ,解码h265绿屏,花屏解决方法
  20. 公网部署freeswitch1.8.6后,终端注册成功后,在fs_cli控制台执行originate user/1000 echo,呼通1000的SIP终端后,终端讲话没有收到fs的回声解决方法

热门文章

  1. 数据分析系列:绩效(效率)评价与python实现(层析分析、topsis、DEA)
  2. DNS和BIND总结
  3. WLAN 无线二层组网,旁挂AC
  4. 批量替换Word中的表格为图片并保存
  5. 上周热点回顾(8.20-8.26)
  6. tushare实战分析上证综指与美债收益率的关系
  7. 永中科技剥离台资百慕大是”执政败笔“
  8. 系统时间有误,造成网站无法登录故障处理
  9. 自己用html + js 一百行代码做一个朗读器
  10. c语言程序设计西安理工,c语言程序设计-西安理工大学三电教学中心!.doc