8.1.2 访问分布式对象

    熟悉EJB的读者可能非常了解分布式对象通信机制,EJB的Stub正是使用代理模式的好例子。

    为了不增加学习该模式的难度,我们并不打算使用EJB来介绍代理模式,而是仿照其原理来创建自己的分布式对象进行说明。

    我们首先看看分布式对象的实现原理,如图8-2所示。

    figure_0129_0121

    图8-2

    Client对象使用Stub代理对象进行远程访问,对Client对象来说,由于接口一致,操作远程对象和本地对象并没有什么不同。Stub对象转发这些操作的请求给远程对象,Skeleton接收请求返回Service对象执行结果,这即是一次远程调用执行的全过程。

    我们这里有一个实现了Service接口的ServiceImpl分布式对象。Service接口包含一个hello()方法。

    figure_0129_0122

    实现类ServiceImpl的hello()方法非常简单,仅返回“Server says hello!”字符串。代码如下所示。

    figure_0129_0123

    我们引入了Service_Stub代理对象,实现Service接口。它将hello()方法的调用请求发远程Skeleton对象,Skeleton将服务对象的方法执行结果写回给Service_Stub代理,这样Service_Stub对象为客户对象隐藏了远程访问。Stub的代码片段大致如下所示。

    figure_0130_0125

    figure_0131_0126

    figure_0132_0127

    我们在该例中使用Java NIO实现远程通信,如果有人对NIO不够熟悉,可以参看下面的详细解说,有兴趣想深入学习的读者可以参考附录A的推荐书籍。

    Service_Stub通讯过程中,我们用到两种SelectionKey:一种关联一个读操作,即key.isReadable()==true,我们把读操作的处理写在if(key.isReadable()){……}代码块里;另一种关联一个写操作,即key.isWritable()==true,我们写在else{……}代码块里。

    Stub通讯主要分为3个步骤。

    1.建立连接:和Skeleton服务建立通信连接,并注册一个写操作,即socketChannel.register(sel, SelectionKey.OP_WRITE),这样,接下来Stub就可以向服务端发送请求了。

    2.发送请求,即写操作:Stub向Skeleton发送“hello”字符串,然后注册一个读操作,这样Stub开始准备从通信通道socketChannel等待执行结果。

    3.接收返回,即读操作:读取远程调用返回的结果,关闭此次的通信通道socketChannel,最后返回这次执行的结果。

    Service_Skeleton类继承于Thread类,让我们来看看它如何实现的,代码片段大致如下所示。

    figure_0132_0128

    figure_0133_0129

    figure_0134_0130

    figure_0135_0131

    Service_Skeleton循环等待接收客户端发送来的请求,直至处理完成一次Stub的服务请求,它的通讯主要分4步。

    1.建立Server端:监听指定的服务端口,然后注册连接操作,即这句serverSocketChannel.register(sel, SelectionKey.OP_ACCEPT),等待Stub端的访问。

    2.和客户端建立连接:监听到Stub发送的连接请求,建立连接,为此次通信通道注册读操作。

    3.接收请求,即读操作:接收Stub发送过来的请求数据,如果接收到的字符串是“hello”,则注册一个写操作并准备向客户端发送执行结果。

    4.发送结果,即写操作:执行服务对象的hello()方法,把执行结果写回客户端,关闭此通信通道,最后设置finished=true以关闭服务端。

    到此为止,我们已经完成了分布式调用的所有机制。为了等待Stub对象的调用,我们必须先启动Service_Skeleton服务,启动代码如下。

    figure_0135_0132

    现在,让我们看看客户端如何使用,代码如下。

    figure_0135_0133

    我们可以看到,客户端实例化了一个Service_Stub对象,调用它的hello()方法,但是它并不知道这个Stub对象只是远程对象的一个网络代理,即客户端对远程对象的访问具有透明性,运行结果如下。

    Server says hello!

    实际应用中的EJB分布式调用比这个例子复杂,这里只是以其为例介绍代理模式。