远程部署的RMI
远程程序调用技术(Remote Metond Invocation )
当客户端调用远程对象的方法时,其实是调用代理商的方法,此代理被称为stub
客户端对象看起来像是在调用远程的方法,但实际上它只是在调用本地处理Socket和串流细节的“”“代理”
Java RMI提供客户端和服务器端的辅助设施对象。
使用RMI时,必须要决定协议:
JRMP:RMI原生的协议,它是为了Java对Java间的远程调用而设计的
IIOP:为了CORBZ(Common Object Request Broker Architecture)而产生的,能够调用Java对象或其他类型的远程方法。
在RMI中,客户端的辅助设施称为stub,而服务器端的辅助设施成为skeleton
stub是个处理低层网络细节的辅助性对象,它会把方法的调用包装起来送到服务器上。
创建远程服务
1.创建Remote接口(Server)
远程的接口定义了客户端可以远程调用的方法。它是个作为服务的多态化类。stub和服务都会实现此接口
2.实现Remote(Server)
真正执行的类。实现出定义在改接口上的方法。它是客户端会调用的对象。
3.用rmic产生stub与skeleton
客户端和服务器都有helper,你无需创建这些类或产生这些类的源代码,这都会在你执行JDK所附的rmic工具时自动地处理掉
4.启动RMI Registry
用户从此处取得代理(客户端的stub/helper对象)
5.启动远程服务
实现服务的类会起始服务的实例并想RMI registry注册。
1.创建远程接口
a.继承java.rmi.Remote
public interface MyRemote extends Remote{
b.声明所有的方法都会抛出RemoteException
import java.rmi.*;
pulbic interface MyRemote extends Remote{
public String sayHello() throws RemoteException;
}
c.确定参数和返回值都是primitive主数据类型或Serializable
2.实现远程接口
a.实现Remote这个接口
public class MyRemoteImpl extends UnicastRemoteObject implements MyRemote{
pulbic String sayHello(){
return "Server says,'Hey'";
}
//more code
}
b.继承UnicastRemoteObject
c.编写声明RemoteException的无参构造函数
public MyRemoteImpl() throws RemoteException{ }
d.向RMI registry注册服务
try {
MyRemote service = new MyRemoteImpl();
Naming.rebind("Remote Hello",service);
} catch(Exception ex) {...}
3.产生stub和skeleton
a.对实现出的类执行rmic
伴随JavaSdk而来的rmic工具会以服务的实现产生两个新类:stub和skeleton
%rmic MyRemoteImpl
4.执行rmiregistry
a.调出命令行来启动rmiregistry
&rmiregistry
5.启动服务
a.调用另一个命令行来启动服务
%java MyRemoteImpl
完整的代码
Remote Interface
import java.rmi.*;
public interface MyRemote extends Remote {
//每个远程调用都会被认为是有风险的,throws RemoteException
//远程方法的参数和返回这必须是primitive或Serializable的。
public String sayHello() throws RemoteException;
}
import java.rmi.*;
import java.rmi.server.*;
public class MyRemoteImpl extends UnicastRemoteObject implements MyRemote {//创建远程对象最简单的方式:继承UnicastRemoteObject
public String sayHello(){
return "Server says, 'Hey";
}
//父类的构造函数声明了异常,所以你必须写出构造函数,
//因为它代表你的构造函数会调用有风险的程序代码
public MyRemoteImpl() throws RemoteException{}
public static void main(String[] args){
try{
MyRemote service = new MyRemoteImpl();//创建出远程对象,
Naming.rebind("Remote hello", service);//然后用静态的Naming.rebind()来产生关联,所注册的名称会供客户端查询
} catch(Exception ex){
ex.printStackTrace();
}
}
}
客户端如何取得stub对象?
使用RMI registry
MyRemote service = (MyRemote) Naming.lookup("rmi://127.0.0.1/Remote Hello");
用户如何取得stub类?
最简单的情况:直接把类交给用户
更酷的方式:使用动态类下载(dynamic class downloading):stub对象会被加上注明RMI可以去哪里找到该对象的类围巾的URL标记。之后再解序列化的过程中,RMI会在本机上找不到类,所以就使用HTTP的Get来从该URL取得类文件。
完整的客户端程序代码
import java.rmi.*;
public class MyRemoteClient {
public void go(){
try{
//MyRemote:客户端必须要使用与服务相同的类型,事实上 客户端不需要知道服务实际上的类名称
//(MyRemote):必须转化成接口的类型,因为查询结果会是object类型
//lookup()是个静态方法
//127.0.0.1:主机名称或者ip地址
//Remote Hello:必须要跟注册的名称一样
MyRemote service = (MyRemote) Naming.lookup("rmi://127.0.0.1/Remote Hello");
String s = service.sayHello();
System.out.println(s);
}catch(Exception ex){
ex.printStackTrace();
}
}
public static void main(String[] args){
new MyRemoteClient().go();
}
}
使用RMI时常犯的错误
1.忘记在启动远程服务前启动rmiregistry(使用Naming.rebind()注册服务前rmiregistry必须启动)
2.忘记把参数和返回类型做成可序列化
3.忘记将stub类交给客户端
servlet
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class MyServletA extends HttpServlet {
//一般的servlet会继承HttpServlet
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException ,IOException {
//告诉服务器和浏览器相应结果的形式
response.setContentType("text/html");
//此对象会给我们可写回结果的输出串流
PrintWriter out = response.getWriter();
String message = "If you're reading this ,it worked1";
//我们的print对象是个网页
out.println("<HTML><BODY>");
out.println("<H1>" + message + "</H1>");
out.println("</BODY></HTML>");
out.close();
}
}
连接到servlet的HTML网页
<HTML>
<BODY>
<a href="servlets/MyServletA">This is an amazing servlet.</a>
</BODY>
</HTML>
JSP(Java Server Pages):实际上Web服务器最终会把JSP转换成servlet,但差别在于你所写出的是JSP。servlet让你写出带有HTML输出的类,而JSP让你写出带有JAva程序的网页。这样你就能在编写一般HTML网页的时候还能够同时掌握动态内容的能力,内嵌的程序代码可以于执行时处理。