Java RMI (Remote Method Invocation) 是 Java 原生的分布式物件調用機制,通過 Stub/Skeleton 代理實現跨 JVM 透明方法呼叫。雖然微服務時代多用 REST/gRPC,但 RMI 仍在傳統 Java EE 系統、內部服務調用、JMX 管理中廣泛使用。本文提供最精簡的三類別完整範例,涵蓋介面定義、服務端啟動、客戶端調用、rmic 編譯、rmiregistry 註冊表啟動全流程。
Client JVM Server JVM
+----------------+ +----------------+
| Client Code | | Impl Object |
| (Stub) ─────→| JRMP/RMI | (Skeleton) |
+----------------+ Protocol +----------------+
↑ ↑
Naming.lookup() Naming.rebind()
↑ ↑
+----------------+ +----------------+
| RMI Registry |◀─────| rmiregistry |
| (Port 1099) | | (獨立進程) |
+----------------+ +----------------+
// HelloIn.java
import java.rmi.Remote;
import java.rmi.RemoteException;
public interface HelloIn extends Remote {
String sayHello() throws RemoteException;
}
// HelloImpl.java
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
import java.rmi.Naming;
public class HelloImpl extends UnicastRemoteObject implements HelloIn {
protected HelloImpl() throws RemoteException {
super(); // 匯出遠程物件,監聽匿名埠
}
@Override
public String sayHello() throws RemoteException {
return "Hello, RMI World! " + new java.util.Date();
}
public static void main(String[] args) {
// System.setSecurityManager(new SecurityManager()); // JDK 8+ 通常不需
try {
HelloImpl obj = new HelloImpl();
// 綁定到註冊表: rmi://host:port/name
Naming.rebind("rmi://localhost:1099/HelloService", obj);
System.out.println("RMI Server ready... (bound as HelloService)");
} catch (Exception e) {
e.printStackTrace();
}
}
}
// HelloClient.java
import java.rmi.Naming;
public class HelloClient {
public static void main(String[] args) {
try {
// 從註冊表查找遠程介面代理
HelloIn service = (HelloIn) Naming.lookup("rmi://localhost:1099/HelloService");
for (int i = 0; i < 5; i++) {
System.out.println("Call " + i + ": " + service.sayHello());
Thread.sleep(1000);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
javac HelloIn.java HelloImpl.java HelloClient.javarmic HelloImplHelloImpl_Stub.class (客戶端代理) 與 HelloImpl_Skel.class (伺服端骨架,JDK 1.2+ 不再使用但仍生成)。類路徑必須包含遠程介面。rmiregistryrmiregistry 的 classpath 不需 包含應用類別 (JDK 1.2+ 支援動態類下載),但若使用自定義類載入器需注意 java.rmi.server.codebase。java HelloImplRMI Server ready...java HelloClient| 錯誤現象 | 原因 | 解決 |
|---|---|---|
ConnectException: Connection refused | rmiregistry 未啟動或端口錯誤 | 先啟動 rmiregistry,確認端口 1099 未被佔用 |
ClassNotFoundException: HelloImpl_Stub | 未執行 rmic 或 classpath 缺少 Stub | 執行 rmic HelloImpl,確保 Stub 與介面同目錄 |
UnmarshalException: Class not found | 客戶端缺少遠程介面類別 | 客戶端 classpath 包含 HelloIn.class |
AccessException: Registry.Registry.rebind disallowed | SecurityManager 禁止遠程綁定 | 啟動參數 -Djava.security.policy=server.policy,策略檔授權 permission java.net.SocketPermission "*", "accept,connect,listen,resolve" |
| 跨機器調用失敗 | IP/主機名解析、防火牆、codebase | 服務端啟動加 -Djava.rmi.server.hostname=實際IP;防火牆開放 1099 + 匿名埠範圍;若用動態下載需配置 java.rmi.server.codebase=https://host:port/classes/ |
codebase URL 下載,需 HTTP 伺服器托管 .class 檔。RMIClientSocketFactory/RMIServerSocketFactory 使用 SSLSocket。rmid (Activation Daemon) 支援服務按需啟動、持久化狀態。JMXConnectorServer 底層即 RMI,service:jmx:rmi:///jndi/rmi://host:port/jmxrmi。RMI 以「介面導向、Stub 代理、註冊表發現」三大支柱,提供了純 Java 的分布式調用能力。掌握 rmic、rmiregistry、Naming 三大工具鏈與常見網路/安全配置坑,即可快速建立可用的 RMI 服務。現代架構下,RMI 更多作為內部管理平面 (JMX, Spring RMI Exporter) 或舊系統整合橋樑,新業務開發優先考慮 REST/gRPC/Dubbo 等跨語言、可觀測性更強的框架。