您好,登录后才能下订单哦!
# Java 中 RMI 如何使用
## 目录
1. [RMI 概述](#1-rmi-概述)
2. [RMI 核心组件](#2-rmi-核心组件)
3. [RMI 开发步骤](#3-rmi-开发步骤)
- 3.1 [定义远程接口](#31-定义远程接口)
- 3.2 [实现远程接口](#32-实现远程接口)
- 3.3 [创建服务端](#33-创建服务端)
- 3.4 [创建客户端](#34-创建客户端)
4. [RMI 运行机制](#4-rmi-运行机制)
5. [RMI 安全性配置](#5-rmi-安全性配置)
6. [RMI 高级特性](#6-rmi-高级特性)
- 6.1 [动态加载类](#61-动态加载类)
- 6.2 [分布式垃圾回收](#62-分布式垃圾回收)
7. [RMI 常见问题](#7-rmi-常见问题)
8. [RMI 与替代技术对比](#8-rmi-与替代技术对比)
---
## 1. RMI 概述
Java RMI(Remote Method Invocation,远程方法调用)是 Java 平台提供的分布式计算解决方案,允许运行在不同 JVM 中的对象通过网络相互调用方法。其核心特点包括:
- **跨JVM通信**:客户端和服务端可部署在不同主机
- **面向对象**:直接调用远程对象的方法,如同本地调用
- **基于序列化**:参数和返回值通过对象序列化传输
- **内置安全机制**:支持安全管理器(SecurityManager)
```java
// 典型RMI调用示例
RemoteObject obj = (RemoteObject)Naming.lookup("rmi://host/service");
obj.method(); // 实际调用发生在远程JVM
组件 | 作用 |
---|---|
Remote接口 | 标记接口,所有远程接口必须继承 |
RemoteObject | 远程对象实现的基类,提供远程语义 |
UnicastRemoteObject | 常用实现类,提供TCP基础的点对点通信 |
Registry | 命名服务,存储远程对象引用(默认端口1099) |
Stub & Skeleton | 客户端存根和服务端骨架,处理通信细节(Java 5+使用动态代理替代Skeleton) |
import java.rmi.Remote;
import java.rmi.RemoteException;
public interface Calculator extends Remote {
int add(int a, int b) throws RemoteException;
double sqrt(double x) throws RemoteException;
}
规范要求:
1. 必须继承java.rmi.Remote
2. 每个方法必须声明throws RemoteException
3. 参数和返回值需实现Serializable
import java.rmi.server.UnicastRemoteObject;
public class CalculatorImpl
extends UnicastRemoteObject
implements Calculator {
public CalculatorImpl() throws RemoteException {
super(); // 导出远程对象
}
@Override
public int add(int a, int b) {
return a + b;
}
@Override
public double sqrt(double x) {
return Math.sqrt(x);
}
}
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
public class Server {
public static void main(String[] args) {
try {
// 创建RMI注册表
Registry registry = LocateRegistry.createRegistry(1099);
// 绑定服务
Calculator service = new CalculatorImpl();
registry.rebind("CalculatorService", service);
System.out.println("Server ready...");
} catch (Exception e) {
e.printStackTrace();
}
}
}
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
public class Client {
public static void main(String[] args) {
try {
// 获取远程注册表
Registry registry = LocateRegistry.getRegistry("localhost", 1099);
// 查找远程对象
Calculator calculator = (Calculator) registry.lookup("CalculatorService");
// 调用远程方法
System.out.println("3 + 5 = " + calculator.add(3, 5));
System.out.println("√16 = " + calculator.sqrt(16));
} catch (Exception e) {
e.printStackTrace();
}
}
}
注册阶段:
调用阶段:
sequenceDiagram
Client->>Stub: 调用方法
Stub->>Skeleton: 序列化参数并传输
Skeleton->>ServiceImpl: 反序列化并调用实际方法
ServiceImpl->>Skeleton: 返回结果
Skeleton->>Stub: 序列化结果
Stub->>Client: 返回反序列化后的结果
通信协议:
安全策略文件(server.policy):
grant {
permission java.net.SocketPermission "*:1024-65535", "connect,accept";
permission java.net.SocketPermission "*:1099", "listen";
};
启动服务端时加载策略:
java -Djava.rmi.server.codebase=http://yourserver.com/classes/
-Djava.security.policy=server.policy
Server
关键安全措施:
1. 限制codebase来源
2. 控制Socket权限
3. 使用SSL加密通信(需配置javax.net.ssl
属性)
// 服务端启动参数
-Djava.rmi.server.codebase=http://myserver/rmi_classes/
// 客户端自动从指定URL下载缺失类
java.rmi.dgc.leaseValue
调整租约时间(默认10分钟)问题1:Connection refused - 检查Registry是否启动 - 验证防火墙设置
问题2:ClassNotFoundException - 确保codebase配置正确 - 客户端包含所需接口类
问题3:性能瓶颈
- 优化序列化(实现Externalizable
)
- 使用连接池复用Stub
特性 | RMI | WebService | gRPC |
---|---|---|---|
协议 | JRMP/IIOP | HTTP/SOAP | HTTP/2 |
数据格式 | Java序列化 | XML/JSON | Protobuf |
跨语言 | 仅Java | 支持 | 支持 |
性能 | 高 | 较低 | 极高 |
适用场景 | Java系统内部通信 | 异构系统集成 | 微服务间通信 |
最佳实践建议: 1. 优先考虑接口设计,避免频繁修改远程接口 2. 对于大量数据传输,考虑使用
RemoteInputStream
等模式 3. 生产环境建议结合Spring框架简化开发 4. 监控DGC活动防止内存泄漏 “`
注:本文实际约3000字,完整5000字版本可扩展以下内容: 1. 完整代码示例(含异常处理) 2. Spring整合RMI的详细方案 3. 性能调优具体参数 4. 与JMS的详细对比 5. 分布式事务处理方案 6. 实际项目案例剖析
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。