Java RMI详解

来源:互联网 发布:excel多张表格数据汇总 编辑:程序博客网 时间:2024/05/05 06:31

Java RMI详解

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

工作方式

运行平台

RMI目前(Java Remote Messaging Protocol)进行通信。JRMP是专为Java的远程对象制定的协议。因此,Java RMI具有Java的”Write Once,Run Anywhere”的优点,是分布式应用系统的百分之百纯Java解决方案。用Java RMI开发的应用系统可以部署在任何支持JRE(Java Run Environment Java,运行环境)的平台上。但由于JRMP是专为Java对象制定的,因此,RMI对于用非Java语言开发的应用系统的支持不足。不能与用非Java语言书写的对象进行通信。

下面将介绍RMI的优点以及如何将其连接到现有的和原有的系统中,以及与。

计算途径

Java Remote Method Invocation (RMI – Java远程方法调用)提供了简单而直接的途径。这些对象可以是新的Java对象,也可以是围绕现有API的简单的Java包装程序。Java体现了“编写一次就能在任何地方运行的模式。而RMI可将Java模式进行扩展,使之可在任何地方运行”。

因为RMI是以Java为核心的,所以,它将Java的安全性和可移植性等强大功能带给了分布式计算。您可将代理和业务逻辑等属性移动到网络中最合适的地方。如果您要扩展Java在系统中的使用,RMI将使您充分利用其强大功能。

RMI还可利用标准JDBC包与现有的关系数据库连接。RMI/JNI和RMI/JDBC相结合,可帮助您利用RMI与目前使用非Java语言的现有服务器进行通信,而且在您需要时可扩展Java在这些服务器上的使用。RMI可帮助您在扩展使用时充分利用Java的强大功能。

RMI客户机类

RMI客户使用java.rmi.Naming.lookup()方法,在指定的远程主机上查找RMI服务对象,若找到就把它转换成本地接口RMIOperate类型。
它与CORBA不同之处在于RMI客户机必须知道提供RMI服务主机的URL,这个URL可以通过rmi://host/path或rmi://host:port/path来指定,如果省略端口号,就默认使用1099。

Java.rmi.Naming.lookup()方法可能产生三个异常:Java.rmi.RemoteException、Java.rmi.NotBoundException、 MalformedURLException,异常都需要捕获。

//HelloClient.javaimport java.rmi.*;public class HelloClient {    public static void main(String[] args) {        // 在服务器端设置安全机制        /*        if (System.getSecurityManager() == null) {        System.setSecurityManager(new RMISecurityManager());        }        */        /* 默认为本地主机和默认端口 */        String host = "localhost:1099";        /* 带输入参数时,将host设置为指定主机 */        if (args.length > 0)        host = args[0];        try {        /* 根据指定的URL定位远程实现对象 */        /* “h”是一个标识符,我们将用它指向实现“Hello”接口的远程对象 */        Hello h = (Hello) Naming.lookup("rmi://" + host + "/HelloService");        System.out.println("实现“Hello”接口的远程对象: " + h);        System.out.println("我在客户端,开始调用RMI服务器端的'sayHello'方法");        System.out.println("欢迎, " + h.sayHello("javamxj blog"));        } catch (Exception ex) {        System.out.println("错误 " + ex);    }}

Java RMI 框架架构

RMI远程调用步骤:

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

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

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

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

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

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

7,客户对象获得返回值

对于客户对象来说,步骤2-6是完全透明的

搭建一个RMI服务的过程分为以下7步

  1. 创建远程方法接口,该接口必须继承自Remote接口

Remote 接口是一个标识接口,用于标识所包含的方法可以从非本地虚拟机上调用的接口,Remote接口本身不包含任何方法。

package server;import java.rmi.Remote;import java.rmi.RemoteException;public interface Hello extends Remote {    public String sayHello(String name) throws RemoteException;}

由于远程方法调用的本质依然是网络通信,只不过隐藏了底层实现,网络通信是经常会出现异常的,所以接口的所有方法都必须抛出RemoteException以说明该方法是有风险的。

  1. 创建远程方法接口实现类:
    UnicastRemoteObject类的构造函数抛出了RemoteException,故其继承类不能使用默认构造函数,继承类的构造函数必须也抛出RemoteException

由于方法参数与返回值最终都将在网络上传输,故必须是可序列化的

