Java RMI中codebase是什么?相关使用场景与配置疑问
1. codebase机制是否用于在服务端创建非分布式对象?和不用codebase的分布式对象场景有什么区别?
codebase的核心作用是让JVM可以从远程路径加载本地classpath不存在的类,不止支持服务端加载客户端的类,也支持客户端加载服务端的类,和是否创建非分布式对象没有直接绑定关系。
你之前使用分布式对象时没用到codebase,本质是你用到的所有类(包括远程接口、参数/返回值的实现类)在服务端和客户端的本地classpath都已提前存在,RMI会优先调用本地classpath的类,不会触发远程加载逻辑。只有当服务端需要处理客户端传过来的、服务端本地没有的实现类实例时,才需要用codebase从客户端指定的远程路径加载对应类,这种场景下加载的类既可以是普通非分布式对象的类,也可以是分布式对象的实现类。
2. 为什么不直接把类放在服务端classpath,而是用codebase?适用场景是什么?
直接把类放到服务端classpath仅适合类固定、不会频繁变动的场景,codebase的核心价值是支持动态类加载,不需要每次类更新就重新部署所有服务端节点。
它和接口确实有直接关联:服务端必须提前把对应类的公共接口放在本地classpath中,codebase仅需要存放接口的实现类即可,服务端可以用接口引用加载到的实现类实例,不需要提前知晓具体实现逻辑。
典型适用场景:
- 分布式计算框架:服务端是通用任务调度节点,客户端可以提交自定义的任务实现类,服务端不需要提前存储所有可能的任务实现类,通过codebase动态加载即可执行
- 多客户端独立开发场景:不同客户端有自己的专属自定义实现,不需要统一同步到服务端做全量部署
3. 为什么对应的类同时存在于客户端的classpath和codebase中?
客户端本身需要实例化这个类的对象,所以必须在本地classpath有这个类才能完成实例创建、序列化操作,之后才能把对象传给服务端。而codebase里的类是专门给服务端加载用的,两边的类必须是同一份,避免序列化/反序列化时出现类版本不一致的问题。
简单逻辑梳理:客户端用本地classpath的类创建并序列化对象,把对象传给服务端的同时会携带codebase地址,服务端发现本地没有这个类,就去对应codebase路径加载同一份类完成反序列化操作。
4. codebase的实际形态是什么?有没有可复刻的示例?
codebase本质就是一个可访问的类资源路径,常见的形态有三种:
- 本地文件路径:比如
file:/opt/rmi_classes/,适合服务端和客户端在同一台机器的测试场景 - HTTP服务器路径:比如
http://192.168.1.100:8080/rmi_classes/,是生产环境最常用的形态,把打包好的jar或者class文件放在HTTP服务器的静态资源目录即可 - FTP路径:用法和HTTP类似,目前使用频次较低
最简可复刻测试步骤:
- 先定义公共接口
Task,打包成common.jar,同时放到服务端、客户端的classpath里
public interface Task extends Serializable { Object execute(); }
- 客户端写实现类
CalculateTask,编译后把class文件或者打包的client-impl.jar放到本地HTTP服务的静态目录(可以直接用Python起简易测试服务:python -m http.server 8000,把jar放在服务启动目录下即可) - 客户端启动时添加JVM参数指定codebase地址:
-Djava.rmi.server.codebase=http://客户端IP:8000/client-impl.jar,同时开启权限配置:-Djava.security.policy=policy.all,policy文件配置所有权限即可 - 客户端创建
CalculateTask实例,调用远程服务的方法把实例传给服务端 - 服务端接收参数时发现本地没有
CalculateTask的实现类,会自动从客户端带过来的codebase地址下载对应的类加载执行
内容的提问来源于stack exchange,提问作者Samuel Carva

