JAVA_RMI使用快速入门

摘要

RMI目前使用Java远程消息交换协议JRMP(Java RemoteMessaging Protocol)进行通信。JRMP是专为Java的远程对象制定的协议。因此,Java RMI具有Java的“

一.RMI原理及介绍

1.基本介绍

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

JavaRemote Method Invocation ( RMI -- Java远程方法调用)允许您使用Java编写分布式对象。本文将介绍RMI的优点以及如何将其连接到现有的和原有的系统中,以及与用Java 编写的组件的连接。

RMI为采用Java对象的分布式计算提供了简单而直接的途径。这些对象可以是新的Java对象,也可以是围绕现有API的简单的Java包装程序。Java体现了“编写一次就能在任何地方运行的模式。而RMI可将Java模式进行扩展,使之可在任何地方运行”。因为RMI是以Java为核心的,所以,它将Java的安全性和可移植性等强大功能带给了分布式计算。务逻辑等属性移动到网络中最合适的地方。如果您要扩展Java在系统中的使用,RMI将使您充分利用其强大功能。

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

2.基本组成

一个正常工作的RMI系统由下面几个部分组成:

1)远程服务的接口定义
2)远程服务接口的具体实现
3)桩(Stub)和框架(Skeleton)文件
4)一个运行远程服务的服务器
5)一个RMI命名服务,它允许客户端去发现这个远程服务
6)类文件的提供者(一个HTTP或者FTP服务器)
7)一个需要这个远程服务的客户端程序

3.原理

RMI系统结构,在客户端和服务器端都有几层结构。
--------- ----------
| 客户 | | 服务器|
---------- ----------
| |
------------- ----------
| 占位程序 | | 骨干------------------
| 远程引用层 |
------------------------------------
| |
------------------------------------
| 传输层 |
------------------------------------

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

要完成以上步骤需要有以下几个步骤:

1)生成一个远程接口
   2)实现远程对象(服务器端程序)
   3)生成占位程序和骨干网(服务器端程序)
   4)编写服务器程序
   5)编写客户程序
    6)注册远程对象
   7)启动远程对象


二.RMI在spring环境下使用

1.首先导入各种spring的jar包(打钩的一定要有的)


2.编写接口以及实现类

publicinterface ICalculator {

publiclong add(long a, long b);

publiclong sub(long a, long b);

publiclong mul(long a, long b);

publiclong div(long a, long b);

}//参数必须是可序列化的参数实现java.io.Serializable

publicclass CalculatorImp implements ICalculator {}

3.配置spring文件

服务端:

<bean id="CalculatorServcie"class="cn.com.starit.test.server.imp.CalculatorImp">

</bean>

<bean id="serviceExporter"class="org.springframework.remoting.rmi.RmiServiceExporter">

<propertyname="service" ref="CalculatorServcie"/><!-- 服务-->

<propertyname="serviceName" value="calculatorServcie"/><!-- 服务名称 -->

<propertyname="serviceInterface" value="cn.com.starit.test.server.ICalculator"/><!--接口-->

<propertyname="registryPort" value="1099"/><!-- 注册端口 -->

</bean>

客户端:

<bean id="someServiceProxy"class="org.springframework.remoting.rmi.RmiProxyFactoryBean">

<propertyname="serviceUrl" value="rmi://192.168.100.105:1099/calculatorServcie"/>

<propertyname="serviceInterface" value="cn.com.starit.test.server.ICalculator"/>

</bean>

4.服务端启动会在web服务器启动的时候启动

如果不是web服务器,则需要线程进行启动。

ApplicationContext context = new ClassPathXmlApplicationContext("context/RmiServiceContext.xml");

          System.out.println("RMI starting ok.....");

5.客户端调用

获取服务端bean并强转为相应的接口就可以使用了

ApplicationContext context = new ClassPathXmlApplicationContext("context/RmiStubContext.xml");

ICalculator stub = (ICalculator) context.getBean("someServiceProxy");

stub.add(100,20);

参考资料:http://docs.huihoo.com/spring/zh-cn/remoting.html;

http://mhbjava.javaeye.com/blog/26597;




三.不在spring中使用RMI

分为以下四个步骤
1. 创建远程接口及声明远程方法(HelloInterface.java)
2. 实现远程接口及远程方法(继承UnicastRemoteObject)(Hello.java)
3. 启动RMI注册服务,并注册远程对象(HelloServer.java)
4. 客户端查找远程对象,并调用远程方法(HelloClient)
5. 执行程序:启动服务HelloServer;运行客户端HelloClient进行调用
具体代码及对应步骤如下:
1) 创建远程接口及声明远程方法(HelloInterface.java)

