• 代理模式之远程代理


    远程代理:最经典的代理模式之一,远程代理负责与远程JVM通信,以实现本地调用者与远程被调用者之间的正常交互

    有些事情必须得依靠代理来完成,比如要调用另一台机器上的一个方法,我们可能就不得不用代理
    远程代理的内部机制是这样的:

    解释一下,Stub是“桩”也有人称之为“存根”,代表客服服务对象,也就是所谓的代理,代表了Server对象;
    Skeleton是“骨架”,是服务的扶助对象。Stub和Skeleton负责通信,类似于用Socket编写的聊天程序

    ##步骤:

    1、制作远程接口

    • 扩展java.rmi.Remote
      表示此接口用来支持远程调用
    • 声明所有的方法都会抛出RemoteException
      因为每次远程调用都是有风险的,所以客户在实现方法的时候要处理异常。
    • 确定变量和返回值是属于原语类型或者可序列化类型
      也就是说远程方法的变量或者返回值需要io传送,必须是原语或者Serializable类型
    1. packageProxyPattern;
    2. import java.rmi.RemoteException;
    3. /**
    4. * 定义服务接口(扩展自java.rmi.Remote接口)
    5. * @author ayqy
    6. */
    7. publicinterfaceServiceextends java.rmi.Remote{
    8. /* 1.方法返回类型必须是可序列化的Serializable
    9. * 2.每一个方法都要声明异常throws RemoteException(因为是RMI方式)
    10. * */
    11. /**
    12. * @return 完整的问候语句
    13. * @throws RemoteException
    14. */
    15. publicString greet(String name)throwsRemoteException;
    16. }

    2、制作远程实现(也就是所谓的服务器端的代码)

    • 实现远程接口
      服务端和客户端都要实现这个接口,保证客户调用正确方法
    • 扩展UnicastRemoteObject
      要保证你的类具有远程功能就继承该类,让超类帮你做这些工作
    • 设计一个不带变量的构造器,并声明RemoteException
      超类的问题就是构造器会抛出异常,所以子类需要声明构造器也抛出异常。
    • 用RMI Registry注册此服务
      为你的服务命名,好让客户在注册表中按照此名字找到,在绑定服务对象的时候,RMI会把服务换成stub,stub放在register中。
    1. packageProxyPattern;
    2. import java.rmi.RemoteException;
    3. import java.rmi.server.UnicastRemoteObject;
    4. /**
    5. * 实现远程服务(扩展自UnicastRemoteObject并实现自定义远程接口)
    6. * @author ayqy
    7. */
    8. publicclassMyServiceextendsUnicastRemoteObjectimplementsService{
    9. /**
    10. * 用来校验程序版本(接收端在反序列化是会验证UID,不符则引发异常)
    11. */
    12. privatestaticfinallong serialVersionUID =1L;
    13. /**
    14. * 空的构造方法,只是为了声明异常(默认的构造方法不会声明异常)
    15. * @throws RemoteException
    16. */
    17. protectedMyService()throwsRemoteException{
    18. }
    19. @Override
    20. publicString greet(String name)throwsRemoteException{
    21. return"Hey, "+ name;
    22. }
    23. }

    服务端有了服务还不够,我们需要一个Server帮助我们启动RMI注册服务,并注册远程对象,供客户端调用:

    1. packageProxyPattern;
    2. import java.net.MalformedURLException;
    3. import java.rmi.Naming;
    4. import java.rmi.RemoteException;
    5. import java.rmi.registry.LocateRegistry;
    6. /**
    7. * 实现服务器类,负责开启服务并注册服务对象
    8. * @author ayqy
    9. */
    10. publicclassServer{
    11. publicstaticvoid main(String[] args){
    12. try{
    13. //启动RMI注册服务,指定端口为1099 (1099为默认端口)
    14. LocateRegistry.createRegistry(1099);
    15. //创建服务对象
    16. MyService service =newMyService();
    17. //把service注册到RMI注册服务器上,命名为MyService
    18. Naming.rebind("MyService", service);
    19. }catch(RemoteException e){
    20. // TODO Auto-generated catch block
    21. e.printStackTrace();
    22. }catch(MalformedURLException e){
    23. // TODO Auto-generated catch block
    24. e.printStackTrace();
    25. }
    26. }
    27. }

    3、产生Stub和Skeleton

    • 在远程实现类(不是远程接口)上执行rmic
      rmic是JDK的工具,主要就是来产生stub和skel两个类的,注意此时这两个类都会以_stub和_skel后缀出现,但stub是需要到客户端,所以等会我们看一下这个类怎么传送到客户端。
      直接在CMD中执行
    1. %rmic MyService

    4、执行remiregistry

    • 开启一个终端,启动rmiregistry
      也就是启动注册表,但你要保证可以访问你的类,所以在classes目录下启动比较好
    1. %rmiregistry

    5、启动服务

    开启一个终端,启动服务
    从main中启动,实例化了服务对象并在RMI register中注册

    1. %java Server

    6、制作客户端测试

    客户端只有拿到了这个stub以后就可以操纵这个服务了

    • 客户到RMI registry中寻找
    1. //如果要从另一台启动了RMI注册服务的机器上查找MyService对象,修改IP地址即可
    2. Service service =(Service)Naming.lookup("//127.0.0.1:1099/MyService");
    • RMI register返回stub对象, stub再返回的时候回被反序列化的,但是首先保证你要有stub类的,这个是因为你实现了同一个接口,所以rmic会自动在客户端产生stub类的。
    • 客户调用stub的方法,就像stub就是真正的服务对象一样
    1. packageProxyPattern;
    2. /* 参考资料:
    3. * 1.JAVA RMI怎么用
    4. * http://blog.csdn.net/afterrain/article/details/1819659
    5. * 2.RMI内部原理
    6. * http://www.cnblogs.com/yin-jingyu/archive/2012/06/14/2549361.html
    7. * */
    8. import java.rmi.Naming;
    9. /**
    10. * 实现客户类
    11. * @author ayqy
    12. */
    13. publicclassClient{
    14. /**
    15. * 查找远程对象并调用远程方法
    16. */
    17. publicstaticvoid main(String[] argv)
    18. {
    19. try
    20. {
    21. //如果要从另一台启动了RMI注册服务的机器上查找MyService对象,修改IP地址即可
    22. Service service =(Service)Naming.lookup("//127.0.0.1:1099/MyService");
    23. //调用远程方法
    24. System.out.println(service.greet("SmileStone"));
    25. }
    26. catch(Exception e)
    27. {
    28. System.out.println("Client exception: "+ e);
    29. }
    30. }
    31. }

    总结

    拦截并控制方法调用(这也是代理模式最大的特点,最典型的,防火墙代理。。) 远程对象的存在对客户是透明的(客户完全把Stub代理对象当做远程对象了,虽然客户有点好奇为什么可能会出现异常。。) 远程代理隐藏了通信细节
    当我们需要调用另一台机器(JVM)上指定对象的方法时,使用远程代理是一个不错的选择。。





  • 相关阅读:
    不容易系列之一(错排)
    找新朋友(欧拉函数)
    二分查找
    快速排序(分治)
    归并排序(分治)
    畅通工程(并查集)
    A Knight's Journey (DFS)
    Network Saboteur (DFS)
    Oil Deposits(油田)(DFS)
    Dungeon Master (三维BFS)
  • 原文地址:https://www.cnblogs.com/oneNightStand/p/28864710b610d4639970ea5f113779cf.html
Copyright © 2020-2023  润新知