Java RMI 调用浅析

RMI(Remote Method Invocation)为远程方法调用,是允许运行在一个Java虚拟机的对象调用运行在另一个Java虚拟机上的对象的方法。 这两个虚拟机可以是运行在相同计算机上的不同进程中,也可以是运行在网络上的不同计算机中。(抄的定义)

我们知道远程过程调用(Remote Procedure Call, RPC)可以用于一个进程调用另一个进程(很可能在另一个远程主机上)中的过程,从而提供了过程的分布能力。Java 的 RMI 则在 RPC 的基础上向前又迈进了一步,即提供分布式对象间的通讯。

1.Stub和Skeleton

RMI框架采用代理来负责客户与远程对象之间通过Socket进行通信的细节。RMI框架为远程对象分别生成了客户端代理和服务器端代理。位于客户端的代理类称为存根(Stub),位于服务器端的代理类称为骨架(Skeleton)。

image

2.工作原理

22989531-b7a675415fce2716.png

22989531-5e56796de21913f7.png

3.实现思路

(1)创建远程接口(RemoteInterface):直接或间接继承java.rmi.Remote接口接口中需要被远程调用的方法,必须抛出RemoteException异常。
(2)创建远程类(RemoteImpl):实现远程接口。为了使远程类的实例变成能为远程客户提供服务的远程对象,可通过以下两种途径之一把它导出(export)为远程对象:
   (a). 使远程类实现远程接口时,同时继承java.rmi.server.UnicastRemoteObject类,这是最常用的方式.

public class RemoteImpl extends UnicastRemoteObject implements RemoteInterface{
}

   (b)可以在构造方法中调用UnicastRemoteObject类的静态exportObject()方法,同样,远程类的构造方法也必须声明抛出RemoteException。。

  public RemoteImpl ()throws RemoteException{
    UnicastRemoteObject.exportObject(this,0);
  }

(3)创建服务器程序(Server):创建远程对象,通过createRegistry()方法注册远程对象。并通过bind或者rebind方法,把远程对象绑定到指定名称空间(URL)中。

            RemoteInterface remoteService = new RemoteImpl();
            // 把远程对象注册到RMI注册服务器上,并命名为RemoteObj(名字可自定义,客户端要对应)
            // 绑定的URL标准格式为:rmi://host:port/name(其中协议名可以省略,下面两种写法都是正确的)
            Naming.rebind(URL, remoteService);// 将stub代理绑定到Registry服务的URL上

(4)创建客户程序(Client):通过 lookup()方法查找远程对象,进行远程方法调用

//          // 在RMI服务注册表中查找名称为RemoteObj的对象,并调用其上的方法
//          // 客户端通过命名服务Naming获得指向远程对象的远程引用
//          RemoteInterface rmObj = (RemoteInterface) Naming.lookup(URL);
//          rmObj .doSomething();

4.扩展

(1)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需要占用更多的网络带宽。
  RMI主要是用于远程方法的”调用“(RMI是多么的名符其实:)),其技术内涵强调的是 “调用”,当你的应用不需要在client与server之间传输大量的数据时,RMI是较好的选择。但是,一旦你的应用需要在client与server之间传输大量的数据,极端的,比如FTP应用,则RMI是不适合的,我们应该使用 socket。

PS: RMI的效率还是很高的,一般情况下会比Hessian更高效,比Web Service更是高效很多;当然和socket相比,当然要低效一点了,socket更底层一些啊。RMI的具体实现,依然是依赖于底层的Socket编程。

(2)RMI使用:

   由于RMI调用要求一个远程服务(service)对应一个port,所以管理时要注意一一对应,最好建表。使用时一般结合动态代理使用。

client = (T) Proxy.newProxyInstance(this.getClass().getClassLoader(), new Class[] {classObj}, (obj, mtd, args) -> {
                try{
                    IRMIClient rmiClient = rmiClientMap.get(key);
                    if (rmiClient == null) {
                        Registry reg = LocateRegistry.getRegistry(ip, port);
                        rmiClient = (IRMIClient) reg.lookup(key);
                        IRMIClient previous = rmiClientMap.putIfAbsent(key, rmiClient);
                        if (previous != null) {
                            rmiClient = previous;
                        }
                    }
                    return rmiClient.exec(classObj, new MethodSign(mtd), args);
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容