您的位置:首页 > 编程语言 > Java开发

Java RMI详解

2016-02-02 22:13 387 查看

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.java

import 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);
}
}




RMI远程调用步骤:

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

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

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

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

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

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

7,客户对象获得返回值

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

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

创建远程方法接口,该接口必须继承自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以说明该方法是有风险的。

创建远程方法接口实现类:

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;
}
}


利用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文件

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

方式一:后台启动rmiregistry服务

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


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

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

LocateRegistry.createRegistry(12312);


编写服务端代码

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();
}
}
}


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

运行服务端:

类class的目录结构

Test

server

Hello.class

HelloImpl.class

HelloServer.class

[path ]$ java server.HelloServer

HelloServer启动成功

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

编写客户端代码

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("对象未绑定");
}
}
}


运行客户端

目录结构

Test

client

HelloClient.class

server

Hello.class

HelloImpl_Stub.class//服务端生成的存根文件

[client]$ java client.HelloClient

Hello,zx

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



PS:

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

java RMI的缺点:

1. 从代码中也可以看到,代码依赖于ip与端口

2. RMI依赖于Java远程消息交换协议JRMP(Java Remote Messaging Protocol),该协议为java定制,要求服务端与客户端都为java编写。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: