1、示例

三个角色:RMIService、RMIServer、RMIClient。(RMIServer向RMIService注册Stub、RMIService在RMIClient lookup时向其提供Stub)

服务端编写完后,把服务端的功能接口类给客户端,客户端编写自己的代码即可。(客户端通过向RMI Service查找指定的服务得到Stub,不用手动生成任何Stub)

代码:

server:

接口定义及实现:

 /**
* <br>
* 在Java中,只要一个类extends了java.rmi.Remote接口,即可成为存在于服务器端的远程对象, 供客户端访问并提供一定的服务。JavaDoc描述:Remote 接口用于标识其方法可以从非本地虚拟机上 调用的接口。任何远程对象都必须直接或间接实现此接口。只有在“远程接口” (扩展 java.rmi.Remote 的接口)中指定的这些方法才可被远程调用。
*/
public interface Hello extends Remote {
/*
* extends了Remote接口的类或者其他接口中的方法若是声明抛出了RemoteException异常, 则表明该方法可被客户端远程访问调用。
*/
public String sayHello(String name) throws RemoteException;
} /**
* 远程对象必须实现java.rmi.server.UniCastRemoteObject类,这样才能保证客户端访问获得远程对象时, 该远程对象将会把自身的一个拷贝以Socket的形式传输给客户端,此时客户端所获得的这个拷贝称为“存根”, 而服务器端本身已存在的远程对象则称之为“骨架”。其实此时的存根是客户端的一个代理,用于与服务器端的通信,
* 而骨架也可认为是服务器端的一个代理,用于接收客户端的请求之后调用远程方法来响应客户端的请求。
*/
/* java.rmi.server.UnicastRemoteObject构造函数中将生成stub和skeleton */
public class HelloImpl extends UnicastRemoteObject implements Hello {
private static final long serialVersionUID = -271947229644133464L; // 这个实现必须有一个显式的构造函数,并且要抛出一个RemoteException异常
public HelloImpl() throws RemoteException {
super();
} @Override
public String sayHello(String name) throws RemoteException {
// TODO Auto-generated method stub
return "hello " + name;
}
}

服务注册及服务端:

 /**
* 注册远程对象,向客户端提供远程对象服务.远程对象是在远程服务上创建的,你无法确切地知道远程服务器上的对象的名称 但是,将远程对象注册到RMI Service之后,客户端就可以通过RMI Service请求 到该远程服务对象的stub了,利用stub代理就可以访问远程服务对象
*/
public class HelloServer { public static void main(String[] args) {
// TODO Auto-generated method stub
try {
Hello h = new HelloImpl(); /* 生成stub和skeleton,并返回stub代理引用 */
String serverIp = "localhost";
int listenPort = 12345;
String serverURL = serverIp + ":" + listenPort; /*
* 本地创建并启动RMI Service注册表,被创建的Registry将在指定的端口上侦听到来的请求
*/
Registry registry = LocateRegistry.createRegistry(listenPort);
// Registry registry = LocateRegistry.getRegistry("localhost", 12345);// 也可以获取远程RMI Service注册表,该RMI Service通过 rmiregistry -p 1099 启动 /* 将stub代理绑定到Registry服务的URL上 */
registry.bind("MyHello", h);// 通过RMI注册表绑定服务,不用指定完整RMI URL
// Naming.bind("rmi://" + serverURL + "/MyHello", h);// 或者通过命名服务绑定服务,由于命名服务不止为RMI提供查询服务,故需指定完整RMI URL,java.lang.String://host:port/name System.out.println("HelloServer启动成功");
} catch (Exception e) {
e.printStackTrace();
}
}
}

client:(把Hello接口给客户端并编写客户端代码)

  查找服务并调用:

 public class HelloClient {

     public static void main(String[] args) throws RemoteException, MalformedURLException, NotBoundException {
// String serverIp = "192.168.7.39";
String serverIp = "localhost";
int serverPort = 12345;
String serverURL = serverIp + ":" + serverPort;
Hello h = null; /* 从RMI Registry中请求stub */
// h = (Hello) Naming.lookup("rmi://" + serverURL + "/MyHello"); Registry registry = LocateRegistry.getRegistry(serverIp, serverPort);
h = (Hello) registry.lookup("MyHello"); /* 通过stub调用远程接口实现 */
System.out.println(h.sayHello("hello"));
}
}

RMI可以实现远程通讯,缺点之一:客户端只能是Java的,不能跨语言。

2、原理

本质是利用客户端的Stub(静态代理)和服务端的Skeleton(骨架)来为上层屏蔽底层通信。

RMI远程调用步骤:

