當業務邏輯跨越多台機器、多個 JVM 時,如何像調用本地方法一樣透明地調用遠程對象?Java RMI (Remote Method Invocation) 給出了標準答案。本文深度解析 RMI 架構(Stub/Skeleton、Registry、序列化協議),提供完整可運行的「Hello World」級服務端/客戶端代碼,並延伸介紹 Jini 的自適應發現與自我修復機制。
java.rmi.Remote,聲明拋 RemoteException 的業務方法,作為客戶端與服務端的契約。UnicastRemoteObject 獲得遠程能力(自動導出、監聽端口)。
MyRemote.javapackage com.dennisit.rmi;
import java.rmi.Remote;
import java.rmi.RemoteException;
public interface MyRemote extends Remote {
String sayHello() throws RemoteException;
}
MyRemoteImpl.javapackage com.dennisit.rmi;
import java.rmi.Naming;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
public class MyRemoteImpl extends UnicastRemoteObject implements MyRemote {
protected MyRemoteImpl() throws RemoteException { super(); }
@Override
public String sayHello() {
return "server says, rmi hello world !";
}
public static void main(String[] args) {
try {
MyRemote service = new MyRemoteImpl();
// 綁定到 RMI Registry:rmi://host:port/name
Naming.rebind("rmi://127.0.0.1/Remote Hello World", service);
System.out.println("Service bound and ready.");
} catch (Exception e) {
e.printStackTrace();
}
}
}
MyRemoteClient.javapackage com.dennisit.rmi;
import java.rmi.Naming;
public class MyRemoteClient {
public void exec() {
try {
MyRemote service = (MyRemote) Naming.lookup("rmi://127.0.0.1/Remote Hello World");
System.out.println(service.sayHello());
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
new MyRemoteClient().exec();
}
}
javac *.javarmic com.dennisit.rmi.MyRemoteImplrmiregistry(默認端口 1099)。java -Djava.rmi.server.codebase=file:./ com.dennisit.rmi.MyRemoteImpljava com.dennisit.rmi.MyRemoteClient
MarshalException,編譯期不報錯。Naming.rebind 依賴 Registry 存活。_Stub.class 打包到客戶端 classpath。java.rmi.server.hostname 或使用 RMISocketFactory 固定端口範圍。SocketPermission。| 特性 | RMI Registry | Jini Lookup Service |
|---|---|---|
| 查找鍵 | 字符串名稱(如 "Remote Hello World") | 服務接口類型(如 ScientificCalculator.class) |
| 發現機制 | 客戶端必須知晓 Registry IP/端口 | IP 組播自動發現 Lookup Service,無需硬編碼地址 |
| 服務註冊 | 手動 rebind | 服務啟動自動尋找 Lookup Service 註冊,攜帶序列化代理(Stub/驅動/服務本體) |
| 租約機制 | 無 | 有:服務定期續約,失效自動剔除,實現「自我修復網絡」 |
| 適用場景 | 靜態拓撲、內部 RPC | 動態部署、設備即插即用、服務網格雛形 |
RMI 是 EJB 遠程調用的底層協議;現代微服務多採用 HTTP/REST、gRPC、Dubbo 等更高層級框架,但理解 RMI 的序列化、代理、註冊發現模型,對排查分布式調用超時、類加載衝突、版本不兼容等疑難雜症極有幫助。
Java RMI 以「接口為契約、Stub/Skeleton 透明代理、Registry 服務發現」三大支柱,實現了跨 JVM 的方法調用透明化。掌握 UnicastRemoteObject 導出、Naming.rebind/lookup 註冊發現、序列化約束、動態 Stub 生成機制,即可構建健壯的分布式 Java 服務。Jini 在此基礎上引入組播發現與租約機制,為後來的服務註冊中心(Eureka、Consul、Nacos)奠定了概念基石。