RMI远端方法调用

一、RMI介绍

RMI(Remote Method Invocation),RMI是分布式对象软件包,它简化了在多台计算机上的JAVA应用之间的通信。
必须在jdk1.1以上,RMI用到的类:
java.rmi.Remote  所有可以被远程调用的对象都必须实现该接口
java.rmi.server.UnicastRemoteObject  所有可以被远程调用的对象都必须扩展该类
什么是RMI ?
远程方法调用是一种计算机之间对象互相调用对方函数,启动对方进程的一种机制,使用这种机制,某一台计算机上的对象在调用另外一台计算机上的方法时,使用的程序语法规则和在本地机上对象间的方法调用的语法规则一样

RMI的优势
这种机制给分布计算的系统设计、编程都带来了极大的方便。只要按照RMI规则设计程序,可以不必再过问在RMI之下的网络细节了,如:TCP和Socket等等。任意两台计算机之间的通讯完全由RMI负责。调用远程计算机上的对象就像本地对象一样方便。
1、面向对象:
RMI可将完整的对象作为参数和返回值进行传递,而不仅仅是预定义的数据类型。也就是说,可以将类似Java Hash表这样的复杂类型作为一个参数进行传递。
2、可移动属性:
RMI可将属性从客户机移动到服务器,或者从服务器移动到客户机。
3、设计方式:
对象传递功能使你可以在分布式计算中充分利用面向对象技术的强大功能,如二层和三层结构系统。如果用户能够传递属性,那么就可以在自己的解决方案中使用面向对象的设计方式。所有面向对象的设计方式无不依靠不同的属性来发挥功能,如果不能传递完整的对象——包括实现和类型——就会失去设计方式上所提供的优点。
4、安全性:
RMI使用Java内置的安全机制保证下载执行程序时用户系统的安全。RMI使用专门为保护系统免遭恶意小程序侵害而设计的安全管理程序。
5、便于编写和使用
RMI使得Java远程服务程序和访问这些服务程序的Java客户程序的编写工作变得轻松、简单。远程接口实际上就是Java接口。为了实现RMI的功能必须创建远程对象任何可以被远程调用的对象必须实现远程接口。但远程
接口本身并不包含任何方法。因而需要创建一个新的接口来扩展远程接口。
新接口将包含所有可以远程调用的方法。远程对象必须实现这个新接口,由于新的接口扩展了
远程接口,实现了新接口,就满足了远程对象对实现远程接口的要求,所实现的每个对象都将
作为远程对象引用。

RMI的劣势
从上面的过程来看,RMI对服务器的IP地址和端口依赖很紧密,但是在开发的时候不知道将来的服务器IP和端口如何,但是客户端程序依赖这个IP和端口。这也是RMI的局限性之一。这个问题有两种解决途径:一是通过DNS来解决,二是通过封装将IP暴露到程序代码之外。
RMI的局限性之二是RMI是Java语言的远程调用,两端的程序语言必须是Java实现,对于不同语言间的通讯可以考虑用Web Service或者公用对象请求代理体系(CORBA)来实现

RMI与Socket的比较
RMI技术比较socket的网络编程主要有以下几个方面:
    第一、RMI是面向对象的,而后者不是。
    第二、RMI是与语言相绑定的。比如当你使用Java RMI技术的时候,客户端与服务器端都必须使用Java开发。而socket的网络编程是使用独立于开发语言的,甚至独立于平台。基于socket的网络编程,客户端与服务器端可以使用不同开发语言和不同的平台。
    第三、从网络协议栈的观点来看,RMI与socket的网络编程处于不同层次上。基于socket的网络编程位于TCP协议之上,而RMI在TCP协议之上,又定义了自己的应用协议,其传输层采用的是Java远程方法协议(JRMP)。可见,在网络协议栈上,基于RMI的应用位置更高一些,这也决定了,与socket的网络编程相比,RMI会丧失一些灵活性和可控性,但是好处是它带给了应用开发者更多的简洁,方便和易用。比如:如果你用的是RMI,你不需要关心消息是怎么序列化的,你只需要像本地方法调用一样,使用RMI。代价是:应用开发者无法很好地控制消息的序列化机制。
    第四、这是最后一点不同,我认为也是比较重要的一点,就是两种方法的性能比较,其往往决定着你将使用那种技术来开发你的应用。
    实验的结果是:RMI与TCP based socket相比,传输相同的有效数据,RMI需要占用更多的网络带宽(protocol overhead)。从这里,我们可以得出一个一般性的结论:RMI主要是用于远程方法的”调用“(RMI是多么的名符其实:)),其技术内涵强调的是 “调用”,基于此,我能想到的是:移动计算,和远程控制,当你的应用不需要在client与server之间传输大量的数据时,RMI是较好的选择,它简洁、易于开发。但是,一旦你的应用需要在client与server之间传输大量的数据,极端的,比如FTP应用,则RMI是不适合的,我们应该使用 socket。
