Java RMI 示例
Java RMI(Remote Method Invocation)是一种强大的技术,它允许对象调用运行在另一个 Java 虚拟机(JVM)中的对象的方法。RMI 为创建分布式应用程序提供了一个简单直接的模型,使开发人员能够构建和部署可轻松跨网络(包括互联网)运行的系统。它是 Java SE(标准版)平台的一部分。
尽管 Java RMI 有其优势,但随着网络服务和其他远程通信技术(如 REST API 和 gRPC)的兴起,Java RMI 的使用已经减少。不过,对于某些以 Java 为中心的分布式应用程序来说,RMI 仍然是一个强大的选择。
创建远程服务需要以下几个步骤:
制作远程接口
远程接口定义客户可以远程调用的方法,客户将用它作为服务的类类型。
import java.rmi.*;
public interface MyRemote extends Remote {
String sayHello() throws RemoteException;
}有以下几点注意:
- 声明所有的方法都抛出
RemoteException
每次远程方法调用都必须考虑成“有风险的”。因为这是通过网络和I/O来完成的,网络和I/O是有风险的,会失败!随时随地都可能抛出异常。在每个方法中声明RemoteException,强制客户注意并承认事情可能会出错。 - 确定参量和返回值是属于原语(primitive)类型或
Serializable类型
远程方法的任何参量都必须被打包并通过网络运送,这要靠序列化来完成。返回值也是一样。如果你使用原语类型、字符串,以及许多API中存在的类型(包括数组和集合),不会有问题。如果你传送自己的类型,就必须保证你的类实现了Serializable。
实现远程接口
你的服务必须实现远程接口,带有客户要调用的方法的接口。
import java.rmi.*;
import java.rmi.server.*;
public class MyRemoteImpl extends UnicastRemoteObject implements MyRemote {
protected MyRemoteImpl() throws RemoteException {
}
private static final long serialVersionUID = 1L;
@Override
public String sayHello() {
return "Server says, 'Hey'";
}
public static void main(String[] args) {
try {
MyRemote service = new MyRemoteImpl();
Naming.rebind("RemoteHello", service);
} catch (Exception e) {
e.printStackTrace();
}
}
}为了成为远程服务对象,你的对象需要某些和“远程”有关的功能,最简单的方式是扩展UnicastRemoteObject(来自java.rmi.server包),让这个类(你的超类)做这些工作。
UnicastRemoteObject实现Serializable,所以我们需要serialVersionUID字段
编写一个声明RemoteException的无参量构造器,当类被实例化时,其超类的构造器总是会被调用。如果超类的构造器抛出一个异常,那么你没有选择,你的构造器也要抛出一个异常。
OK,现在有了一个远程服务,必须让它可以被远程客户调用。要做的是实例化它并放进RMI Regstry(RMI Registry 必须正在运行,否则注册会失败)。当注册这个实现对象时,RMI 系统其实注册的是桩(RMI 将客户辅助对象称为stub,也叫桩,服务辅助对象称为 skeleton,也叫骨架),因为这是客户真正需要的。注册服务是用java.rmi.Naming类的静态rebind()方法。
运行 rmiregistry
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
public class StartRmiRegistry {
public static void main(String[] args) {
try {
// Start RMI registry on port 1099
Registry registry = LocateRegistry.createRegistry(1099);
System.out.println("RMI Registry started on port 1099");
// Prevent the application from exiting immediately
Object keepAlive = new Object();
synchronized(keepAlive) {
keepAlive.wait();
}
} catch (Exception e) {
System.err.println("Failed to start RMI registry: " + e.getMessage());
e.printStackTrace();
}
}
}启动服务
运行MyRemoteImpl,将对象在RMIRegistry中注册。
运行客户端
import java.rmi.*;
public class MyRemoteClient {
public static void main(String[] args) {
new MyRemoteClient().go();
}
public void go() {
try {
MyRemote service = (MyRemote) Naming.lookup("rmi://localhost/RemoteHello");
String s = service.sayHello();
System.out.println(s);
} catch (Exception e) {
e.printStackTrace();
}
}
}MyRemote service = (MyRemote) Naming.lookup("rmi://localhost/RemoteHello");
- 客户总是使用远程接口作为服务类型,事实上,客户不需要知道远程服务的实际类名。
lookup()方法返回Object类型,必须把它转成接口。RemoteHello,这必须是服务注册时用的名称。rmi://localhost/,服务运行的主机名或者IP地址
评论已关闭