RMI(远程方法调用)

一.RMI原理及介绍

1.基本介绍

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

Java Remote 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/JNIRMI/JDBC相结合,可帮助您利用RMI与目前使用非Java语言的现有服务器进行通信,而且在您需要时可扩展Java在这些服务器上的使用。RMI可帮助您在扩展使用时充分利用Java的强大功能。

2.基本组成

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

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

3.原理

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

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

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

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

二.RMIspring环境下使用

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

2. 编写接口以及实现类

public interface ICalculator {

public long add(long a, long b);

public long sub(long a, long b);

public long mul(long a, long b);

public long div(long a, long b);

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

public class 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">

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

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

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

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

</bean>

客户端:

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

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

        <property name="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  

  */  

 public interface HelloInterface extends Remote   

 {   

    /**  

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

    */  

    public String say() throws RemoteException;   

 }   

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

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

java 代码

package com.unmi;   

import java.rmi.*;   

import java.rmi.server.*;   

/** 

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

 */  

public class 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;   

public class HelloServer   

{   

   /** 

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

    */  

   public static void main(String[] argv)   

   {   

      try  

      {   

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

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

         //这里用这种方式避免了再打开一个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("Hello Server is ready.");   

      }   

      catch (Exception e)   

      {   

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

      }   

   }   

}  

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

java 代码

package com.unmi;   

import java.rmi.Naming;   

public class HelloClient   

{   

   /** 

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

    */  

   public static void main(String[] argv){   

      try{   

    HelloInterface hello = (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("HelloClient exception: " + e);   

      }   

   }   

}   

5)执行程序:启动服务HelloServer;运行客户端HelloClient进行调用
代码如何编译这里就不细讲
(1)打开一个Dos窗口执行命令 java com.unmi.HelloServer 启动服务HelloServer
E:workspaceTestRMIbin>java com.unmi.HelloServer
Hello Server is ready.
运行成功则可以看到 Hello Server is ready
(2)打开另一个Dos窗口执行命令 java com.unmi.HelloClient 运行客户端程序
E:workspaceTestRMIbin>java com.unmi.HelloClient
Hello, world!
调用成功则可以看到 Hello, world!
并且在启动服务端的窗口中看到紧跟 Hello Server 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实例
  HelloInterface hello = 

(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.11099端口上查找Hello命令对象,如果第二条语句写成lookup("192.168.1.105/Hello")与原语句是 同等的,因为默认端口号就是1099
代码中 HelloServer 和 HelloClient 省略了设置安全管理器的过程 System.setSecurityManager(new RMISecurityManager()); ,如果设置的安全管理则必须编写相应的访问策略文件,并且在执行时指定参数
无论是启动服务端还是客户端都可以用参数 

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

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

HelloServerHelloClient中的 "//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 Registry Inspector,这是会多出来一个窗口,这里可以显示已经注册的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

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值