RMI,远程方法调用(Remote Method Invocation)是Enterprise JavaBeans的支柱,是建立分布式Java应用程序的方便途径。RMI是非常容易使用的,但是它非常的强大。
RMI的基础是接口,RMI构架基于一个重要的原理:定义接口和定义接口的具体实现是分开的。下面我们通过具体的例子,建立一个简单的远程计算服务和使用它的客户程序,你可以在适当的地方使用RMI来实现分布式服务。
RMI主要包括几个部分:
远程服务的接口定义
● 远程服务接口的具体实现
● 桩(Stub)和框架(Skeleton)文件
● 一个运行远程服务的服务器
● 一个RMI命名服务,它允许客户端去发现这个远程服务
● 类文件的提供者(一个HTTP或者FTP服务器)
● 一个需要这个远程服务的客户端程序
大白话解释一下就是:有一个特殊的服务器端和一个特殊的客户端;为什么特殊呢?因为这个服务器提供服务的方法必须实现Romote接口,而且必须提供stub和skeleton文件,还不许有一个命名服务;客户端要实现客户端的接口;
下面我们手动实现RMI,建立一个简单的分布式服务的例子
第一步:服务器上程序接口
Calculator.cs
import java.rmi.Remote;
/**
* @author 小峰 远程服务接口
*/
public interface Calculator extends Remote {
public int add(int a, int b) throws java.rmi.RemoteException;
}
第二步:服务器上接口的实现
CalculatorImpl.cs文件
import java.io.Serializable;
import java.rmi.RemoteException;
/**
* @author 小峰 接口的实现
*/
@SuppressWarnings("serial")
public class CalculatorImpl implements Calculator, Serializable {
/**
* 实现接口方法,两个整数相加
*/
@Override
public int add(int a, int b) throws RemoteException {
return a + b;
}
}
第三步:生成桩(Stubs)和框架(Skeletons)
D:\JAVAProject\hiberante\WebContent\WEB-INF\classes>D:\JDK6\bin\rmic CalculatorI
mpl
现在就可以使用了
第四步:编写服务端程序
RomoteServer.cs
import java.rmi.Naming;
/**
* @author 邓小峰
*
*/
public class RomoteServer {
public RomoteServer() {
try {
Calculator c = new CalculatorImpl();
Naming.rebind("rmi://localhost:1099/CalculatorService", c);
} catch (Exception e) {
System.out.println("Trouble: " + e);
}
}
/**
* @param args
*/
public static void main(String[] args) {
new RomoteServer();
}
}
第五步:客户端程序
RemoteClient.cs
import java.net.MalformedURLException;
import java.rmi.Naming;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;
public class RemoteClient {
/**
* @param args
* @throws NotBoundException
* @throws RemoteException
* @throws MalformedURLException
*/
public static void main(String[] args) throws MalformedURLException,
RemoteException, NotBoundException {
try {
Calculator c = (Calculator) Naming
.lookup("rmi://localhost/CalculatorService");
System.out.println(c.add(4, 5));
} catch (Exception ex) {
System.out.println(ex);
}
}
}
第六步:注册,不管是服务端程序还是服务端程序,要想启动都需要先注册
D:\JAVAProject\hiberante\WebContent\WEB-INF\classes>D:\jdk6\bin\rmiregistry
第七步:启动服务器程序
D:\JAVAProject\hiberante\WebContent\WEB-INF\classes>java RomoteServer
第八步:启动客户端程序
D:\JAVAProject\hiberante\WebContent\WEB-INF\classes>D:\jdk6\bin\java RemoteClient
呵呵,是不是很看到运算结果了?
RMI的深入点介绍
一:工作原理
RMI系统结构,在客户端和服务器端都有几层结构。
--------- ---------
| 客户对象| | 远程对象|
--------- ---------
| | | |
------------------------------ ---------------------------------
| 占位程序 Stub | | 骨干网 Skeleton |
------------------------------- ------- ------------------------
| | | |
-----------------------------------------------------------------
| 远 程 引 用 层 Remote Reference Layer |
------------------------------------ ----------------------------
| |
------------------------------------
| 传 输 层 Transport Layer |
------------------------------------
方法调用从客户对象经占位程序(Stub)、远程引用层(Remote Reference
Layer)和传输层(Transport Layer)向下,传递给主机,然后再次经传
输层,向上穿过远程调用层和骨干网(Skeleton),到达服务器对象。
占位程序扮演着远程服务器对象的代理的角色,使该对象可被客户激活。
远程引用层处理语义、管理单一或多重对象的通信,决定调用是应发往一个
服务器还是多个。传输层管理实际的连接,并且追追踪可以接受方法调用的
远程对象。服务器端的骨干网完成对服务器对象实际的方法调用,并获取返
回值。返回值向下经远程引用层、服务器端的传输层传递回客户端,再向上
经传输层和远程调用层返回。最后,占位程序获得返回值。
(以上为引用他人原话)
二:rmi相关类
rmi由5个包和3个应用工具组成:
java.rmi 组织客户端的rmi类,接口和异常
java.rmi.sever 组织服务器端的rmi类,接口和异常
java.rmi.registry 组织用于管理rmi命名服务的类
java.rmi.dgc 组织用于管理分布式垃圾收集的类
java.rmi.activation 组织用于实现按需缉获的rmi服务的类
rmic 编译器,生成stub和sketon
rmiregistry 一个为rmi提供命名服务的服务器,这项服务把名字和对象关联在一起
rmid 一个支持rmi激活框架的服务器
但是大多数情况下,我们只需要使用每个包的一部分方法和接口就可以成功的实现一个使用rmi分布式解决方案
实际应用的思考
如果我们要是使用RMI,当然不会是这样笨拙的使用方式,肯定得有RMI的容器,让我们轻易地编程和注册、客户端轻易调用。什么容器提供RMI服务器端的开发、注册和调用呢?我们可以轻而易举地把上面的代码服务器端程序改成一个web程序,客户端程序也是一样,关键是容器要能够自动register就ok了。
下面列举一个Tomcat中注册RMI的例子
我们知道我们可以在tomcat启动的时候预先加载一些类,我们就可以把绑定的过程(也就是我们的服务器代码写成在servlet中加载),所以例子如下
web.xml添加
RMIServlet
RMIServlet
RMIServlet
0
RMIServlet.cs如下
import java.io.IOException;
import java.rmi.AccessException;
import java.rmi.AlreadyBoundException;
import java.rmi.NoSuchObjectException;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.UnicastRemoteObject;
import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import rmi.Calculator;
import rmi.CalculatorImpl;
/**
* Servlet implementation class RMIServlet
*/
public class RMIServlet extends HttpServlet {
/**
*
*/
private static final long serialVersionUID = -1942511496585743905L;
/**
* @see HttpServlet#HttpServlet()
*/
public RMIServlet() {
super();
}
/**
* @see Servlet#init(ServletConfig)
*/
public void init(ServletConfig config) throws ServletException {
Registry registry = null;
try {
registry = LocateRegistry.createRegistry(1099);
} catch (RemoteException e1) {
}
Calculator c = new CalculatorImpl();
try {
registry.bind("CalculatorService1", c);
} catch (AccessException e) {
e.printStackTrace();
} catch (RemoteException e) {
e.printStackTrace();
} catch (AlreadyBoundException e) {
e.printStackTrace();
}
}
/**
* @see HttpServlet#service(HttpServletRequest request, HttpServletResponse
* response)
*/
protected void service(final HttpServletRequest request,
final HttpServletResponse response) throws ServletException,
IOException {
}
public void destroy() {
Registry registry = null;
try {
registry = LocateRegistry.getRegistry(1099);
} catch (RemoteException e1) {
}
try {
UnicastRemoteObject.unexportObject(registry, false);
} catch (final NoSuchObjectException e) {
}
try {
registry.unbind("CalculatorService1");
} catch (final AccessException e) {
} catch (final RemoteException e) {
} catch (final NotBoundException e) {
}
}
}
然后
D:\jdk6\bin\rmiregistry
重新启动Tomcat试试,就ok了,现在已经是自动注册和绑定了