龙空技术网

Java RMI 远程调用详解,优劣势说明

JAVA破局之路 457

前言:

当前大家对“java远程接口”大体比较看重,小伙伴们都需要剖析一些“java远程接口”的相关知识。那么小编同时在网摘上网罗了一些对于“java远程接口””的相关资讯,希望看官们能喜欢,朋友们快快来学习一下吧!

今天在看RPC看到了RMI这个东东,于是在网上查了学习了一下,借鉴了几个blog写的。

借鉴网站在最后有写。

Java RMI不是什么新技术,但却是是非常重要的底层技术。

概念

RMI(Remote Method Invocation,远程方法调用)是用Java在JDK1.2中实现的,它大大增强了Java开发分布式应用的能力。Java作为一种风靡一时的网络开发语言,其巨大的威力就体现在它强大的开发分布式网络应用的能力上,而RMI就是开发百分之百纯Java的网络分布式应用系统的核心解决方案之一。其实它可以被看作是RPC的Java版本。但是传统RPC并不能很好地应用于分布式对象系统。而Java RMI 则支持存储于不同地址空间的程序级对象之间彼此进行通信,实现远程对象之间的无缝远程调用。

RMI 调用步骤

RMI远程调用步骤:

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

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

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

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

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

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

7,客户对象获得返回值

搭建一个RMI服务

创建HelloInterface

package rmi;import java.io.Serializable;import java.rmi.Remote;import java.rmi.RemoteException;public interface HelloInterface extends Remote, Serializable {    /**     * 远程接口方法必须抛出 java.rmi.RemoteException     * 否则会报错,大家可以去掉看一下错误内容     */    String sayHello() throws RemoteException;}

创建HelloImpl

package rmi;import java.rmi.RemoteException;import java.rmi.server.UnicastRemoteObject;public class HelloImpl extends UnicastRemoteObject implements HelloInterface {    private String message;    /**     * 必须定义构造方法,即使是默认构造方法,     * 也必须把它明确地写出来,因为它必须抛出出RemoteException异常     * @param msg     * @throws RemoteException     */    public HelloImpl(String msg) throws RemoteException {        this.message = msg;    }    /**     * @return     * @throws RemoteException     * @Description:     * @author xingle     * @data 2015-9-28 下午4:39:41     */    @Override    public String sayHello() throws RemoteException {        System.out.println("Called by HelloClient");        return message;    }}

创建HelloServer:

package rmi;import java.net.MalformedURLException;import java.rmi.Naming;import java.rmi.RemoteException;import java.rmi.registry.LocateRegistry;/** *  服务端 * @author pine * @date 20200116 */public class HelloServer {    public static void main(String[] args) {        try {            // 启动RMI注册服务,指定端口为1099 (1099为默认端口)            LocateRegistry.createRegistry(1099);            // 创建远程对象的一个或多个实例            // 可以用不同名字注册不同的实例            HelloImpl service = new HelloImpl("hello,world!");            // 如果要把hello实例注册到另一台启动了RMI注册服务的机器上            // Naming.rebind("//192.168.1.105:1099/Hello",hello);            Naming.rebind("Hello", service);            System.out.println("Hello Server is ready.");        } catch (RemoteException e) {            System.out.println("Hello Server failed: " + e);            e.printStackTrace();        } catch (MalformedURLException e) {            e.printStackTrace();        }    }}

创建HelloClient:

package rmi;import java.rmi.Naming;/** * 客户端 * @author pine * @date 20200116 */public class HelloClient {    public static void main(String[] argv) {        try {            // 如果要从另一台启动了RMI注册服务的机器上查找hello实例            HelloInterface hello = (HelloInterface)Naming.            lookup("//127.0.0.1:1099/Hello");            // 调用远程方法            System.out.println(hello.sayHello());        } catch (Exception e) {            System.out.println("HelloClient exception: " + e);        }    }}

一个RMI服务就这样完成了,然后运行服务端HelloServer.java ,再运行客户端

HelloClient.java,然后观察控制台打印内容:


客户端

服务端

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实现基于Java的分布式计算)


希望阅读我,每个人都有收获,如果感觉不错可以转发一下大家一起学习。

标签: #java远程接口