设计模式(11)代理模式The Proxy Pattern - 1 - 远程代理rmi

    技术2024-07-12  80

    代理模式

    假设现在需要设置一个监视器Monitor,能够监视糖贩卖机GumballMachine的状态,位置信息、还有多少糖果等。一个简单的想法就是创建一个Monitor的类,构造参数包括GumballMachine。然后就可以调用GumballMachine的一些状态函数。

    但是如果GumballMachine在另一台机器上、或者在不同的Java堆上,显然是不能够将GumballMachine的引用传递过来。

    这是后就需要使用代理模式来解决。

    思路:添加一个新的类叫做MonitorProxy。Monitor和MonitorProxy在一个Java堆上,MonitorProxy这个代理负责与远程的GumballMachine进行沟通,得到数据后把自己伪装成GumballMachine来被MonitorProxy调用。

    远程方法

    原理

    我们要这设计这样一个系统:该系统允许我们调用本地的一个对象,本地的这个对象又能够将请求转发到远程对象上。

    四个关键的角色 client 、client helper、service helper、Service

    客户端

    client helper的作用是让client调用和调用远程remoter效果一样。client helper处理转发了client的请求。

    换句话说,从client的角度来看,client就是在调用一个远程的服务。client helper假装自己是service

    client helper有和service一样的方法。但是他们的逻辑不同,client helper里面的方法是对service进行响应请求、然后进行参数转换返回给client的。

    服务器端

    同客户端一样,存在一个Service helper负责处理对话(可能是Socket)

    Java RMI(Remote Method Invocation)

    Java RMI已经封装好了相关功能。不需要自己动手写网络和I/O相关的代码了。和调用本地的对象一样。唯一的区别时会有网络通讯,可能引起失败抛出异常。

    client helper 叫做RMI Stub

    service helper 叫做RMI Skeleton

    使用RMI

    Remote service

    一共五步走:

    1. 写一个Remote 接口

    这个远程接口所定义的方法是client远程调用的。这也就意味着stub和实际的service都要实现这个接口

    //1. 扩展java.rmi.Remote接口 public interface MyRemote extends Remote{ //2. 接口中所有的函数都要能够抛出RemoteException异常 //因为涉及到网络或者I/O,可能出现连接错误等。 public String sayHello() throws RemoteException; //3. 要保证参数和返回值都是基本的数据类型或者是可序列化的。 }

    2. 实现Remote接口

    远程的class要实现上一步定义的接口

    // 1. 注意MyRemoteImpl所继承的类UnicastRemoteObject, 这个类定义了一些远程服务所需要的功能函数。 public class MyRemoteImpl extends UnicastRemoteObject implements MyRemote{ // 2. 抛出这个异常的原因是它的父类可能UnicastRemoteObject会抛出这个异常,所以子类也一样。 public MyRemoteImpl() throws RemoteException{ } public String sayHello() { return "server says, hi"; // other impl } // 主函数入口。方便起见直接写在了这个类 public static void main (String[] args) { /*方法一*/ // 需要运行第四步命令 try { MyRemote service = new MyRemoteImpl(); Naming.rebind(“RemoteHello”, service); //在RMI中注册 } catch(Exception ex) { ex.printStackTrace(); } } /*方法二*/ //使用内在注册器,不用单独运行rmiregistry服务。 try { //默认端口1099 Registry registry = LocateRegistry.createRegistry(1099); MyRemoteImpl service = new MyRemoteImpl(); registry.bind("RemoteHello", service); // 在RMI中注册 System.out.println("Run Successfully"); } catch (Exception ex) { ex.printStackTrace(); } }

    定义好service,需要使用。方法就是把上面的MyRemoteImpl实例化之后,在RMI 中注册这个服务。

    try{ MyRemote service = new MyRemoteImpl(); Naming.rebind(“RemoteHello”, service); //起一个名字并且注册。 }catch(Exception ex){ … }

    3. 生成stubs和skeletons(即service helper和client helper)(java1.5及以后不再被需要)可以略过。

    使用rmic工具生成两个类:

    rmic MyRemoteImpl MyRemoteImpl_Stub.class 给clientMyRemoteImpl_Skel.class 给service

    4. 运行RMI registry(在service主机上)

    rmiregistry

    确保运行的文件能够到达你的类。稳妥的办法是从你的类文件运行此命令。

    例如从全限定类名的根目录开始。比如bin/test/MyRemoteImpl.class

    那么从bin目录运行

    5. 运行远程服务(在service主机上)

    java MyRemoteImpl

    xxxx是main函数的远程服务。

    从全限定类名的根目录开始。同上。

    Client

    client如何能够得到stub object呢?

    import java.rmi.*; public class MyRemoteClient { // 主程序入口 public static void main (String[] args) { new MyRemoteClient().go(); } public void go() { try { //查找,返回时Object,需要Cast //如果是默认端口和本地地址,可以忽略不写 MyRemote service = (MyRemote) Naming.lookup(“rmi://127.0.0.1:1099/RemoteHello”); String s = service.sayHello(); System.out.println(s); } catch(Exception ex) { ex.printStackTrace(); } } } client去rmi注册表中查找RemoteHello(是注册时的名字)Client需要有远程的接口MyRemote.java

    client如何获得stub这个类?

    方法一:手工交付方法二:动态下载。放到服务器上动态,如果本地搜索不到就是用http请求下来。

    注意事项

    在远程服务启动之前,启动rmiregistry。service运行时调用了Naming.rebind(),这个需要rmiregistry服务。所以必须提前运行忘记使参数或者返回类型序列化。这个编译器检查不出来。只有运行时报错。忘记把stub给client。

    代理模式定义

    为另一个对象提供一个代理或者占位符,来控制对他的访问。

    几个控制访问的方法:

    一个远程代理控制对远程对象的访问

    虚拟代理控制对创建成本较高的资源的访问

    保护代理控制对于权限资源的访问

    远程代理

    上面提的那种

    虚拟代理

    虚拟代理用来标识一个需要昂贵代价去创建的对象。虚拟代理通常来推迟这个对象的创建,直到这个对象被需要时再创建。在被代理对象创建完成之前,虚拟代理扮演者被代理者(RealSubject)的角色, 创建完成之后,虚拟代理把请求转交给被代理对象。

    例如:播放在线音乐,当音乐封面没有被下载的时候,虚拟代理充当封面的角色(可以使一串字符串“正在下载封面”,或者其他的某个默认图片), 一旦封面下载完成,虚拟代理就把所有调用的方法委托给实际的图片。

    // Head Fist Design Pattern P456 class ImageProxy implements Icon { ImageIcon imageIcon; URL imageURL; Thread retrievalThread; boolean retrieving = false; public ImageProxy(URL url) { imageURL = url; } public int getIconWidth() { if (imageIcon != null) { return imageIcon.getIconWidth(); } else { return 800; } } public int getIconHeight() { if (imageIcon != null) { return imageIcon.getIconHeight(); } else { return 600; } } public void paintIcon(final Component c, Graphics g, int x, int y) { if (imageIcon != null) { imageIcon.paintIcon(c, g, x, y); } else { g.drawString(“Loading CD cover, please wait..., x+300, y+190); if (!retrieving) { retrieving = true; retrievalThread = new Thread(new Runnable() { public void run() { try { imageIcon = new ImageIcon(imageURL, “CD Cover”); c.repaint(); } catch (Exception e) { e.printStackTrace(); } } }); retrievalThread.start(); } } } }
    Processed: 0.024, SQL: 9