java 代码

package com.unmi;  

import java.rmi.*;

/**  

 * 远程接口必须扩展接口java.rmi.Remote  

 */

publicinterface HelloInterface extends Remote  

{  

/**  

   * 远程接口方法必须抛出 java.rmi.RemoteException

   */

public String say() throws RemoteException;  

}  

2) 实现远程接口及远程方法(继承UnicastRemoteObject)Hello.java

如果传递参数,参数必须是可序列化的参数实现java.io.Serializable

java 代码

package com.unmi;  

import java.rmi.*;  

import java.rmi.server.*;  

/**

* 扩展了UnicastRemoteObject类,并实现远程接口 HelloInterface

*/

publicclass Hello extends UnicastRemoteObject implements HelloInterface  

{  

private String message;  

/**

   * 必须定义构造方法,即使是默认构造方法,也必须把它明确地写出来,因为它必须抛出出RemoteException异常

   */

public Hello(String msg) throws RemoteException

  {  

    message =msg;  

  }  

/**

   * 远程接口方法的实现

   */

public String say() throws RemoteException  

  {  

     System.out.println("Called by HelloClient");  

return message;  

  }  

}

3) 启动RMI注册服务,并注册远程对象(HelloServer.java)

java 代码

package com.unmi;

import java.rmi.Naming;  

import java.rmi.registry.LocateRegistry;  

publicclass HelloServer  

{  

/**

   * 启动RMI 注册服务并进行对象注册

   */

publicstaticvoid main(String[] argv)  

  {  

try

     {  

//启动RMI注册服务,指定端口为1099 (1099为默认端口)

//也可以通过命令$java_home/bin/rmiregistry1099启动

//这里用这种方式避免了再打开一个DOS窗口

//而且用命令rmiregistry启动注册服务还必须事先用RMIC生成一个stub类为它所用

       LocateRegistry.createRegistry(1099);

//创建远程对象的一个或多个实例,下面是hello对象

//可以用不同名字注册不同的实例

       HelloInterface hello = new Hello("Hello,world!");    

//把hello注册到RMI注册服务器上,命名为Hello  

       Naming.rebind("Hello", hello);

//如果要把hello实例注册到另一台启动了RMI注册服务的机器上

//Naming.rebind("//192.168.1.105:1099/Hello",hello);

       System.out.println("HelloServer is ready.");  

     }  

catch (Exception e)  

     {  

       System.out.println("HelloServer failed: " + e);  

     }  

  }  

}  

4) 客户端查找远程对象,并调用远程方法(HelloClient)

java 代码

package com.unmi;  

import java.rmi.Naming;  

publicclass HelloClient  

{  

/**

   * 查找远程对象并调用远程方法

   */

publicstaticvoid main(String[] argv){  

try{  

   HelloInterfacehello = (HelloInterface) Naming.lookup("Hello");  

//如果要从另一台启动了RMI注册服务的机器上查找hello实例

//HelloInterface hello =(HelloInterface)Naming.lookup("//192.168.1.105:1099/Hello");  

//调用远程方法

       System.out.println(hello.say());  

     }  

catch (Exception e)  

     {  

       System.out.println("HelloClientexception: " + e);  

     }  

  }  

}

5)执行程序:启动服务HelloServer;运行客户端HelloClient进行调用
代码如何编译这里就不细讲
(1)打开一个Dos窗口执行命令 javacom.unmi.HelloServer 启动服务HelloServer
E:workspaceTestRMIbin>java com.unmi.HelloServer
Hello Server is ready.
运行成功则可以看到 HelloServer is ready
(2)打开另一个Dos窗口执行命令 javacom.unmi.HelloClient 运行客户端程序
E:workspaceTestRMIbin>java com.unmi.HelloClient
Hello, world!
调用成功则可以看到 Hello,world!
并且在启动服务端的窗口中看到紧跟 HelloServer is ready. 打印出
Called by HelloClient
如果您能一路顺畅的执行到这里,恭喜!您已度过了一个轻快的RMI之旅。

