Java RMI(Remote Method Invocation)是分布式 Java 應用的基石,核心原則:**遠程介面定義契約,本地實現履行契約,Stub/Skeleton 透明代理網絡通信**。完整 RMI 系統包含七大組件:
Remote)UnicastRemoteObject)rmic 生成)Calculator.javapackage rmi.demo;
import java.rmi.Remote;
import java.rmi.RemoteException;
public interface Calculator extends Remote {
long add(long a, long b) throws RemoteException;
long sub(long a, long b) throws RemoteException;
long mul(long a, long b) throws RemoteException;
long div(long a, long b) throws RemoteException;
}
鐵律:介面必須 extends Remote;所有方法必須拋出 RemoteException。
CalculatorImpl.javapackage rmi.demo;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
public class CalculatorImpl extends UnicastRemoteObject implements Calculator {
protected CalculatorImpl() throws RemoteException { super(); }
public long add(long a, long b) { return a + b; }
public long sub(long a, long b) { return a - b; }
public long mul(long a, long b) { return a * b; }
public long div(long a, long b) { return a / b; }
}
關鍵點:繼承 UnicastRemoteObject 自動啟用 RMI 遠程能力;必須提供拋出 RemoteException 的構造器並調用 super()。
> rmic rmi.demo.CalculatorImpl
// 生成 CalculatorImpl_Stub.class (客戶端代理)
// JDK 1.2+ 無需 Skeleton,直接由 Stub 與遠程對象通信
CalculatorServer.javapackage rmi.demo;
import java.rmi.Naming;
import java.rmi.RemoteException;
public class CalculatorServer {
public static void main(String[] args) {
try {
Calculator calc = new CalculatorImpl();
// 綁定至 RMI Registry:rmi://host:port/name
Naming.rebind("rmi://localhost:1099/CalculatorService", calc);
System.out.println("CalculatorService 已就緒...");
} catch (Exception e) {
e.printStackTrace();
}
}
}
CalculatorClient.javapackage rmi.demo;
import java.rmi.Naming;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;
import java.net.MalformedURLException;
public class CalculatorClient {
public static void main(String[] args) {
try {
Calculator calc = (Calculator) Naming.lookup("rmi://localhost:1099/CalculatorService");
System.out.println("4 - 3 = " + calc.sub(4, 3));
System.out.println("4 + 5 = " + calc.add(4, 5));
System.out.println("3 * 6 = " + calc.mul(3, 6));
System.out.println("9 / 3 = " + calc.div(9, 3));
} catch (MalformedURLException | RemoteException | NotBoundException e) {
e.printStackTrace();
}
}
}
| 終端 | 命令 | 關鍵點 |
|---|---|---|
| 1. Registry | rmiregistry | 需在類文件根目錄執行,確保 Stub 類可被動態下載 |
| 2. Server | java -Djava.security.policy=policy.txt rmi.demo.CalculatorServer | 需安全策略文件授權 permission java.security.AllPermission |
| 3. Client | java -Djava.security.policy=policy.txt rmi.demo.CalculatorClient | 需遠程介面類與 Stub 類 |
RMI 透過 annotateClass 在序列化流中嵌入類 URL(HTTP/FTP/file),客戶端/服務端自動下載缺失類。生產環境建議部署專用 HTTP 類服務器,避免 codebase 配置錯誤導致 ClassNotFoundException。
// policy.txt - 僅示範,生產嚴禁 AllPermission
grant {
permission java.security.AllPermission;
};
實際部署應限制 SocketPermission、PropertyPermission、RuntimePermission 至最小集合。
ClassNotFoundException: CalculatorImpl_Stub → 確保 rmic 生成的 Stub 類在客戶端 classpath 或可經 codebase 下載。ConnectException: Connection refused → 檢查 Registry 是否啟動、端口 1099 是否被佔用、防火牆規則。UnmarshalException: Class not found → 服務端缺少客戶端傳遞的可序列化類,需同步部署共享類庫。serialVersionUID 显式聲明。RMI 是 Java 早期分布式標準,但存在序列化效能低、防火牆穿透難、僅限 Java 生態等短板。現代架構多遷移至:
理解 RMI 原理有助於掌握「代理模式、序列化、命名服務、安全沙箱」等分布式核心概念,是進階微服務架構的基石。