• RMI原理及简单示例


    分布式对象

    在学习 RMI 之前,先来分布式对象(Distributed Object):分布式对象是指一个对象可以被远程系统所调用。对于 Java 而言,即对象不仅可以被同一虚拟机中的其他客户程序(Client)调用,也可以被运行于其他虚拟机中的客户程序调用,甚至可以通过网络被其他远程主机之上的客户程序调用。

    下面的图示说明了客户程序是如何调用分布式对象的:

    从图上我们可以看到,分布式对象被调用的过程是这样的:

    1. 客户程序调用一个被称为 Stub (存根)的客户端代理对象。该代理对象负责对客户端隐藏网络通讯的细节。Stub 知道如何通过网络套接字(Socket)发送调用,包括如何将调用参数转换为适当的形式以便传输等。
    2. Stub 通过网络将调用传递到服务器端,也就是分布对象一端的一个被称为 Skeleton(骨干) 的代理对象。同样,该代理对象负责对分布式对象隐藏网络通讯的细节。Skeleton 知道如何从网络套接字(Socket)中接受调用,包括如何将调用参数从网络传输形式转换为 Java 形式等。
    3. Skeleton 将调用传递给分布式对象。分布式对象执行相应的调用,之后将返回值传递给 Skeleton,进而传递到 Stub,最终返回给客户程序。

    这个场景基于一个基本的法则,即行为的定义和行为的具体实现相分离。如图所示,客户端代理对象 Stub 和分布式对象都实现了相同的接口,该接口称为远程接口(Remote Interface)。正是该接口定义了行为,而分布式对象本身则提供具体的实现。对于 Java RMI 而言,我们用接口(interface)定义行为,用类(class)定义实现。

    RMI架构

    RMI就是基于上述分布式原理的实现的,它的底层架构示意图如下:


    1. Stub/Skeleton 层:该层提供了客户程序和服务程序彼此交互的接口。
    2. 远程引用(Remote Reference)层:这一层相当于在其之上的 Stub/Skeleton 层和在其之下的传输协议层之前的中间件,负责处理远程对象引用的创建和管理。
    3. 传输协议(Transport Protocol) 层:该层提供了数据协议,用以通过线路传输客户程序和远程对象间的请求和应答。

    Java RMI 的客户程序使用客户端的 Stub 向远程对象请求方法调用;服务器对象则通过服务器端的 Skeleton 接受请求。

    在 Java 1.2 之后,RMI 不再需要 Skeleton 对象,而是通过 Java 的反射机制(Reflection)来完成对服务器端的远程对象的调用;而在Java 1.5之后,使用了UnicastRemoteObject类就可以不需要手动生成stub了,而是由Proxy动态生成,客户端从服务器端下载stub后装载。

    整个调用过程如下:客户端从服务器端下载stub到本地,stub把客户端的参数序列化后,传至远程引用层;远程引用层根据RMI协议转换为传输层数据,通过传输层把数据传到服务器端;服务器端接收到从传输层传过来的数据,通过远程引用层通过RMI协议进行转换,Skeleton 把参数反序列化后传递给服务器端的方法调用。如果方法调用产生异常或返回值再经由Skeleton 序列化给客户端,客户端再反序列化。(序列化可参考这里。)

    创建RMI流程

    建立RMI的流程如下:

    1. 通过分析需求定义远程接口(客户端和服务器端公用的),此接口必须扩展java.rmi.Remote,且远程方法必须声明抛出 java.rmi.RemoteException 异常,或者该异常的超类(Superclass)。
    2. 服务器端实现远程接口,为了不手动生成stub需要继承UnicastRemoteObject类,并调用其构造器;
    3. 服务器端注册服务并启动;
    4. 客户端查询服务并调用远程方法;

    示意图如下:

    代码示例

    分别建立三个项目:服务器端(DemoRMI.Server)、客户端(DemoRMI.Client)和远程接口(DemoRMI.RmoteInterface),如下图:

    DemoRMI.RmoteInterface中的IHello.java

    package net.oseye.DemoRMI.RmoteInterface;
    
    import java.rmi.Remote;
    import java.rmi.RemoteException;
    
    /**
     * 远程接口
     * @author oseye
     */
    public interface IHello extends Remote{
    
    	String sayHello(String name) throws RemoteException;
    }

    DemoRMI.Server中的HelloImpl.java

    package net.oseye.DemoRMI.Server;
    
    import java.rmi.RemoteException;
    import java.rmi.server.UnicastRemoteObject;
    
    import net.oseye.DemoRMI.RmoteInterface.IHello;
    
    @SuppressWarnings("serial")
    public class HelloImpl extends UnicastRemoteObject implements IHello {
    
    	protected HelloImpl() throws RemoteException {
    		super();
    	}
    
    	public String sayHello(String name) {
    		return "Hello,"+name;
    	}
    }

    DemoRMI.Server中的App.java

    package net.oseye.DemoRMI.Server;
    
    import java.net.MalformedURLException;
    import java.rmi.Naming;
    import java.rmi.RemoteException;
    import java.rmi.registry.LocateRegistry;
    
    import net.oseye.DemoRMI.RmoteInterface.IHello;
    
    /**
     * 服务器端
     * @author oseye
     */
    public class App 
    {
        public static void main( String[] args ) throws RemoteException, MalformedURLException
        {
            IHello hello=new HelloImpl();
            
            LocateRegistry.createRegistry(1099);
            Naming.rebind("rmi://10.9.146.113/hello", hello);
            System.out.println("ok");
        }
    }

    DemoRMI.Client中的App.java:

    package net.oseye.DemoRMI.Client;
    
    import java.net.MalformedURLException;
    import java.rmi.Naming;
    import java.rmi.NotBoundException;
    import java.rmi.RemoteException;
    
    import net.oseye.DemoRMI.RmoteInterface.IHello;
    
    /**
     * RMI客户端
     * @author oseye
     */
    public class App 
    {
        public static void main( String[] args ) throws MalformedURLException, RemoteException, NotBoundException
        {
        	IHello hello=(IHello) Naming.lookup("rmi://10.9.146.113/hello");    	
            System.out.println( hello.sayHello("oseye"));
        }
    }

    运行

    1. 首先运行服务器端,切换到classes目录:

      cd /D D:workspaceDemoRMI.Server	argetclasses
      执行服务器端:
      java -cp .;D:/workspace/DemoRMI.RmoteInterface/target/classes  net.oseye.DemoRMI.Server.App
      PS:如果你嫌使用classpath麻烦,可以打个包执行

      输出

      ok

    2. 运行客户端,也先切换classes目录:
      cd /D D:workspaceDemoRMI.Client	argetclasses
      执行客户端:
      java -cp .;D:workspaceDemoRMI.RmoteInterface	argetclasses net.oseye.DemoRMI.Client.App
      输出

      Hello,oseye

    出处:http://www.zhaiqianfeng.com    
    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
  • 相关阅读:
    《STL源码剖析》-- 序列式容器
    IPV6 组播学习理解
    C 语言指针 引用学习
    C++ list 源码学习
    汇编基础学习---浮点运算
    C++ 运算符重载
    C++ 迭代器学习
    Play框架连接Mysql遇到的一些问题
    Jython中文乱码问题
    多线程之生产者消费者模式
  • 原文地址:https://www.cnblogs.com/zhaiqianfeng/p/4620357.html
Copyright © 2020-2023  润新知