1,客户对象调用客户端辅助对象上的方法

2,客户端辅助对象打包调用信息(变量,方法名),通过网络发送给服务端辅助对象

3,服务端辅助对象将客户端辅助对象发送来的信息解包,找出真正被调用的方法以及该方法所在对象

4,调用真正服务对象上的真正方法,并将结果返回给服务端辅助对象

5,服务端辅助对象将结果打包,发送给客户端辅助对象

6,客户端辅助对象将返回值解包,返回给客户对象

7,客户对象获得返回值

对于客户对象来说,步骤2-6是完全透明的

A.

B.

Java RMI由3个部分构成:

  1. RMIService即JDK提供的一个可以独立运行的程序(bin目录下的rmiregistry)。
  2. RMIServer即我们自己编写的一个java项目,这个项目对外提供服务。
  3. RMIClient即我们自己编写的另外一个java项目,这个项目远程使用RMIServer提供的服务。

首先,RMIService必须先启动并开始监听对应的端口。
其次,RMIServer将自己提供的服务的实现类注册到RMIService上,并指定一个访问的路径(或者说名称)供RMIClient使用。
最后,RMIClient使用事先知道(或和RMIServer约定好)的路径(或名称)到RMIService上去寻找这个服务,并使用这个服务在本地的接口调用服务的具体方法。

RMIService只负责接受RMIServer注册Stub和RMIClient查询Stub,不参与RMIServer、RMIClient间的后续交互过程。

RMIService没和RMIServer一起

通常RMIService是在RMIServer里被创建的,此时执行顺序是RMIServer—RMIService—RMIClient;

但RMIService、RMIServer、RMIClient也可以部署到3个不同的JVM中,即此时RMI Service不在RMI Server里被创建,这时执行顺序是RMIService---RMIServer—RMIClient。这种情况下在执行RMIService前,需要通过 rmic 类名 命令产生stub类并连同功能接口类放到RMIService下,然后通过 rmiregistry -p 端口 命令或代码 LocateRegistry.createRegistry(listenPort) 启动RMIService(默认端口号为1099)。

实际应用中很少有单独提供一个RMIService服务器,开发的时候可以使用Registry类在RMIServer中启动RMIService。

RMI并发

在JDK1.5及以前版本中,RMI每接收一个远程方法调用就生成一个单独的线程来处理这个请求,请求处理完成后,这个线程就会释放;在JDK1.6之后,RMI使用线程池来处理新接收的远程方法调用请求-ThreadPoolExecutor,RMIService亦然。

在JDK1.6中,RMI提供了可配置的线程池参数属性(启动参数 java -jar -Dxxx=xx  xxx):

sun.rmi.transport.tcp.maxConnectionThread - 线程池中的最大线程数量,默认无限,但Linux单进程可打开最大文件数有限,此时可能出问题。

sun.rmi.transport.tcp.threadKeepAliveTime - 线程池中空闲的线程存活时间(ms),默认1分钟。

3、RMI相关资料

1、http://blog.csdn.net/a19881029/article/details/9465663——示例

2、http://blog.csdn.net/sinat_34596644/article/details/52599688——底层原理简述

3、http://blog.csdn.net/sureyonder/article/details/5653609——Java RMI线程模型及内部实现原理

4、http://blog.csdn.net/yinwenjie/article/details/49120813——详细介绍了RMI不同运行方式及底层原理

4、缺点与改进

RMI的缺点:

跨平台能力差,服务端和客户端只能是Java

客户端对服务端依赖严重,客户端和服务端分别有自动生成的Stub和Skeleton,若服务端接口变化则需要重新生成Stub和Skekleton

针对跨平台能力差的缺点,可以通过自动生成不同语言的Stub和Skeleton(如Google的 Protobuf 即如此)

针对客户端对服务端依赖严重的缺点,一种解决方法是:去掉Stub并让服务端与客户端通过JSON或XML等数据格式进行交互,实现解耦。这其实就是现在很流行的HTTP Restfull API

更多关于客户端服务端通信方法的演进可参阅:咖啡馆的故事:FTP, RMI , XML-RPC, SOAP, REST一网打尽

