RMI(remote method invoke)
一、基本原理
RMI通过代理来负责客户和远程对象之间通过socket进行通信的细节。
RMI分别为远程对象生成了客户端代理和服务端代理。客户端的叫stub,服务端的叫Skeleton。
Stub进行参数编组,将下列信息发给服务端:
1)要访问的远程对象的名字
2)被调用的方法的描述
3)编组后的参数的字节序列
服务端由skeleton来处理这一信息,进行下列动作
1)反编组参数
2)定位要访问的对象
3)调用远程对象相应的方法
4)对返回值或者异常进行相应的编组
5)将编组后的内容发给客户端。
二、创建一个RMI应用
1、创建远程类接口
1)直接或间接继承Remote接口
2)接口中所有的方法抛出RemoteException异常
2、创建远程类,实现远程接口
1)远程类继承UnicastRemoteObject,
2)可以不继承,直接在构造函数中调用UnicastRemoteObject.exportObject(this,0).
3)远程类的构造函数需要声明抛出RemoteException
4)远程方法必须抛出RemoteException
3、创建服务器程序,
通过 JNDI的javax.naming.Context在rmigegistry注册表中注册远程对象。
几个方法bind,rebind,lookup,unbind
创建远程对象:
HelloService service1 = new HelloServiceImpl("service1");
Context namingContext = new InitialContext();
namingContext.rebind("rmi:HelloService",service1);
4、创建客户端程序。
获取远程对象的存根对象
String url = "rmi://localhost/";
HelloService service1 = (HelloService) namingContext.lookup(url
+ "HelloService1");
三、远程对象设计工厂模式
就是创建一个工厂类和产品类,这个工厂类和产品类都是远程对象,都要实现remote接口,远程方法都要声明RemoteException
1)客户端每次访问一个远程对象的时候,都会得到一个新的stub对象。
2)stub对象重写了equal方法,如果两个stub对象都是对应一个远程对象的代理,则equal返回true。equal不是远程方法。
3)stub对象操作远程方法,会导致远程对象的实现类进行相应的操作。
四、远程方法中的参数与返回值的传递
在服务器端和客户端传递的方法或返回值,必须是远程对象、可序列化对象,或者是基本类型数据,否则在进行远程方法调用的时候会出现UnmarshalException.
五、回调客户端的远程对象
六、远程对象的并发访问
七、分布式垃圾收集
1、RMI框架采用分布式垃圾收集机制。
1)DGC回收规则:一个远程对象不受到任何本地引用和远程引用,这个对象可以被回收。
2)租约通知:通知服务器远程对象被引用了。
3)租约期限:通过java.rmi.dgc.leaseValue来设置。
4)通过实现java.rmi.server.Unreferenced接口,可以来释放相关资源。
5)强制服务端等待客户端获得该远程对象的引用service.isAccessed()
八、远程对象的equals()、hashCode()和clone()方法
stub重写了equal和hashcode方法,没有重写clone
九、使用安全管理器
1、客户端使用安全管理器的两个步骤
1)创建安全策略文件,例如:
grant{
permission java.net.SocketPermission "*:1024-65535","connect";
};
2)为客户端设置安全策略文件和RMISecurityManager
System.setProperty("java.security.policy", SimpleClient.class
.getResource("client.policy").toString());
System.setSecurityManager(new RMISecurityManager());//会从client.policy中读取安全策略
2、通过命令进行设置
java -D java.security.policy=c:\chapter11\client.policy SimpleClient
3、如果服务端需要从客户端动态加载类,也要按照类似方式设置。
十、RMI应用部署以及类的动态加载
java.rmi.server.codebase系统属性,从指定的位置动态加载类文件
十一、远程激活
1、RMI框架提供了一种远程激活机制,当有一个客户去访问远程对象的时候,还去创建这个远程对象。和延迟加载差不多的概念。
rmid:激活系统。远程对象由这个系统来管理生命周期。
2、RMI类介绍
Activatable:可以被激活的远程对象都是这个类的实例,ActivationID表示激活对象的唯一标识符。
ActivationGroup:激活组,可以被激活的对象都放在这里面。
ActivationGroupDesc:描述激活组(属性文件,启动虚拟机的路径)
一个激活组对应一个java虚拟机,rmid程序根据ActivationGroupDesc提供的信息来启动一个java虚拟机,然后在这个虚拟机内创建并管理可以激活的远程对象,每个激活组都有唯一的id,用ActivationGroupID来表示。
ActivationDesc用来描述一个可以激活的远程对象。
ActivationDesc(ActivationGroupID groupID, String className, String location, MarshalledObject data)
3、一个远程对象如果希望被激活,它的远程类应该继承java.rmi.activation.Activatable类。
4、rmid程序激活对象步骤
//1.创建一个激活组描述符ActivationGroupDesc对象
ActivationGroupDesc group = new ActivationGroupDesc(prop, null);
//2.向rmid程序注册ActivationGroup,配置信息由group提供
ActivationGroupID id = ActivationGroup.getSystem().registerGroup(
group);
String classURL = System.getProperty("java.rmi.server.codebase");
MarshalledObject param1 = new MarshalledObject("service1");
MarshalledObject param2 = new MarshalledObject("service2");
//3.创建激活对象描述符ActivationDesc
ActivationDesc desc1 = new ActivationDesc(id,
"activate.HelloServiceImpl", classURL, param1);
ActivationDesc desc2 = new ActivationDesc(id,
"activate.HelloServiceImpl", classURL, param2);
//4.向rmid注册激活对象,配置信息由desc提供
HelloService s1 = (HelloService) Activatable.register(desc1);
HelloService s2 = (HelloService) Activatable.register(desc2);
System.out.println(s1.getClass().getName());
Context namingContext = new InitialContext();
//5.向rmiregistry注册激活对象
namingContext.rebind("rmi:HelloService1", s1);
namingContext.rebind("rmi:HelloService2", s2);