RMI简介
一、简介
RMI (Remote Method Invocation) 允许在一个Java虚拟机中运行的对象调用在另一个Java虚拟机中运行的对象的方法。RMI用于构建分布式应用程序; 它提供了Java程序之间的远程通信。
二、架构及原理
1、RMI程序简介
RMI应用程序通常包含两个独立的程序:一个服务器和一个客户端。
典型的服务器程序创建一些远程对象,使对这些对象的引用可访问,并等待客户端调用这些对象上的方法。
典型的客户端程序获取对服务器上一个或多个远程对象的远程引用,然后调用这些对象上的方法。
RMI提供了服务器和客户端通信和来回传递信息的机制;这样的应用程序有时被称为分布式对象应用程序。
2、RMI注册表
RMI注册表(registry)是一个放置所有服务器对象的命名空间;每次服务器创建一个对象时,它都会向RMI Registry 注册这个对象(使用bind()
或reBind()
方法)。它们使用一个唯一的绑定名称进行注册。
调用远程对象时,客户端需要该远程对象的引用;客户端可以使用远程对象的绑定名称(通过lookup()
方法)从注册表中提取对象。
过程如下:
3、RMI架构图
RMI应用程序架构如下:
上图中名词解释如下:
- Transport Layer
传输层,用于连接客户端和服务器;它管理现有的连接并建立新的连接。
- Stub
Stub是远程对象在客户端上的表示(representation)或代理(proxy),它驻留在客户端系统中,做为客户端程序的网关。
- Skeleton
Skeleton是驻留在服务器端的对象;Stub与Skeleton通信,将请求传递给远程对象。
- RRL(Remote Reference Layer)
远程引用层,管理客户端对远程对象引用的层。
4、RMI工作原理
-
当客户端对远程对象进行调用时,Stub接收到这个请求,并最终将这个请求传递给RRL。
-
当客户端RRL接收到请求时,它调用远程对象引用的invoke()方法,将请求传递给服务器端的RRL。
-
服务器端的RRL将请求传递给Skeleton(服务器上的代理) ,Skeleton最终调用服务器上对应的对象。
-
执行结果会一直传递回客户端。
三、RMI程序开发
1、开发步骤
编写RMI Java应用程序的步骤大致如下:
-
定义远程接口
-
开发接口的实现类(远程对象)
-
开发服务器程序
-
开发客户端程序
-
编译&执行
2、具体实现
- Message类
import java.io.Serializable;
public class MyMessage implements Serializable{
private static final long serialVersionUID = -6205209644018307258L;
private String title;
private String content;
public MyMessage() {}
public MyMessage(String title, String content) {
super();
this.title = title;
this.content = content;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
}
- 远程接口
新增HelloWorld接口,扩展自Remote接口,
import java.rmi.Remote;
import java.rmi.RemoteException;
public interface HelloWorld extends Remote{
public String sendMessage(MyMessage message) throws RemoteException;
}
- 实现类
import java.rmi.RemoteException;
public class HelloWorldImpl implements HelloWorld{
@Override
public String sendMessage(MyMessage message) throws RemoteException {
String msg = String.format("Title: %s\nContent: %s", message.getTitle(), message.getContent());
System.out.println(msg);
return "copy that";
}
}
- 服务器
在服务器程序中创建一个远程对象并将其绑定到RMI注册表:
import java.rmi.AlreadyBoundException;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.UnicastRemoteObject;
public class MyServer {
public static void main(String[] args) {
try {
//实例化实现类
HelloWorld hw = new HelloWorldImpl();
//暴露实现类的对象
HelloWorld stub = (HelloWorld) UnicastRemoteObject.exportObject(hw, 0);
Registry registry = LocateRegistry.getRegistry(5555);
//在注册表中绑定远程对象
registry.bind("HelloWorld", stub);
System.out.println("Server ready.");
} catch (RemoteException e) {
e.printStackTrace();
} catch (AlreadyBoundException e) {
e.printStackTrace();
}
}
}
- 客户端
客户端程序中获取远程对象并调用其方法:
import java.rmi.NotBoundException;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
public class MyClient {
public static void main(String[] args) {
try {
//获取注册表
Registry registry = LocateRegistry.getRegistry(5555);
//在注册表中查找远程对象
HelloWorld hw = (HelloWorld) registry.lookup("HelloWorld");
//调用远程对象方法
MyMessage message = new MyMessage("Hi", "I Miss You So Much");
String result = hw.sendMessage(message);
System.out.println(String.format("Call succeeded, the result: %s", result));
} catch (RemoteException e) {
e.printStackTrace();
} catch (NotBoundException e) {
e.printStackTrace();
}
}
}
3、运行
- 编译所有Java文件
javac *.java
- 启动rmi注册表
rmiregistry [port]
rmiregistry 5555
- 运行服务器
java MyServer
- 运行客户端
java MyClient