【Java基础】- RMI原理和使用详解
文章目录
- 【Java基础】- RMI原理和使用详解
- 一、什么RMI
- 二、RMI原理
- 2.1 工作原理图
- 2.2 工作原理
- 三、RMI远程调用步骤
- 3.1 RMI远程调用运行流程图
- 3.2 RMI 远程调用步骤
- 四、JAVA RMI简单实现
- 4.1 如何实现一个RMI程序
- 4.2 JAVA实现RMI程序
一、什么RMI
RMI:远程方法调佣(Remort Method Invocation),它支持存储于不同地址空间的程序级对象之间彼此通信,实现远程对象之间的无缝远程调用。
JAVA RMI:用于不同虚拟机之间的通信,这些虚拟机可以在不同的主机上,也可以在同一个主机上;一个虚拟机中的对象调用另一个虚拟机上中的对象的方法,只不过是允许被远程调用的对象要通过一些标志加以标识。
远程过程调用(Remote Procedure Call,RPC):可以用于一个进程调用另一个进程(很可能在另一个远程主机上)中的过程,从而提供了过程的分布能力。Java的RMI则在RPC的基础上向前又迈进了一步,既提供分布式对象间通讯。
二、RMI原理
2.1 工作原理图
2.2 工作原理
方法调用从客户对象经占位程序(Stub)、远程引用层(Remote Reference Layer)和传输层(Transport Layer)向下,传递给主机,然后再次经传输层,向上穿过远程调用层和骨干网(Skeleton),到达服务器对象。 占位程序扮演着远程服务器对象的代理的角色,使该对象可被客户激活。 远程引用层处理语义、管理单一或多重对象的通信,决定调用是应发往一个服务器还是多个。传输层管理实际的连接,并且追踪可以接受方法调用的远程对象。服务器端的骨干网完成对服务器对象实际的方法调用,并获取返回值。返回值向下经远程引用层、服务器端的传输层传递回客户端,再向上经传输层和远程调用层返回。最后,占位程序获得返回值。
实际上,客户端只与代表远程主机中对象的Stub对象进行通信,丝毫不知道Server的存在。客户端只是调用Stub对象中的本地方法,Stub对象是一个本地对象,它实现了远程对象向外暴露的接口,也就是说它的方法和远程对象暴露的方法的签名是相同的。客户端认为它是调用远程对象的方法,实际上是调用Stub对象中方法,可以理解为Stub对象是远程对象在本地的一个代理,当客户端调用方法的时候,Stub对象会将调用通过网络传输给远程对象。
三、RMI远程调用步骤
3.1 RMI远程调用运行流程图
3.2 RMI 远程调用步骤
- 客户端对象调用客户端辅助对象(Stub)上的方法。
- 客户端辅助对象打包调用信息(变量、方法名),通过网络发送给服务端辅助对象。
- 服务端辅助对象将客户端辅助对象发送来的信息解包,找出真正被调用的方法以及该方法所在对象。
- 调用真正服务对象上的真正方法,并将结果返回给服务端辅助对象。
- 服务端辅助对象将结果打包,发送给客户端辅助对象。
- 客户端辅助对象将返回值解包,返回给客户对象
- 客户对象获得返回值
四、JAVA RMI简单实现
4.1 如何实现一个RMI程序
1). 创建远程接口, 并且继承java.rmi.Remote接口。
2). 实现远程接口,并且继承:UnicastRemoteObject。
3). 创建服务器程序: createRegistry方法注册远程对象,暴露一个监听。
4). 创建客户端程序,通过ip和端口连接到指定的服务器,并且将数据做封装(序列化)
5). 服务器端收到请求,先反序列化。再进行业务逻辑处理。把返回结果序列化返回
4.2 JAVA实现RMI程序
1). 定义一个远程接口
/**
* 必须继承Remote接口。
* 所有参数和返回类型必须序列化(因为要网络传输)。
* 任意远程对象都必须实现此接口。
* 只有远程接口中指定的方法可以被调用。
*/
public interface IRemoteMath extends Remote {
// 所有方法必须抛出RemoteException
public double add(double a, double b) throws RemoteException;
public double subtract(double a, double b) throws RemoteException;
}
2). 远程接口实现类
/**
* 服务器端实现远程接口。
* 必须继承UnicastRemoteObject,以允许JVM创建远程的存根/代理。
*/
public class RemoteMath extends UnicastRemoteObject implements IRemoteMath {
private int numberOfComputations;
protected RemoteMath() throws RemoteException {
numberOfComputations = 0;
}
@Override
public double add(double a, double b) throws RemoteException {
numberOfComputations++;
System.out.println("Number of computations performed so far = "
+ numberOfComputations);
return (a+b);
}
@Override
public double subtract(double a, double b) throws RemoteException {
numberOfComputations++;
System.out.println("Number of computations performed so far = "
+ numberOfComputations);
return (a-b);
}
}
3). 服务器端
/* 注册远程对象,向客户端提供远程对象服务
* 远程对象是在远程服务上创建的,你无法确切地知道远程服务器上的对象的名称
* 但是,将远程对象注册到RMI Service之后,客户端就可以通过RMI Service请求
* 到该远程服务对象的stub了,利用stub代理就可以访问远程服务对象了
*/
public class RMIServer {
public static void main(String[] args) {
try {
IRemoteMath remoteMath = new RemoteMath();
LocateRegistry.createRegistry(1088);
Registry registry = LocateRegistry.getRegistry();
registry.bind("Compute", remoteMath);
System.out.println("Math server ready");
} catch (Exception e) {
e.printStackTrace();
}
}
}
4). 客户端
public class MathClient {
public static void main(String[] args) {
try {
// 如果RMI Registry就在本地机器上,URL就是:rmi://localhost:1088/Compute
// 否则,URL就是:rmi://RMIService_IP:1088/Compute
Registry registry = LocateRegistry.getRegistry("localhost");
// 从Registry中检索远程对象的存根/代理
IRemoteMath remoteMath = (IRemoteMath)registry.lookup("Compute");
// 调用远程对象的方法
double addResult = remoteMath.add(5.0, 3.0);
System.out.println("5.0 + 3.0 = " + addResult);
double subResult = remoteMath.subtract(5.0, 3.0);
System.out.println("5.0 - 3.0 = " + subResult);
}catch(Exception e) {
e.printStackTrace();
}
}
}