PS: RMI的效率还是很高的,一般情况下会比Hessian更高效,比Web Service更是高效很多;当然和socket这种东东相比,当然要低效一点了,socket更底层一些啊。RMI的具体实现,依然是依赖于底层的Socket编程

二、RMI交互图

方法调用从客户对象经存根(stub)、远程引用层(Remote Reference Layer)和传输层(Transport Layer)向下,传递给主机,然后再次经传输层,向上穿过远程调用层和骨干网(Skeleton),到达服务器对象。
存根扮演着远程服务器对象的代理的角色,使该对象可被客户激活。
远程引用层处理语义、管理单一或多重对象的通信,决定调用是应发往一个服务器还是多个。
传输层管理实际的连接,并且追踪可以接受方法调用的远程对象。
骨干网完成对服务器对象实际的方法调用,并获取返回值。
返回值向下经远程引用层、服务器端的传输层传递回客户端,再向上经传输层和远程调用层返回。最后,存根获得返回值。

三、示例

  1.服务端创建服务接口继承Remote  

import java.rmi.Remote;
import java.rmi.RemoteException;

/**  
 * @Title:  IRemote.java   
 * @Package    
 * @Description:    TODO(用一句话描述该文件做什么)   
 * @author: liandy    
 * @date:   2019年7月13日 下午3:28:52   
 * @version V1.0 
 */

/**   
 * @ClassName:  IRemote   
 * @Description:TODO(这里用一句话描述这个类的作用)   
 * @author: liandy 
 * @date:   2019年7月13日 下午3:28:52   
 *     
 */
public interface IService extends Remote{ 
    String show()throws RemoteException;//声明方法
}
IService

  2.服务端创建服务实现类并继承UnicastRemoteObject

import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;

/**  
 * @Title:  ServiceImpl.java   
 * @Package    
 * @Description:    TODO(用一句话描述该文件做什么)   
 * @author: liandy    
 * @date:   2019年7月13日 下午3:30:17   
 * @version V1.0 
 */

/**   
 * @ClassName:  ServiceImpl   
 * @Description:TODO(这里用一句话描述这个类的作用)   
 * @author: liandy 
 * @date:   2019年7月13日 下午3:30:17   
 *     
 */
public class ServiceImpl extends UnicastRemoteObject implements IService{

    /**   
     * @Title:  ServiceImpl   
     * @Description:    TODO(这里用一句话描述这个方法的作用)   
     * @param:  @throws RemoteException  
     * @throws   
     */
    protected ServiceImpl() throws RemoteException {
        super();
        // TODO Auto-generated constructor stub
    }

    /**   
     * <p>Title: show</p>   
     * <p>Description: </p>   
     * @return
     * @throws RemoteException   
     * @see IService#show()   
     */
    @Override
    public String show() throws RemoteException {
        // TODO Auto-generated method stub
        return "show";
    }

}
ServiceImpl

  3.客户端调用

import java.net.MalformedURLException;
import java.rmi.Naming;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;

/**  
 * @Title:  Client.java   
 * @Package    
 * @Description:    TODO(用一句话描述该文件做什么)   
 * @author: liandy    
 * @date:   2019年7月13日 下午3:33:29   
 * @version V1.0 
 */

/**   
 * @ClassName:  Client   
 * @Description:TODO(这里用一句话描述这个类的作用)   
 * @author: liandy 
 * @date:   2019年7月13日 下午3:33:29   
 *     
 */
public class Client {

    /**
     * @throws NotBoundException 
     * @throws RemoteException 
     * @throws MalformedURLException    
     * @Title: main   
     * @Description: TODO(这里用一句话描述这个方法的作用)   
     * @param: @param args      
     * @return: void      
     * @throws   
     */
    public static void main(String[] args) throws MalformedURLException, RemoteException, NotBoundException {
        // TODO Auto-generated method stub
        IService r = (IService) Naming.lookup("rmi://localhost:1234/testrmi");//获取远程1234端口对象注册表中testrmi的stub
        String a = r.show();//调用引用的方法,实际上调用的是stub,由stub与服务端交互并返回结果
        System.out.println(a);
    }

}
Client
原文地址:https://www.cnblogs.com/liandy001/p/11182055.html