RMI深入探究
前面我们学习了RMI的一些基本知识,本节我们将深入理解RMI应用的基本架构并在此基础上构建自己的RMI应用程序。
RMI 应用架构
前面的文章,主要介绍了RMI框架的基本知识,本节我们将继续深入探讨RMI应用的架构及开发步骤,并深入理解这种架构的应用场景及优缺点。那就从下面这副图开始吧!下面这幅图是本人按照网络资料整理绘制的。这是我个人认为的RMI应用架构图:
根据上面这幅图,个人觉得有以下几点需要注意:
- Rmiregistry.exe应用主要完成远程对象的管理,即新增、删除、修改等
- Server服务端主要完成远程对象的注册
- Client服务主要从Rmiregistry.exe服务端查询远程对象,并利用其调用Server服务端的相关服务
- Rmiregistry.exe和Server代表的服务端程序可以部署在同一台服务器上(通常都会从安全角度出发,并考虑将这两者部署到同一台服务器上)。当然在最极端情况下也可以将Client、Rmiregistry.exe和Server部署到一台机器上
RMI 开发常用 API
根据前面整理的资料,我们知道RMI是Java版的RPC实现,所以Java提供了相关API以帮助开发者快速构建自己的小型分布式应用。常见的API有:
- Remote:这是一个接口,所有要发布的远程对象要继承这个接口
- UnicastRemoteObject:所有要发布的远程对象实现类都要继承这个类,继承这个类的本质是要调用这个类的静态方法export(Remote obj, int port)。如果远程对象实现类因为已经继承了某个类造成无法继承UnicastRemoteObject类,那么我们也可以通过在远程对象实现类的构造方法中直接调用UnicastRemoteObject类的export(Remote obj, int port)。这里有一点要注意:远程对象实现类的构造方法要抛出RemoteException异常
- Registry:这也是一个接口,其主要作用是完成远程对象的发布、删除、查询等操作。我们可以通过下述代码获取Registry对象:
Registry registry = LocateRegistry.createRegistry(1099);
当然除了用Registry完成远程对象的发布、删除、查询操作,我们还可以使用另外两个类完成同样的操作:1) java.rmi.Naming;2) javax.naming.Context(后面我们会展示这些类的具体用法)
构建自己的 RMI 应用
了解了RMI应用的基本架构,又学习了开发RMI应用的相关API,接下来我们就可以构建自己的RMI应用了。开发RMI应用主要分为以下几个步骤:
- 创建远程接口,继承RMI的关键接口java.rmi.Remote
- 创建远程类,实现远程接口
- 创建服务器程序,负责向RMI注册器中注册远程对象
- 创建客户程序,负责定位远程对象,并调用远程对象的方法
接下来我们将按照这个步骤创建自己的RMI应用。
1 创建远程接口
这里我们直接贴出代码,如下所示:
public interface RMIService extends Remote {
int add(int a, int b) throws RemoteException;
}
注意:
- 继承Remote接口是关键,并且其中的远程方法要抛出RemoteException异常
- 我们也可以先定义接口A(其中要指定相关的远程方法),接着定义继承接口A的接口B,此时这个接口B就是远程接口,而A不是远程接口
2 创建远程类
public class RMIServiceImpl extends UnicastRemoteObject implements RMIService {
public RMIServiceImpl() throws RemoteException {
}
@Override
public int add(int a, int b) throws RemoteException {
return a + b;
}
}
注意:
- 红色标注的是关键地方
- 如果远程类因为已经继承了某个类,从而导致无法继续继承UnicastRemoteObject类,我们可以在远程类的构造方法中调用UnicastRemoteObject类的exportObject(Remote obj, int port)方法(该方法的主要作用是将当前对象导出为远程对象),但一定要注意该构造方法一定要抛出RemoteException异常或其父类
3 创建服务器程序
public class SimpleServer {
public static void main(String[] args) {
try {
// 创建远程对象
RMIService obj = new RMIServiceImpl();
// 创建注册服务,同时指定其监听端口
Registry registry = LocateRegistry.createRegistry(1099);
// 调用Registry的bind方法向Registry服务中发布远程服务,并将其绑定到某个远程服务名称上,比如:RMIService
registry.bind("RMIService", obj);
System.out.println("RMIService 已注册");
} catch (Exception e) {
System.out.println("RMIServiceImpl 发生异常:" + e.getMessage());
e.printStackTrace();
}
}
}
注意:
- 红色标注的是关键地方
- 正如前面所讲,除了Registry这种发布服务的方式外,还可以通过java.rmi.Naming和javax.naming.Context来完成这个操作
4 创建客户程序
public class SimpleClient {
public static void main(String[] args) {
try {
// 获取远程对象
Registry registry = LocateRegistry.getRegistry("127.0.0.1", 1099);
RMIService obj = (RMIService) registry.lookup("RMIService");
// 调用远程方法
int result = obj.add(10, 20);
System.out.println("远程调用结果:" + result);
} catch (Exception e) {
System.out.println("RemoteClient发生异常:" + e.getMessage());
e.printStackTrace();
}
}
}
关于RMI应用程序的几点思考
通过上一节,我们创建了一个属于自己的简单的RMI服务,但是这个服务有一些缺陷存在,比如:
- Rmiregistry.exe和Server代表的服务端程序部署于同一台服务器,那遇到服务其宕机,所以依赖于这个Rmiregistry的客户端将出现瘫痪,会对客户带来不好的体验
- 即使Rmiregistry与Server分开部署,服务器宕机,客户端就无法调用的情况依旧存在,因为Rmiregistry无法支持高可用部署(这种说法或许不对,但确实会有这种情况)