远程方法调用(RMI)

1. RMI简介

RMI用于不同虚拟机之间的通信,这些虚拟机可以在不同的主机上、也可以在同一个主机上,这里的通信可以理解为一个虚拟机上的对象调用另一个虚拟机上对象的方法。RMI要解决的是:是让客户端对远程方法的调用可以相当于对本地方法的调用而屏蔽其中关于远程通信的内容,即使在远程上,也和在本地上是一样的。

2.RMI通信模型:

在这里插入图片描述
1、客户端调用辅助对象stub上方法

2、stub对调用信息(变量、方法)打包,网络发给服务端辅助对象

3、(1.2前)skeleton将stub送来的信息解包,找到被调方法的对象及本身

4、调用服务端对象上的方法,将结果返回给skeleton、打包、给stub

5、stub解包,返给客户端对象,获取返回值

可以把stub理解为本地的一个代理对象,客户端不知道server的存在,

注意事项:

1、子接口中方法必须抛出java.rmi.RemoteException异常(使用RMI时可能抛出的大多数异常的父类)

2、子接口的实现类直接、间接继承java.rmi.server.UnicastRemoteObject(提供了很多支持RMI的方法,这些方法可以通过JRMP协议导出一个远程对象的引用并动态代理构建可以和远程对象交互的stub对象)

数据传递问题:
分布式系统、不同内存空间,虚拟机A对象的引用对于虚拟机B没有意义

解决方案:引用传递更改为值传递

将对象序列化为字节,使用字节副本在客户端、服务器间传递,一个虚拟机对该值的修改不影响其他主机的数据,问题:对象嵌套引用造成序列化嵌套,数据量激增.

能不能被序列化要满足下面任一条件:
1、java基本类型,2、实现Serializable接口,3、容器类中的对象可以序列化,容器也可以序列化,4、子类可序列化,其可序列化

当远程主机调用本地主机方法时,通过本地主机查询引用对应的对象;对象共享、一变都收影响

RMI参数传递和结果返回的三种机制:

1、简单类型:按值传递、传递数据拷贝;
2、(实现了Remote接口的)远程对象引用、以远程对象的引用传递;
3、(未实现Remote接口)远程对象引用,按值传递,通过序列化传递副本

3. 实现流程

1. 声明远程接口

import java.rmi.Remote;
import java.rmi.RemoteException;
/**
*** 远程服务对象接口必须继承Remote接口;同时方法必须抛出RemoteExceptino异常**
*/
public interface Hello extends Remote {
	public String sayHello(User user) throws RemoteException;	
}

其中有一个引用对象作为参数:

import java.io.Serializable;
/**
* 引用对象应该是可序列化对象,这样才能在远程调用的时候:1. 序列化对象 2. 拷贝 3. 在网络中传输
* 4. 服务端反序列化 5. 获取参数进行方法调用; 这种方式其实是将远程对象引用传递的方式转化为值传递的方式
*/
public class User implements Serializable {

/**
 * 
 */
private static final long serialVersionUID = 2258201344880196063L;

private String name;
private int age;
public String getName() {
	return name;
}
public void setName(String name) {
	this.name = name;
}
public int getAge() {
	return age;
}
public void setAge(int age) {
	this.age = age;
}
}	

2. 实现远程服务对象

import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
/**
* 远程服务对象实现类写在服务端;必须继承UnicastRemoteObject或其子类
**/
public class HelloImpl extends UnicastRemoteObject implements 	Hello {

/**
 * 
 */
private static final long serialVersionUID = 3638546195897885959L;

protected HelloImpl() throws RemoteException {
	super();
	// TODO Auto-generated constructor stub
}

@Override
public String sayHello(User user) throws RemoteException {
	System.out.println("this is server, hello:" + user.getName());
	return "hello";
}
}

3. 服务端程序

