RMI(即Remote Method Invoke 远程方法调用)。在Java中,只要一个类extends了java.rmi.Remote接口,即可成为存在于服务器端的远程对象,供客户端访问并提供一定的服务。JavaDoc描述:Remote 接口用于标识其方法可以从非本地虚拟机上调用的接口。任何远程对象都必须直接或间接实现此接口。只有在“远程接口”(扩展 java.rmi.Remote 的接口)中指定的这些方法才可远程使用。
注意:extends了Remote接口的类或者其他接口中的方法若是声明抛出了RemoteException异常,则表明该方法可被客户端远程访问调用。
同时,远程对象必须实现java.rmi.server.UniCastRemoteObject类,这样才能保证客户端访问获得远程对象时,该远程对象将会把自身的一个拷贝以Socket的形式传输给客户端,此时客户端所获得的这个拷贝称为“存根”,而服务器端本身已存在的远程对象则称之为“骨架”。其实此时的存根是客户端的一个代理,用于与服务器端的通信,而骨架也可认为是服务器端的一个代理,用于接收客户端的请求之后调用远程方法来响应客户端的请求。
RMI 框架的基本原理大概如下图,应用了代理模式来封装了本地存根与真实的远程对象进行通信的细节:
下面给出一个简单的RMI 应用,其中类图如下:其中IService接口用于声明服务器端必须提供的服务(即service()方法),ServiceImpl类是具体的服务实现类,而Server类是最终负责注册服务器远程对象,以便在服务器端存在骨架代理对象来对客户端的请求提供处理和响应:
1 理论知识
在J2EE软件开发中,通常会对系统模块进行分层。展示层主要负责数据的展示,定义数据库的UI组织模式;业务逻辑层负责具体的业务逻辑处理;持久层通常指数据库以及相关操作。在一个大型系统中,这些层次很有可能被分离,并部署在不同的服务器上。而在两个层次之间,可能通过远程过程调用RMI等方式进行通信。如图2.15所示,展示层组件作为RMI的客户端,通过中间的业务逻辑层取得一个订单(Order)的信息。假设一个订单由客户名、商品名和数量构成,那么一次交互过程可能由图2.15所描述的这样,RMI的客户端会与服务端进行3次交互,依次取得这些信息。
基于以上模式的通信方式是一种可行的解决方案,但是它存在两个严重的问题:
(1)对于获取一个订单对象而言,这个操作模式略显繁琐,且不具备较好的可维护性。
(2)前后累计进行了3次客户端与服务器的通信,性能成本较高。
为了解决这两个问题,就可以使用Value Object模式。Value Object模式提倡将一个对象的各个属性进行封装,将封装后的对象在网络中传递,从而使系统拥有更好的交互模型,并且减少网络通信数据,从而提高系统性能。使用Value Object模式对以上结构进行改良,定义对象Order,由Order对象维护客户名、商品名和数量等信息,而Order对象也就是Value Object,它必须是一个可串行化的对象。将Value Object模式应用到本例中,便可以得到如图2.16所示的结构。
在基于Value Object模式的结构中,为了获得一份订单信息,只需要进行一次网络通信,缩短了数据存取的响应时间,减少了网络数据流量。
注意:使用Value Object模式可以有效减少网络交互次数,提高远程调用方法的性能,也能使系统接口具有更好的可维护性。
2 代码实现
(1)RMI服务端接口,即业务逻辑层:(必须继承Remote接口)
import java.rmi.Remote;
import java.rmi.RemoteException;
public interface IOrderManager extends Remote{
Order getOrder(int id) throws RemoteException;
String getProductName(int id) throws RemoteException;
double getPrice(int id) throws RemoteException;
String getBrand(int id) throws RemoteException;
}
(2)RMI服务器端接口的实现:(必须继承类UnicastRemoteObject)
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
public class OrderManager extends UnicastRemoteObject implements IOrderManager {
private static final long serialVersionUID = 1L;
protected OrderManager() throws RemoteException {
super();
// TODO Auto-generated constructor stub
}
@Override
public Order getOrder(int id) throws RemoteException {
// TODO Auto-generated method stub
Order order=new Order();
order.setPrice(112.3);
order.setProductName("AIR-MAX");
return order;
}
@Override
public String getProductName(int id) throws RemoteException {
// TODO Auto-generated method stub
return "AIR-MAX";
}
@Override
public double getPrice(int id) throws RemoteException {
// TODO Auto-generated method stub
return 112.3;
}
@Override
public String getBrand(int id) throws RemoteException {
// TODO Auto-generated method stub
return "NIKE";
}
}
(3)Value Object:(RMI传输的对象)
import java.io.Serializable;
public class Order implements Serializable {
private static final long serialVersionUID = -3485578276309961802L;
private int id;
private String productName;
private double price;
private String brand;
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
public String getProductName() {
return productName;
}
public void setProductName(String productName) {
this.productName = productName;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
}
(4)业务逻辑层注册,并开启RMI服务器:
import java.rmi.Naming;
import java.rmi.registry.LocateRegistry;
public class OrderServer {
public static void main(String[] args) {
try {
LocateRegistry.createRegistry(1099); //注册RMI端口
IOrderManager manager=new OrderManager(); //RMI远程业务对象
Naming.rebind("OrderManager", manager); //绑定RMI对象
System.out.println("orderManager is ready");
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
(5)客户端代码:
import java.net.MalformedURLException;
import java.rmi.Naming;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;
public class OrderClient {
public static void main(String[] args) {
try {
IOrderManager manager=(IOrderManager)Naming.lookup("OrderManager");
long begin=System.currentTimeMillis();
for(int i=0;i<1000;i++){
manager.getOrder(i);
}
System.out.println("耗时:"+(System.currentTimeMillis()-begin));
begin=System.currentTimeMillis();
for(int i=0;i<1000;i++){
manager.getPrice(i);
manager.getProductName(i);
manager.getBrand(i);
}
System.out.println("耗时:"+(System.currentTimeMillis()-begin));
} catch (MalformedURLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (RemoteException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (NotBoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
运行结果:
耗时:110
耗时:140
注意:运行顺序,先运行Server端,注册RMI端口,绑定RMI对象,开启服务器;然后,运行client端。
由运行结果可以看出,对传输的数据进行有效的封装,可以明显提升RMI的性能。
3 参考文献
<1> Java程序性能优化. .葛一鸣等著