美文网首页Java设计模式设计模式java后端
代理模式——远程代理(Java RMI)

代理模式——远程代理(Java RMI)

作者: 理查德成 | 来源:发表于2017-12-30 10:54 被阅读47次

代理模式——远程代理(Java RMI)

@(设计模式)

一、远程代理——大使

在日常开发中,我们经常会有本地服务完成不了的功能,需要访问远程服务中的对象(以下称远程对象)。当然,不可能用下面的代码来引用远程对象,以获取其方法:

Ref ref = <另一个JVM堆的对象>
1. 从实践中归纳

从非面向对象角度,可以描述为本地服务通过网络请求远程服务。为了实现本地到远程的通信,我们需要实现网络通信,处理其中可能的异常。为良好的代码设计和可维护性,我们将网络通信部分隐藏起来,只暴露给本地服务一个接口,通过该接口即可访问远程服务提供的功能,而不必过多关心通信部分的细节。正如世上本没有设计模式,设计模式都是从实践中总结出来的良好设计方式,我们也将以上的总结为远程代理。最后,我们良好设计的通信方式可能如下:

良好设计的通信方式

通信时序如下:

远程通信时序图
2. 面向对象思考

从非面向对象角度,我们关注本地服务如何获得远程服务的功能,而从面相对象角度,我们关注本地服务如何获得远程对象,从而获得远程服务提供的功能。这样,本地对象具有与远程对象完全相同的行为,但是本地的行为是通过远程对象提供的,细细思考,这就是代理模式了。不同的是代理类和真实主题类不在同一个JVM堆中,但其类之间的关系还是与代理模式相同的:

代理模式
3.大使

远程代理为一个位于不同的地址空间的对象提供一个局域代表对象,这个不同的地址空间可以是在本机器中,也可以是在另一台机器中,远程代理的角色,就像一个国家在另一个国家的大使一样,重大问题还是要跟国内沟通。

二、Java RMI实现远程代理

现在,实现远程代理,我们需要实现网络通信,封装细节,处理异常,程序员都是懒惰而聪明的,Java已经提供了一套成熟的方案来实现远程代理。

利用Java RMI实现远程代理,需要分别实现客户端与服务端

1. 服务端

实现远程代理服务端,需要

  1. 制作远程接口,即主题接口
    远程接口需要实现java.rmi.Remote类,该标记接口表示其方法可能从非本地虚拟机调用;
    远程接口所有方法需声明java.rmi.RemoteException异常,涉及网络通信,方法调用可能产生异常;
    远程接口方法变量返回值都是原语或者可序列化类型,因为返回值和变量可能需要远程传输。
  2. 实现远程接口,即真正主题类
    继承java.rmi.server.UnicastRemoteObject类,该类提供远程对象
  3. 启动本地RMI registry
    可以使用JDK自带的rmiregistry命令启动,也可以使用java.rmi.registry.LocateRegistry提供的createRegistry方法启动
  4. 注册远程服务
    使用JNDI注册服务

最后远程服务的类图如下:

远程服务

代码清单

// 远程接口
public interface IMyRemote extends Remote {

    String sayHello() throws RemoteException;
}
// 远程接口实现-远程对象
public class MyRemoteImpl extends UnicastRemoteObject implements IMyRemote {

    public MyRemoteImpl() throws RemoteException {
        super();
    }

    @Override
    public String sayHello() throws RemoteException {
        return "Server says, 'Hey'";
    }

    public static void main(String[] args) throws RemoteException, MalformedURLException {
        IMyRemote service = new MyRemoteImpl();
        // 启动本地rmi registry,默认端口1099
        LocateRegistry.createRegistry(1099);
        // 注册远程对象
        Naming.rebind("rmi://localhost:1099/RemoteHello", service);
    }
}
2.客户端

对于客户端来说,只需要使用JNDI服务找到远程对象,使用即可。代码如下:

public class MyRemoteClient {

    private void go() throws RemoteException, NotBoundException, MalformedURLException {
        IMyRemote service = (IMyRemote) Naming.lookup("rmi://localhost/RemoteHello");
        System.out.println(service.sayHello());
    }

    public static void main(String[] args) throws RemoteException, NotBoundException, MalformedURLException {
        new MyRemoteClient().go();
    }
}

首先启动服务端,再启动客户端,就可以看到来自远程对象的问候了。

三、深入Java RMI

Java RMI固然为我们实现远程对象提供了简便的方式,深入了解其实现细节也会让我们受益匪浅。

1. 一切从通信开始

不管怎么封装,本地服务要想获得远程对象,调用远程服务的方法,必然要经过网络通信。Java RMI为我们处理了这些细节,现在我们要还原这些细节一窥RMI实现。一般地,其中涉及了socket编程与TCP通信:

  1. 服务端监听某个端口
  2. 客户端知道服务端地址,并向服务端监听的端口发请求

这样的通讯方式引入了一些问题:

  1. 谁处理服务端socket相关细节
  2. 谁处理客户端socket相关细节
  3. 客户端如何知道服务端地址,以及服务端监听哪一个端口

为解决以上三个问题,RMI引入如下概念

  1. 谁处理服务端socket相关细节 ——skeleton
    骨架skelton接收来自存根的TCP请求,隐藏通信细节,将真正的调用交给远程对象,并负责组织返回,解耦服务与网络通信;
  2. 谁处理客户端socket相关细节 ——stub
    存根stub接收来自客户的请求,组织参数通过TCP发送到服务端骨架,并接收组织返回给客户,解耦客户与网络通信;
  3. 客户端如何知道服务端地址,以及服务端监听哪一个端口 ——rmi registry
    服务端远程对象注册到rmi registry,客户端通过位置总所周知的rmi registry获取存根stub。rmi registry是一个独立的服务,只有在registry服务启动后,才能将远程服务注册(绑定)到registry中。

这样,远程对象的全部时序细节应该如下,其中Program是服务端负责启动的类,RMI registry与服务端在同一个JVM中。

远程对象通信
2. 代码细节

// TODO: java rmi的代码细节

四、分布式与RMI

rmi registry是一个不同于rmi服务端的独立服务(这也是要先启动rmi registry才能绑定rmi远程服务的原因)。由于将rmi服务绑定到RMI registry时,registry使用自己的类加载器加载服务类,因此registry必须能够访问到服务类的classpath,那么registry必须和rmi服务在同一台机器上了。这就意味这rmi 服务不能注册到远程机器的rmi registry上,不能依靠rmi registry解决分布式中rmi服务的单点故障。

成熟的方案是集成zookeeper,将分布式部署的rmi服务的rmi registry地址注册到zk某个节点,rmi客户端访问该节点获取rmi registry地址,采用合适的负载策略选择具体rmi registry,最后通过rmi registry访问到具体rmi服务。那么整个过程如下:

rmi registry -> zk node -> rmi registry list -> Load Balancing Strategy -> specific rmi registry -> rmi server

refer to

[1] 深入理解rmi原理
[2] Java RMI远程方法调用详解
[3] RMI注册表
[4] RMI(远程方法调用)
[5] 使用 RMI + ZooKeeper 实现远程调用框架

相关文章

  • 代理模式——远程代理(Java RMI)

    代理模式——远程代理(Java RMI) @(设计模式) 一、远程代理——大使 在日常开发中,我们经常会有本地服务...

  • 代理模式二

    远程代理 《Head First 设计模式》中讲,怎么毫无印象啊,再去看一遍,用 Java 内置的 RMI 举例,...

  • java动态代理(JDK和cglib)(转载自http://ww

    java动态代理(JDK和cglib) JAVA的动态代理 代理模式 代理模式是常用的java设计模式,他的特征是...

  • 设计模式笔记(十三): 代理模式

    代理模式是比较重要的一种设计模式,应用场合也比较多,例如远程调用(RMI, RPC等),还有Spring的AOP的...

  • 代理模式

    代理模式感觉是一个复杂的设计模式,变种很多,如:远程代理、虚拟代理、静态代理、动态代理、安全代理等; 定义### ...

  • Mac端口号被占用

    idea错误: 代理抛出异常错误: java.rmi.server.ExportException: Port a...

  • Java代理模式之JDK动态代理

    了解什么是动态代理模式,可参考Java设计模式之代理模式 简介 JDK动态代理是java.lang.reflect...

  • 代理模式C++

    代理模式:为其他对象提供一种代理以控制对这个对象的访问。 代理模式结构图 代理模式基本代码 应用场合 远程代理,也...

  • Java设计模式之代理模式

    Java设计模式之代理模式 代理模式 静态代理 动态代理 为什么需要代理 通过代理,我们能够不用知道委托人是谁,而...

  • java | 什么是动态代理?

    最近在复习 Java 相关,回顾了下代理模式。代理模式在 Java 领域很多地方都有应用,它分为静态代理和动态代理...

网友评论

    本文标题:代理模式——远程代理(Java RMI)

    本文链接:https://www.haomeiwen.com/subject/apyggxtx.html