import java.net.MalformedURLException;
import java.rmi.Naming;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
/**
* 服务端程序
**/
public class Server {

public static void main(String[] args) {
	
	try {
		Hello hello = new HelloImpl(); // 创建一个远程对象,同时也会创建stub对象、skeleton对象
		
			LocateRegistry.createRegistry(8080); //启动注册服务
			try {
				Naming.bind("//127.0.0.1:8080/wbh", hello); //将stub引用绑定到服务地址上
			} catch (MalformedURLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			System.out.println("service bind already!!");
		
	} catch (RemoteException e) {
		// TODO Auto-generated catch block
		e.printStackTrace();
	}
	
}
}

4.客户端程序

import java.net.MalformedURLException;
import java.rmi.Naming;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;
/**
* 客户端程序
 * @author wbh
 *
 */
public class Client {
public static void main(String[] args) {
	try {
		Hello hello = (Hello) Naming.lookup("//127.0.0.1:8080/wbh");//获取远程对象
		User user = new User();
		user.setName("james");
		System.out.println(hello.sayHello(user));
	} catch (MalformedURLException e) {
		// TODO Auto-generated catch block
		e.printStackTrace();
	} catch (RemoteException e) {
		// TODO Auto-generated catch block
		e.printStackTrace();
	} catch (NotBoundException e) {
		// TODO Auto-generated catch block
		e.printStackTrace();
	}
}
}

实际应用中客户端程序与服务端程序是分开的,那么客户端要使用远程对象,就必须声明相同的远程接口与一些可序列化的参数对象。但是我们又不想重复写,怎么办?可以通过引用jar包的方式。

在这里插入图片描述

RMI的主要优点如下:

1.面向对象:RMI可将完整的对象作为参数和返回值进行传递,而不仅仅是预定义的数据类型。也就是说,您可以将类似Java哈希表这样的复杂类型作为一个参数进行传递。而在目前的RPC系统中,您只能依靠客户机将此类对象分解成基本数据类型,然后传递这些数据类型,最后在服务器端重新创建哈希表。RMI则不需额外的客户程序代码(将对象分解成基本数据类型),直接跨网传递对象。

2.可移动属性:RMI可将属性(类实现程序)从客户机移动到服务器,或者从服务器移到客户机。例如,您可以定义一个检查雇员开支报告的接口,以便察看雇员是否遵守了公司目前实行的政策。在开支报告创建后,客户机就会从服务器端获得实现该接口的对象。如果政策发生变化,服务器端就会开始返回使用了新政策的该接口的另一个实现程序。您不必在用户系统上安装任何新的软件就能在客户端检查限制条件–从而向用户提供烁快的反馈,并降低服务器的工作量。这样就能具备最大的灵活性,因为政策改变时只需要您编写一个新的Java类,并将其在服务器主机上安装一次即可。

3.设计方式:对象传递功能使您可以在分布式计算中充分利用面向对象技术的强大功能,如二层和三层结构系统。如果您能够传递属性,那么您就可以在您的解决方案中使用面向对象的设计方式。所有面向对象的设计方式无不依靠不同的属性来发挥功能,如果不能传递完整的对象–包括实现和类型–就会失去设计方式上所提供的优点。

4.安 全:RMI使用Java内置的安全机制保证下载执行程序时用户系统的安全。RMI使用专门为保护系统免遭恶意小应用程序侵害而设计的安全管理程序,可保护您的系统和网络免遭潜在的恶意下载程序的破坏。在情况严重时,服务器可拒绝下载任何执行程序。

5.便于编写和使用:RMI使得Java远程服务程序和访问这些服务程序的Java客户程序的编写工作变得轻松、简单。远程接口实际上就是Java接口。服务程序大约用三行指令宣布本身是服务程序,其它方面则与任何其它Java对象类似。这种简单方法便于快速编写完整的分布式对象系统的服务程序,并快速地制做软件的原型和早期版本,以便于进行测试和评估。因为RMI程序编写简单,所以维护也简单。

6.可连接现有/原有的系统:RMI可通过Java的本机方法接口JNI与现有系统进行进行交互。利用RMI和JNI,您就能用Java语言编写客户端程序,还能使用现有的服务器端程序。在使用RMI/JNI与现有服务器连接时,您可以有选择地用Java重新编写服务程序的任何部分,并使新的程序充分发挥Java的功能。类似地,RMI可利用JDBC、在不修改使用数据库的现有非Java源代码的前提下与现有关系数据库进行交互。

7.编写一次,到处运行:RMI是Java“编写一次,到处运行 ”方法的一部分。任何基于RMI的系统均可100%地移植到任何Java虚拟机上,RMI/JDBC系统也不例外。如果使用RMI/JNI与现有系统进行交互工作,则采用JNI编写的代码可与任何Java虚拟机进行编译、运行。

8.分布式垃圾收集:RMI采用其分布式垃圾收集功能收集不再被网络中任何客户程序所引用的远程服务对象。与Java虚拟机内部的垃圾收集类似,分布式垃圾收集功能允许用户根据自己的需要定义服务器对象,并且明确这些对象在不再被客户机引用时会被删除。

9.并行计算:RMI采用多线程处理方法,可使您的服务器利用这些Java线程更好地并行处理客户端的请求。Java分布式计算解决方案:RMI从JDK 1.1开始就是Java平台的核心部分,因此,它存在于任何一台1.1 Java虚拟机中。所有RMI系统均采用相同的公开协议,所以,所有Java 系统均可直接相互对话,而不必事先对协议进行转换。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值