每日扫盲一,java的rmi、

JAVA RMI 原理和使用浅析

本地对象调用

我们先看看本地对象方法的调用:

ObjectClass objectA = new ObjectClass(); 
String retn = objectA.Method();

但是想想,如果objectA对象在JVM a上;而我们的程序在JVM b上,而且想访问JVM a上的objectA对象方法,如何做呢?对于JVM b上的应用程序来说,是不知道JVM a上创建的ObjectClass实例对象名称是什么,因为这次我创建的实例对象可能是objectA,下次我程序一改,我创建的实例对象又叫objectB了,另外,我创没创建ObjectClass实例对象,JVM b上应用程序又怎么知道呢?


RMI就解决了这个问题。

工作原理


方法调用从客户对象经占位程序(Stub)、远程引用层(Remote Reference Layer)和传输层(Transport Layer)向下,传递给主机,然后再次经传 输层,向上穿过远程调用层和骨干网(Skeleton),到达服务器对象。 占位程序扮演着远程服务器对象的代理的角色,使该对象可被客户激活。 远程引用层处理语义、管理单一或多重对象的通信,决定调用是应发往一个服务器还是多个。传输层管理实际的连接,并且追踪可以接受方法调用的远程对象。服务器端的骨干网完成对服务器对象实际的方法调用,并获取返回值。返回值向下经远程引用层、服务器端的传输层传递回客户端,再向上经传输层和远程调用层返回。最后,占位程序获得返回值。 
要完成以上步骤需要有以下几个步骤: 
1、 生成一个远程接口 
2、 实现远程对象(服务器端程序)
3、 生成占位程序和骨干网(服务器端程序)
4、 编写服务器程序 
5、 编写客户程序 
6、 注册远程对象 
7、 启动远程对象  

由于有RMI系统的支持,我们写RMI应用程序时只需要继承相关类,实现相关接口就可以了。也就是说,我们只需要定义接口、接口实现、客户端程序和服务端程序就可以了。

 


上图中的stub和skeleton代理都是在服务端程序中由RMI系统动态生成,服务端程序只需要继承java.rmi.server.UnicastRemoteObject类。

那么上图中的RMI Service(RMI registry)是怎么回事呢?

先卖个关子:

可以说,RMI由3个部分构成,第一个是RMIService即JDK提供的一个可以独立运行的程序(bin目录下的rmiregistry),第二个是RMIServer即我们自己编写的一个java项目,这个项目对外提供服务。第三个是RMIClient即我们自己编写的另外一个java项目,这个项目远程使用RMIServer提供的服务。
首先,RMIService必须先启动并开始监听对应的端口。
其次,RMIServer将自己提供的服务的实现类注册到RMIService上,并指定一个访问的路径(或者说名称)供RMIClient使用。
最后,RMIClient使用事先知道(或和RMIServer约定好)的路径(或名称)到RMIService上去寻找这个服务,并使用这个服务在本地的接口调用服务的具体方法。
通俗的讲完了再稍微技术的讲下:
首先,在一个JVM中启动rmiregistry服务,启动时可以指定服务监听的端口,也可以使用默认的端口。
其次,RMIServer在本地先实例化一个提供服务的实现类,然后通过RMI提供的Naming,Context,Registry等类的bind或rebind方法将刚才实例化好的实现类注册到RMIService上并对外暴露一个名称。
最后,RMIClient通过本地的接口和一个已知的名称(即RMIServer暴露出的名称)再使用RMI提供的Naming,Context,Registry等类的lookup方法从RMIService那拿到实现类。这样虽然本地没有这个类的实现类,但所有的方法都在接口里了,想怎么调就怎么调吧。
值得注意的是理论上讲RMIService,RMIServer,RMIClient可以部署到3个不同的JVM中,这个时候的执行的顺序是RMIService---RMIServer—RMIClient。另外也可以由RMIServer来启动RMIService这时候执行的顺序是RMIServer—RMIService—RMIClient。

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

  

RMI远程调用步骤
RMI的交互图:

RMI由3个部分构成,第一个是rmiregistry(JDK提供的一个可以独立运行的程序,在bin目录下),第二个是server端的程序,对外提供远程对象,第三个是client端的程序,想要调用远程对象的方法。
首先,先启动rmiregistry服务,启动时可以指定服务监听的端口,也可以使用默认的端口(1099)。
其次,server端在本地先实例化一个提供服务的实现类,然后通过RMI提供的Naming/Context/Registry(下面实例用的Registry)等类的bind或rebind方法将刚才实例化好的实现类注册到rmiregistry上并对外暴露一个名称。
最后,client端通过本地的接口和一个已知的名称(即rmiregistry暴露出的名称)再使用RMI提供的Naming/Context/Registry等类的lookup方法从RMIService那拿到实现类。这样虽然本地没有这个类的实现类,但所有的方法都在接口里了,便可以实现远程调用对象的方法了。

存根和骨干网的具体通信过程:

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

JAVA RMI简单示例

本示例是client端调用server端远程对象的加减法方法,具体步骤为:

1. 定义一个远程接口(这个是存在于server端的类)

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

/**
 * 必须继承Remote接口。
 * 所有参数和返回类型必须序列化(因为要网络传输)。
 * 任意远程对象都必须实现此接口。
 * 只有远程接口中指定的方法可以被调用。
 */
public interface IRemoteMath extends Remote {

      // 所有方法必须抛出RemoteException
    public double add(double a, double b) throws RemoteException;
    public double subtract(double a, double b) throws RemoteException;
    
}

2、远程接口实现类(存在于服务器端计算机的类)

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

/**
 * 服务器端实现远程接口。
 * 必须继承UnicastRemoteObject,以允许JVM创建远程的存根/代理。
 */
public class RemoteMath extends UnicastRemoteObject implements IRemoteMath {

    private int numberOfComputations;
    
    protected RemoteMath() throws RemoteException {
        numberOfComputations = 0;
    }
    
    @Override
    public double add(double a, double b) throws RemoteException {
        numberOfComputations++;
        System.out.println("Number of computations performed so far = " 
                + numberOfComputations);
        return (a+b);
    }

    @Override
    public double subtract(double a, double b) throws RemoteException {
        numberOfComputations++;
        System.out.println("Number of computations performed so far = " 
                + numberOfComputations);
        return (a-b);
    }

}

3、 服务器端)(存在于服务器端计算机上)

import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import remote.IRemoteMath;

/**
 * 创建RemoteMath类的实例并在rmiregistry中注册。
 */
public class RMIServer {

    public static void main(String[] args)  {
        
        try {
            // 注册远程对象,向客户端提供远程对象服务。
            // 远程对象是在远程服务上创建的,你无法确切地知道远程服务器上的对象的名称,
            // 但是,将远程对象注册到RMI Registry之后,
            // 客户端就可以通过RMI Registry请求到该远程服务对象的stub,
            // 利用stub代理就可以访问远程服务对象了。
            IRemoteMath remoteMath = new RemoteMath();  
            LocateRegistry.createRegistry(1099);    
            Registry registry = LocateRegistry.getRegistry();
            registry.bind("Compute", remoteMath);
            System.out.println("Math server ready");
            // 如果不想再让该对象被继续调用,使用下面一行
            // UnicastRemoteObject.unexportObject(remoteMath, false);
        } catch (Exception e) {
            e.printStackTrace();
        }        
        
    }
    
}

4. 客户端(存在于客户端计算机上)

import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import remote.IRemoteMath;

public class MathClient {

    public static void main(String[] args) {
        
        try { 
            // 如果RMI Registry就在本地机器上,URL就是:rmi://localhost:1099/hello
            // 否则,URL就是:rmi://RMIService_IP:1099/hello
            Registry registry = LocateRegistry.getRegistry("localhost");        
            // 从Registry中检索远程对象的存根/代理
            IRemoteMath remoteMath = (IRemoteMath)registry.lookup("Compute");
            // 调用远程对象的方法
            double addResult = remoteMath.add(5.0, 3.0);
            System.out.println("5.0 + 3.0 = " + addResult);
            double subResult = remoteMath.subtract(5.0, 3.0);
            System.out.println("5.0 - 3.0 = " + subResult);            
        }catch(Exception e) {
            e.printStackTrace();
        }
                
    }
    
}

结果如下:

server端

 

client端

href:

https://blog.csdn.net/qq_28081453/article/details/83279066#JAVA_RMI_21

https://blog.csdn.net/xinghun_4/article/details/45787549

猜你喜欢

转载自www.cnblogs.com/isme-zjh/p/11596429.html