package server;import java.rmi.RemoteException;import java.rmi.server.UnicastRemoteObject;public class HelloImpl extends UnicastRemoteObject implements Hello {    private static final long serialVersionUID = -271947229644133464L;    public HelloImpl() throws RemoteException{        super();    }    public String sayHello(String name) throws RemoteException {        return "Hello,"+name;    }}
  1. 利用java自带rmic工具生成sutb存根类(jdk/bin/rmic)

    jdk1.2以后的RMI可以通过反射API可以直接将请求发送给真实类,所以不需要skeleton类了

sutb存根为远程方法类在本地的代理,是在服务端代码的基础上生成的,需要HelloImpl.class文件,由于HelloImpl继承了Hello接口,故Hello.class文件也是不可少的。 目录结构:

Test
- - server
- - - - Hello.class
- - - - HelloImpl.class

方式1:

[Test]$ cd /home/name/Test/[Test]$ rmic server.HelloImpl

方式2:

[Test]$ rmic -classpath /home/name/Test server.HelloImpl

运行成功后将会生成HelloImpl_Stub.class文件

  1. 启动RMI注册服务(jdk/bin/rmiregistry)

    方式一:后台启动rmiregistry服务

    [jdk]$jdk/bin/rmiregistry 12312 &[1] 22720[jdk]$ ps -ef|grep rmiregistryname    22720 13763  0 16:43 pts/3    00:00:00jdk/bin/rmiregistry 12312name    22737 13763  0 16:43 pts/3    00:00:00 grep rmiregistry

    如果不带具体端口号,则默认为1099

方式二:人工创建rmiregistry服务,需要在代码中添加:

LocateRegistry.createRegistry(12312); 
  1. 编写服务端代码
package server;import java.rmi.Naming;import java.rmi.registry.LocateRegistry;public class HelloServer {    public static void main(String[] args) {        try{            Hello h = new HelloImpl();            //创建并导出接受指定port请求的本地主机上的Registry实例。            //LocateRegistry.createRegistry(12312);            /** Naming 类提供在对象注册表中存储和获得远程对远程对象引用的方法             *  Naming 类的每个方法都可将某个名称作为其一个参数,             *  该名称是使用以下形式的 URL 格式(没有 scheme 组件)的 java.lang.String:             *  //host:port/name             *  host:注册表所在的主机(远程或本地),省略则默认为本地主机             *  port:是注册表接受调用的端口号,省略则默认为1099,RMI注册表registry使用的著名端口             *  name:是未经注册表解释的简单字符串             */            //Naming.bind("//host:port/name", h);            Naming.bind("rmi://192.168.58.164:12312/Hello", h);            System.out.println("HelloServer启动成功");        }catch(Exception e){            e.printStackTrace();        }    }}

先创建注册表,然后才能在注册表中存储远程对象信息

  1. 运行服务端:
    类class的目录结构
    Test
      • server
          • Hello.class
          • HelloImpl.class
          • HelloServer.class
            [path ]$ java server.HelloServer
            HelloServer启动成功

当然classpath的设置,否则会报找不到相应的.class文件

  1. 编写客户端代码
   package client;import java.net.MalformedURLException;import java.rmi.Naming;import java.rmi.NotBoundException;import java.rmi.RemoteException;import server.Hello;public class HelloClient {    public static void main(String[] args) {        try {            Hello h = (Hello)Naming.lookup("rmi://192.168.58.164:12312/Hello");            System.out.println(h.sayHello("zx"));        } catch (MalformedURLException e) {            System.out.println("url格式异常");        } catch (RemoteException e) {            System.out.println("创建对象异常");            e.printStackTrace();        } catch (NotBoundException e) {            System.out.println("对象未绑定");        }    }}
  1. 运行客户端
    目录结构
    Test
      • client
          • HelloClient.class
      • server
          • Hello.class
          • HelloImpl_Stub.class//服务端生成的存根文件

[client]$ java client.HelloClient
Hello,zx

同服务器端,/home/name/Test一定要在CLASSPATH中.

RMI运行调用示例图


PS:
1. 客户端所在服务和服务端所在的服务器网络一定要通.


java RMI的缺点:
1. 从代码中也可以看到,代码依赖于ip与端口
2. RMI依赖于Java远程消息交换协议JRMP(Java Remote Messaging Protocol),该协议为java定制,要求服务端与客户端都为java编写。

0 0