最后来个说明:
本实例中并没有用到JDK所带的命令 rmic 编译实现类得到存根(Stub)类,也没用命令rmiregistry 命令来启动RMI注册服务。在启动 rmiregistry之前必须能让它加载到相应的stub类,这就是造成**_Stub 类找不到的原因。
如果只是按上面的代码,则服务程序HelloServer 和客户端程序 HelloClient 都必须运行在本机(如此则RMI有何意义呢?);别急,只要修改HelloClient类,使用第二种形式的lookup查找语句,注释第一条 lookup语句,取消注释第二条lookup语句
        //HelloInterface hello =(HelloInterface) Naming.lookup("Hello");
     //如果要从另一台启动了RMI注册服务的机器上查找hello实例
       HelloInterfacehello =

(HelloInterface)Naming.lookup("//192.168.1.105:1099/Hello");
其中的IP地址和端口号1099为 RMI 注册服务器的IP和端口号,这样你的HelloClient就可以在另一台机器运行了,当然HelloInterface类必须能找到(但也可指定参数-Djava.rmi.server.codebase从网络加载HelloInterface类)。lookup("Hello")默认为从本机 127.0.0.1的1099端口上查找Hello命令对象,如果第二条语句写成lookup("192.168.1.105/Hello")与原语句是同等的,因为默认端口号就是1099。
代码中HelloServer 和 HelloClient 省略了设置安全管理器的过程 System.setSecurityManager(newRMISecurityManager()); ,如果设置的安全管理则必须编写相应的访问策略文件,并且在执行时指定参数
无论是启动服务端还是客户端都可以用参数

java.rmi.server.codebase=http://unmi.blogcn.cn/bin 的形式,像JNP一样从网络上加载类,这样更方便于RMI客户端的部署,如RMI客户端是一个Applet
可以拿单独一台机器运行rmiregistry (它需要能加载到相应的stub类,设置classpath)或用LocateRegistry.createRegistry(port),只作为 RMI远程对象的RMI集中注册的服务器,真正提供服务对象只往上注册,客户端只需从注册服务器上查找远程对象引用,然后调用远程方法,具体由谁提供服务由注册服务器来帮助联络。

还可以用 RMIActivation 编程方式来实现RMI远程方法调用,具体请参考 http://java.sun.com/j2se/1.4.2/docs/guide/rmi/activation.html

把HelloServer和HelloClient中的"//192.168.1.105:1099/Hello 写成rmi:/192.168.1.105:1099/Hello 感觉会好看一些,因为直接感觉就是在处理rmi协议。

6)使用插件进行编译启动

1>RMI-eclipse plugin 插件安装

http://www.genady.net/rmi/v20/downloads.html

(该插件可试用,不是免费的。。。。。。不过网上有破解版的)



2>服务端代码一样,只是编译使用工具进行生成:

点击项目工程右键->RMI--Enable Stubs Generation 进行生成代码

如果修改代码后,点击项目工程右键-<RMI-Re-generate RMI Stubs就可以了

在运行之前需要进行一下设置,否则,会提示错误信息,说找不到stub类。
      (1)启动RMIRegistry
      Eclipse菜单window->show view->others,在弹出菜单中选择RMI Views->RMI RegistryInspector,这是会多出来一个窗口,这里可以显示已经注册的RMI应用。
点击工具条上的RMI Plugin图标,在菜单中选择Start Local Registry。再运行-->cmd--->cd 进入你项目生成class文件所在目录注意这个只需要启动一次,除非你把它关掉。
(2)配置运行RMI服务
右键点击左边树中的RMI_Server.java文件,菜单Debug As ->RMI Application。在弹出对话框中找到RMI Properties标签页
这时这里前两项显示红色。
选中java.security.police项的value框,点击选择按钮会出现文件选择对话框,我们这里设置成C:\Java \jre1.5.0_05\lib\security\java.security。就是jre的安全策略配置文件,要选择成泥当前用的jdk的侧略文件。
选中第二项java.rmi.server.codebase的 value项,这里选择编译后类包所在的文件夹。点击选择按钮->add按钮->pick from workspace,选择当前工程的bin文件夹。
点击apply按钮。
点击debug按钮。
(3)单击RmiServer选择Run as--RMI Application
(4)单击RmiClient选择Run as--Java Application

参考资料:

http://www.hudong.com/wiki/RMI

http://damies.javaeye.com/blog/51778

http://hubin.javaeye.com/blog/150492

http://rf.cepark.com/index.php/action-viewnews-itemid-381


IT家园
IT家园

网友最新评论 (0)

发表我的评论
取消评论
表情