HEAD FIRST JAVA 18 分布式计算----远程部署的RMI
--------------------------------------------------
要点: 在某堆上的对象无法进行另外堆上的对象引用
Java Remote Method Invocation(RMI)让你感觉上像是调用远程对象的方法.
当客户端调用远程对象的方法时,鞋袜是调用代理上的方法,称为stub.
stub是个处理低层网络细节的辅助性对象,它会把方法的调用包装起来送到服务器上
要创建远程服务的话,你就必须要以远程接口来启动'
远程接口必须要extend过java.rmi.Remote这个接口,且所有的方法都必须声明RemoteException.
你的远程服务会实现远程接口
远程服务应该要继承UnicasRemoteObject(技术上也有其他方法,但这最简单)
远程服务必须要声明RemoteException的构造函数(因为父类的构造函数声明了)
远程服务的对象必须要向RMI registry注册
使用静态的Namin.rebind()来注册远程服务
RMI registry必须在同一台机器上与远程服务一块执行,且必须在对象的注册之前启动
客户端会以Naming.lookup()查询远程服务
几乎所有与RMI有关的都会抛出RemoteException(由编译器检查)
调用方法的过程:
1.客户端对象对辅助设施对象调用doBigThing()
2.客户端辅助设施把调用信息打包通过网络送到服务器的辅助设施
3.服务端的辅助设施解开来自客户端辅助设施的信息,并以此调用真正的服务
在RMI中,客户端的辅助设施称为stub,而服务器端的辅助设施称为skeleton
别忘记客户端是使用接口来调用stub上的方法,客户端的Java虚拟机必须要有stub类,但客户端不会在程序代码中引用到stub类,客户端总是通过接口来操作真正的远程对象.
服务器上必须要有stub和skeleton,以及服务与远程的接口,它会需要stub类是因为stub会被代换成连接在RMI registry上真正的服务.
-------------------------------------------
程序代码 :
创建远程服务:
1.创建Remote接口
import java.rmi.* //Remote在java.rmi中
public interface MyRemote extends Remote{
//声明所有的方法都会抛出RemoteException
//返回值会通过网络传送,所以必须是Serializable.参数和返回值都是这样打包 //传送的
public String sayHello() throws RemoteException;
}
2.实现Remote
public class MyRemoteImp1 extends UnicastRemoteObject implements MyRemote{
public String sayHello(){
return "Server says,'Hey'";
}
//more code in class
}
//声明RemoteException的无参数构造函数
public MyRemoteImp1() throws RemoteException{}
向RMI registry注册服务
try{
MyRemote service=new MyRemoteImp1();
//帮服务命名(客户端会靠名字查询registry),并向RMI registry注册,RMI会将 //stub做交换并把stub加入registry
Naming.rebind("Remote Hello",service);
}catch(Exception ex) { ...}
3.用rmic产生stub与skeleton
4.启动RMI registry(rmiregistry)
5.启动远程服务
----
完整的程序代码
Remote interface:
import java.rmi.*;
public interface MyRemote extends Remote{
public String sayHello() throws RemoteException;
}
Remote service(实现):
import java.rmi.*;
import java.rmi.server.*; //UnicastRemoteObject是在这个包中
public class MyRemoteImp1 extends UnicastRemoteObject implements MyRemote{
public String sayHello(){
return"Server says,'Hey'";
}
public MyRemoteImp1() throws RemoteException{ }
public static void main(String[] args){
try{
MyRemote service=new MyRemoteImp1();
Naming.rebind("Remote Hello",service);
}catch(Exception ex) {ex.printStackTrace();}
}
}
--------------
客户端如何取得stub对象
MyRemote service=(MyRemote) Naming.lookup("rmi://127.0.0.1/Remote Hello");
//必须要转换成接口的类型因为查询结果会是Object类型
用户如何取得stub的类
完整的客户端程序代码:
import java.rmi.*;
public class MyRemoteClient{
public static void main(String[] args){
new MyRemoteClient().go();
}
public void go(){
try{
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();}
}
}
----------------
使用RMI时程序员最常犯的三个错误:
1.忘记在启动远程服务前启动rmiregistry(使用Naming.rebind()注册服务前rmiregistry必须启动)
2.忘记把参数和返回类型做成可序列化(编译器不会检测到,执行时才会发现)
3.忘记将stub类交给客户端