Java RMI 的使用及原理的更多相关文章

  1. Java rmi漏洞利用及原理记录

    CVE-2011-3556 该模块利用了RMI的默认配置.注册表和RMI激活服务,允许加载类来自任何远程(HTTP)URL.当它在RMI中调用一个方法时分布式垃圾收集器,可通过每个RMI使用endpo ...

  2. java RMI原理详解

    java本身提供了一种RPC框架——RMI(即Remote Method Invoke 远程方法调用),在编写一个接口需要作为远程调用时,都需要继承了Remote,Remote 接口用于标识其方法可以 ...

  3. JAVA RMI分布式原理和应用

    RMI(Remote Method Invocation)是JAVA早期版本(JDK 1.1)提供的分布式应用解决方案,它作为重要的API被广泛的应用在EJB中.随着互联网应用的发展,分布式处理任务也 ...

  4. Java RMI之HelloWorld篇

    Java RMI 指的是远程方法调用 (Remote Method Invocation).它是一种机制,能够让在某个 Java 虚拟机上的对象调用另一个 Java 虚拟机中的对象上的方法.可以用此方 ...

  5. Java RMI 介绍和例子以及Spring对RMI支持的实际应用实例

    RMI 相关知识 RMI全称是Remote Method Invocation-远程方法调用,Java RMI在JDK1.1中实现的,其威力就体现在它强大的开发分布式网络应用的能力上,是纯Java的网 ...

  6. Java学习笔记(十六)——Java RMI

    [前面的话] 最近过的好舒服,每天过的感觉很充实,一些生活和工作的技巧注意了就会发现,其实生活也是可以过的如此的有滋有味,满足现在的状况,并且感觉很幸福. 学习java RMI的原因是最近在使用dub ...

  7. JAVA RMI helloworld入门

    Java RMI 指的是远程方法调用 (Remote Method Invocation).它是一种机制,能够让在某个 Java 虚拟机上的对象调用另一个 Java 虚拟机中的对象上的方法.可以用此方 ...

  8. Java RMI 远程方法调用

    Java RMI 指的是远程方法调用 (Remote Method Invocation).它是一种机制,能够让在某个 Java 虚拟机上的对象调用另一个 Java 虚拟机中的对象上的方法.可以用此方 ...

  9. Java RMI(远程方法调用)开发

    参考 https://docs.oracle.com/javase/7/docs/platform/rmi/spec/rmi-arch2.html http://www.cnblogs.com/wxi ...

随机推荐

  1. Linux 密码过期(WARNING:Your password has expired )

    最近遇到两次这个问题,我们公司用的是开源的堡垒机Jumpserver但是最近有两个同学遇到了 WARNING:Your password has expired 第一次遇到这个问题也没有往深了去查,当 ...

  2. SaaS多租户模式数据存储方案

    云计算多租户几乎用于所有软件即服务 (Software as a Service, SaaS) 应用程序,因为计算资源是可伸缩的,而且这些资源的分配由实际使用决定.话虽如此,用户可以通过 Intern ...

  3. ASP.NET MVC中全局异常处理

    以前不知道从哪里找到的处理全局异常的,觉得蛮好用就记下来了. 1, 建立MyExecptionAttribute.cs类,写入如下代码: using System; using System.Coll ...

  4. Atitit 路径规划法attilax总结 扫描线路法

    Atitit 路径规划法attilax总结 扫描线路法 2017/2/8 20:43:37[吐槽]深圳-小 2017/2/8 20:43:37 群主做什么的2017/2/10 10:03:15系统消  ...

  5. analysis-what-blockchain-technology-means-for-artificial-intelligence-cm888540

    http://m.nasdaq.com/article/analysis-what-blockchain-technology-means-for-artificial-intelligence-cm ...

  6. 用NPOI创建Excel、合并单元格、设置单元格样式、边框的方法

    本篇文章小编为大家介绍,用NPOI创建Excel.合并单元格.设置单元格样式.边框的方法.需要的朋友参考下 今天在做项目中,遇到使用代码生成具有一定样式的Excel,找了很多资料,最后终于解决了,Ex ...

  7. 移动WEB开发基础入门

    什么是移动WEB开发,我个人理解就是,将网页更好的显示在移动端的一些设置,简单来说就两点如下: 1.流式布局,即百分比自适应布局 将body下的div容器的样式设置如下: div{ width:100 ...

  8. Android 视觉规范

    各种分辨率下图片尺寸 工程师通常开发三套UI布局文件,所以设计师通常也要不同的需求处理2-3套切图. con Type Standard Asset Sizes (in Pixels), for Ge ...

  9. 【Android】Handler详解

    Handler的定义 主要接受子线程发送的数据, 并用此数据配合主线程更新UI. 解释: 当应用程序启动时,Android首先会开启一个主线程 (也就是UI线程) , 主线程为管理界面中的UI控件,进 ...

  10. js 将数字转换成人民币大写的方法

    //将数字转换成人民币大写的方法 var digitUppercase = function (n) { var fraction = ['角', '分']; var digit = [ '零', ' ...