使用基于事件的分布式架构来创建异步的 Web 服务应用程序
 

2009-11-16 作者:Francis Geysermans ,Jeff Miller 来源:IBM

 
本文内容包括:
Web 服务已经在实际应用中证明了它自身的用处。然而,当前的 Web 服务模型是一种同步模型,它建立在一种请求/响应的体系结构之上。所以,借助于一种新的框架体系,我们可以跨越这种同步模式的局限性。在本文中, Francis Geysermans 和 Jeff Miller 给我们讲述了这种基于事件的分布式体系结构,将这种结构应用到 Web 服务领域,那么,我们就可以在 Observer (观察者)模式的基础上创建 Web 应用程序。

Web 服务的出现是企业应用程序之间通信方式的一场革命,大大的扩展了应用程序的可用性,便于不同种类的应用程序互相通信。Web 服务的伟大之处在于能够通过 RPC (Remote Procedure Call,远程过程调用)的机制来访问它,这种机制采用自描述的接口,而该接口可以和它的实现解藕,也就是说,可以独立于其实现。Web 服务采用了基于开放的 Internet 标准的体系结构,并且可以动态的发布和发现。

 WSDL (Web Services Description Language, Web 服务描述语言)是一种基于 XML 的语言,可以用来描述 Web 服务的接口。 WSDL 的数据类型和数据结构可以用多种编程语言来实现。Web 服务的运行机制很大程度建立在 service requesterservice provider 之间的清晰划分上。在以前的技术中,它们分别称为客户机和服务器。Service provider 也就是服务提供者,是它提供了 Web 服务,服务请求者(Service Requester)查找、绑定和调用这些 Web 服务。这个过程是单向的:一方发出请求,另外一方作出响应。尽管这种通信也很灵活,但是,这种无状态的过程是固定的。通常情况下,总是先有一个请求,后有同步的响应。尽管已经开始发展更高层次的 Web 服务编排技术,在目前这种 同步请求/响应模型中,每一个 Web 服务调用总要涉及几个固定的步骤,先是服务请求者发送一个请求,随后服务提供者返回一个响应。虽然这种 Web 服务的发现是动态而且灵活的, Web 服务提供者还是需要使用某种方式来发布 Web 服务的接口。在这一层面上, Web 服务的使用是固定的,总是需要先提供 Web 服务的接口,然后才是在开发时期或者运行时期来发现这些 Web 服务。

在本文中,我们将展示如何逾越这种同步模式的局限性。这里要介绍一种 Java 框架结构,使用它可以构建多种异步的 Web 服务体系。您可以从 alphaWorks 下载本文附带的源代码,以及 FAQ (常见问题)和参考资料。在 参考资料部分提供了相应的链接。

观察者模式

在应用程序结构中,通常采用 Observer (观察者)设计模式来减少互相通信的组件之间的耦合。传统的观察者模式有两个主要的参与者:一个 subject (目标)和一个 observer(观察者)。观察者(Oberver)关注目标(Subject)的状态改变。图 1 显示了观察者模式的机制。

观察者模式顺序图

在这种模式中,观察者期望在目标的状态改变时得到一个通知(Notification)。观察者必须注册或者绑定到目标对象,在目标对象中维护了一个观察者列表,当目标对象的状态更改之后,它将通知列表中的观察者。从全局层次上来看,这其实是一种 异步序列,因为开始的 attach() 方法调用是朝着一个方向的。

在登记到目标对象之后,观察者处理它自身的流程。在随后某个未知的时候,目标对象的状态发生了更改,目标对象会使用 update() 回调方法,通知观察者,告知已经发生了状态更改事件。

因此,从一个较高的层次上,观察者模式提供了一种简单的 异步模型。 图 2 展示了类之间的关联。

类之间的关联

观察者模式和 Web 服务

观察者的唯一使用之处就是在 Web 服务的实现中。如 图 1 2,可以将 attach()update()detach() 方法看作 Web 服务。 setState()getState() 方法也可以是 Web 服务,不过从目前来看好像它们是不相关的。如果每一个方法都是一个 Web 服务,那么根据定义,其中的每一个都是包含了请求和响应的同步 Web 服务。 图 3说明了这一点。 图 2 3 之间的惟一的不同之处是,前者把每一个类当作标准设计模式的组件,后者把它们当作 Web 服务组件。

Web 服务组件之间的关联

图 3 中必须注意的一点是:可以选择使用数据组件(Data Bean),用它来描述事件和包含状态更改,将该数据组件作为参数传递给 ServiceRequester.update() 方法。这也可以代替那个回调 ServiceProvider.getState() 方法的服务请求者。

在 Web 服务术语中,最初观察者是服务请求者,目标是服务提供者。然而,如果是使用 Web 服务来实现的话,目标对象和观察者对象就变成了对方的客户机和服务器,也就是说,互相作为客户机和服务器。在 update() 调用期间,目标对象发起了对观察者的回调;因此,从这一点上来说,目标对象是服务请求者,观察者对象是服务提供者。每个组件都是在某些时候充当服务请求者,在另外的时候充当服务提供者。因此,如果使用 Web 服务来实现这种观察者回调方法,我们就可以在同步的基础结构中获得异步的体系。

将基于事件的

在 Web 服务中实现这种设计模式是非常有意义的,因为它引发的体系结构具备了独特的灵活性。这种体系结构可以称为 Distributed Event-Based Architecture ,简称 DEBA (基于事件的分布式体系结构)。将 DEBA 应用到 Web 服务中可以获得两方面的好处,包括 Web 服务的解藕特性和观察者模式的异步特性。

DEBA 的特点是等待事件的分布式活动组件。建立在 DEBA 之上的系统总是试图达到一种均衡,参与者可以随意的加入和离开,这些都是建立在应用程序逻辑的基础之上。 DEBA 组件可以充当目标对象,称为 observable ,也可以充当观察者。它们究竟充当什么对象,并不依赖于它们发布了什么,而是依赖于它们是什么,顾名思义,要看它们扩展了 DEBA 基类的哪一个。

DEBA 可以同时充当观察的目标对象(Subject)和观察者(Observer)。每个观察者决定观察什么,所以整个工作流是建立在单个决定基础之上,而不是一个过载流程控制器。因此, DEBA 提供了一个真正的动态工作流模型,它的优点是高度解藕,并且无需实现描述。

DEBA 组件可以互相建立点对点关系、一对多关系,或者多对多关系。因此,可以有多种结构。 图 4 显示了一个分布式状态机,每个观察者可以根据它当前的状态,综合它相关的观察目标的状态更改,来决定随后的操作。

DEBA 分布式状态机

一个观察者对象接收到了状态更改的通知(箭头所指的方向),然后,该观察者本身也作为被观察目标,它然后决定在下一步通知哪些它自身的观察者。

DEBA 组件也可以充当路由器,如 图 5


DEBA 组件充当路由器

图 5 中,居中的路由器节点是左边观察目标(Subject)的唯一观察者(Observer),同时,在右边观察者仅仅观察路由器。

图 6展示了居中节点所实现的双向工作流。居中的控制器节点可以使用流程语言来保存期望的流程序列。


中央控制器管理 DEBA 工作流

图 7 中,我们组合了前面已经展示的几个模型,来展示 DEBA 的灵活性。工作流模型的一个中央节点控制那些参与了分布式状态机和两个路由器模型的多个节点;整个系统是使用 DEBA 来实现的。 DEBA 的独特之处在于它提供了一个改变,让本来同步的 Web 服务模型也能够提供完全异步的体系结构。

复合 DEBA 模型

DEBA 事件模型提供了一种大有不同的功能,有别于 WSDL 通知操作所能提供的功能,WSDL 通知操作带有自身的局限性。在通知操作中,端点(例如一个服务提供者)发送一个消息。它是单向的回调。而在 DEBA 中的消息都是请求-响应操作。在基本 Web 服务结构之上采用观察者模式,就为 DEBA 带来了回调能力。然而,在 WSDL 1.1 中没有定义通知操作的绑定。要想了解更多关于 WSDL 通知操作的信息,可以参见在后面 参考资料部分提供的 WSDL 1.1 规范链接。

DEBA 框架结构

我们使用了一系列 Java 类,实现了 Web 服务的 DEBA 框架结构。主要的组件是 Observer Observable 类,以及它们的支撑类。表 1 中三个方面的对照,分别为观察者模式、标准 Web 服务模型,以及在 DEBA 框架类中观察者模型的实现。

表 1. DEBA 框架类和观察者模式对照表

观察者模式 Web 服务 DEBA 框架
Subject ServiceProvider AbstractWebObservable
Subject.attach() ServiceProvider.addRequester() AbstractWebObservable.addObserver()
Subject.detach() ServiceProvider.removeRequester() AbstractWebObservable.deleteObserver()
Observer ServiceRequester AbstractWebObserver
Observer.update() ServiceRequester.update() AbstractWebObserver.update()

如表 1 所示,在 Java DEBA 框架的实现中,目标对象(Subject)实现为 AbstractWebObservable ,它是 java.util.Observable 的子类。因此, ObservableObserver 类是框架的基础。因为观察者需要访问被观察目标,观察者客户机可以使用框架提供的两个主要帮助类,来提供更多方便之处。 RemoteObservable RemoteObservableProxy (从 SOAP 架构的角度来看)都是运行在观察者客户机的同一个 JVM 中。 RemoteObservableProxy 类作为一个 Web 服务访问 AbstractWebObservable 的用户实现。后面还有更多步骤;可以参看 图 9 的步骤 3 可以获得更多详细信息。

在 DEBA 框架中有两层代理: SOAP 代理和框架自身的代理,都打包在 deba.core 中。 SOAP 代理类的类名称中必须包含“Proxy”。在 图 8 11 ,初始服务提供者类位于左边,服务请求者类位于右边。在 图 9 11 ,使用 JVM 1 代表提供者,使用 JVM 2 代表请求者,但是这只适用于 DEBA 框架,非 Java 提供者和请求者可以运行期间加入进来。

在图中我们也可以发现, MyObservable MyObserver 类代表了 DEBA 框架中的样本用户实现。这两个样本类可以做为使用该框架的指导。我们将在后面部分进一步讨论。

图 8 显示了框架中 Observable 一边的主要组件。


Class model for the observable components

扩展了 AbstractWebObservable 类的实例做为真正的服务提供者。 AbstractWebObservable 维护了一个 Vector ,以 URI/URL 对的形式集合了 RemoteObservers 对象(参见 图 10 )。在发出通知的时候,列表中的每一个 RemoteObserver 对象都作为 Web 服务而被调用和更新。

在服务请求者一方,需要调用 RemoteObservableRemoteObservableProxy 类的实例。这些类的命名是以 Remote 开头,表明这些类位于服务请求者一方,但是代表了服务提供者一方的观察目标(Observable),或者是它的代理。 RemoteObservable 是一个生成的 SOAP 代理的包装器,它给 SOAP 基础结构中的客户端应用程序类带来了更大程度的灵活性。 RemoteObservable 类使用它的相关代理来完成如下任务:

  • 保存被观察服务提供者的 observableURIobservableURL
  • 保存它代表的观察者对象的 observerURIobserverURL (例如, MyObserver )。
  • 调用底层的 SOAP Web 服务请求,接受同步响应。

在本地实现中, URI/URL 组合充当了对象引用。

图 8 中有一点没有显示。 RemoteObservable 类使用一个验证类的外部化实现(Externalization),来验证服务提供者的 URL 是否有效,这就是所谓的策略(Strategy)模式。 DEBA 支持多种类型的 Web 服务实现,并且,使用外部化验证,支持多种类型的调用前检查。而验证类本身也可以作为 Web 服务而被访问。对于这个类,我们将详细讨论。

图 9 显示了观察者登记或者绑定到一个被观察目标的流程。使用 DEBA 框架,开发人员扩展了 AbstractWebObservable ,在框架的顶层实现了它们自己的被观察 Web 服务。在 图 8 和 9 中, MyObservable 类代表了这样的一个扩展。

观察者注册到被观察目标

图 9 中,着重强调了用户类,特别是 MyObserver MyObservable 。为了添加为一个观察者, MyObserver 用户类(更为精确的说,是 AbstractWebObserver )调用 RemoteObservable 的实例,它代表了远程服务提供者。 RemoteObservable.addObserver() 方法如清单 1 所示。

        
    public void addObserver(String observerEndPointURL, String observerURI) 
        throws Exception
    {
        this.soapProxy.addObserver(observerEndPointURL, observerURI);
    }

RemoteObservable 使用 RemoteObservableProxy ,把它用作一个标准的 SOAP 代理,和服务提供者类 MyObservable 进行真正的通信。实际上,通信过程是调用它的父类 AbstractWebObservable 的一个方法实现。 RemoteObservableProxyaddObserver() 方法通过 SOAP 调用传递 observerEndPointURLobserverURI 参数。然后,在发生一个需要通知的事件后, MyObservable 类便通知所有的观察者,如 图 11所示。

图 10 描述了观察者组件的类模型。同样,组成初始服务提供者的类位于左边,组成服务请求者的类位于右边。这些类和被观察组件相对应。样本用户类 MyObserver 类扩展了 AbstractWebObserver 。为了简单起见,在 图 10 中没有显示外部化验证类。

图 11 中可以看到被观察目标对观察者的回调。使用 DEBA 框架,开发人员扩展 AbstractWebObserver 类,实现它们自己的观察者服务请求者。在 图 10 11 中显示为 MyObserver 类。

观察者组件的类模型

被观察对象更新观察者

服务提供者的状态可以作为 update() 方法的参数,直接传递给服务请求者,而不是单独调用 getState() 方法来获得服务提供者的状态。这里有一种备选的方案,服务提供者使用一个 Web 服务,实现了 getState() 方法,服务请求者在  update() 方法中回调这个 Web 服务,获得被观察目标的状态更改。这个方法是在 MyObserver update() 方法中直接进行的。

在前面我们数次提到了验证类。在 图 11 中没有显示整个图片。为了减少无效的 URL 和 URI 回调所产生的异常,或者是在一个观察者的更新回调之前执行其他类型的校验,DEBA 框架使用了一个附加的类,即 DefaultUpdateStrategy ,如 图 12所示。

DefaultUpdateStrategy

如清单 2 所示, update() 方法是 DefaultUpdateStrategy 类的主要方法。

        
    public void update(Observable observable, Object state) 
    {
        // Do any validation of state or URLs/URIs in this method.
        AbstractWebObservable o = (AbstractWebObservable)observable;
        try 
        {
            String stateString = (String)state;
            this.observer.getSoapProxy().onEvent(o.getEndPointURL(),
                                                 o.getObservableURI(), stateString);
        }
        catch (Exception e) 
        {
            System.out.println(e.toString());
        }
    }

实际上,正是 DefaultUpdateStrategy 类调用了 RemoteObserverProxy.onEvent() 方法,然后跨网络进行 Observer 类的 onEvent() 方法的回调。更精确的说,调用的是它的父类 AbstractWebObserveronEvent() 方法。在清单 4 的底部显示了 AbstractWebObserver.onEvent() 是如何简单的调用 update() 方法。 图 13 显示了使用 DefaultUpdateStrategy 类的真实流程,当然,该策略类也可以被其他的验证类所替代。在观察者一方也可以使用类似的策略( Strategy )模式。


真实的更新流程,包括了 DefaultUpdateStrategy

通读代码

现在我们来看看其中的部分代码,理解 DEBA 框架的机制。实际上是相当的简单。我们可以开始于在 图 8 建模的基本被观察组件。清单 3 是 AbstractWebObservable 类,它是 java.util.Observable 的子类,实现了访问观察者的 SOAP 机制。它维护了注册观察者的 Vector 集合,根据特定的情形添加或者删除它们。

        
package deba.core;
import java.util.Observable;
import java.util.Iterator;
import java.util.Vector;
public class AbstractWebObservable extends Observable 
{
    // Add an observer to the vector of observers
    public void addObserver(String observerEndPointURL, String observerURI)
    {
        RemoteObserver o = this.getRegisteredObserver(observerEndPointURL, observerURI);
        if (o == null) 
       {
            o = new RemoteObserver(observerEndPointURL, observerURI);
            this.addObserver(o);
            this.observers.addElement(o);
        }
    }
    // Remove an observer from the vector of observers
    public synchronized void deleteObserver(String observerEndPointURL, String observerURI)
    {
        RemoteObserver o = this.getRegisteredObserver(observerEndPointURL, observerURI);
        if (o != null) 
       {
            this.deleteObserver(o);
            this.observers.remove(o);
        }
    }
    // Return the registered observer at the passed endpoint URI and URL
    protected RemoteObserver getRegisteredObserver(String observerEndPointURL,
                                                   String observerURI)
    {
        Iterator i = observers.iterator();
        RemoteObserver o = null;
        RemoteObserver temp = null;
        while (i.hasNext()) 
       {
            temp = (RemoteObserver)i.next();
            if (temp.getEndPointURL().equals(observerEndPointURL) &&
                temp.getObserverURI().equals(observerURI)) 
           {
                o = temp;
                break;
            }
        }
        return o;
    }
    // Get this observable's endpoint URL (where is the rpcrouter to this observable?)
    public String getEndPointURL() 
    {
        return endPointURL;
    }
    // Set this observable's endpoint URL
    public void setEndPointURL(String endPointURL) 
    {
        this.endPointURL = endPointURL;
    }
    // Get this observable's URI (what is this observable's Web service name?)
    public String getObservableURI() 
    {
        return observableURI;
    }
    // Set this observable's URI
    public void setObservableURI(String observableURI) 
    {
        this.observableURI = observableURI;
    }
    // The URL of this observable, e.g. http://localhost:8080/debaWeb/servlet/rpcrouter
    private String endPointURL;
    // The URI of this observable, e.g. http://tempuri.org/example.Myobservable
    // or urn:example.MyObservable, etc.
    private String observableURI;
    // The list of registered observers
    private Vector observers = new Vector();
}

由此可知, AbstractWebObservable 主要关注于观察者的管理。 AbstractWebObservable 和它的父类 java.util.Observable 密切相关,包含了一个真正的 notifyObservers() 方法。随后加入了 RemoteObserverRemoteObserverProxy 帮助类(helper),所以,除了处理它自己的业务逻辑之外,用户编写的被观察对象不用完成更多的工作,随后就可以定期的调用 notifyObservers(state) 方法。

清单 4 包含了基本观察者组件的代码。

         
package deba.core;
import java.util.Observer;
import java.util.Observable;
abstract public class AbstractWebObserver implements Observer 
{
    // The one method that must be implemented by subclasses
    public abstract void update(Observable observable, Object state);
    public void register(String observableEndPointURL, String observableURI) 
        throws Exception 
    {
        RemoteObservable o = new RemoteObservable(observableEndPointURL, observableURI);
        o.addObserver(this.getEndPointURL(), this.getObserverURI());
    }
    public synchronized void unRegister(String observableEndPointURL, String observableURI) 
        throws Exception
    {
        RemoteObservable o = new RemoteObservable(observableEndPointURL, observableURI);
        o.deleteObserver(this.getEndPointURL(), this.getObserverURI());
    }
    public String getEndPointURL() 
    {
        return endPointURL;
    }
    public void setEndPointURL(String endPointURL) 
    {
        this.endPointURL = endPointURL;
    }
    public String getObserverURI() 
    {
        return observerURI;
    }
    public void setObserverURI(String observerURI) 
    {
        this.observerURI = observerURI;
    }
    public void onEvent(String observableEndPointURL, String observableURI, String state) 
    {
        RemoteObservable o = new RemoteObservable(observableEndPointURL, observableURI);
        this.update(o, state);
    }
    /* These are the URL and URI of the observer itself, for example
    endPointURL = http://localhost:8080/debaWeb/servlet/rpcrouter
    observerURI = http://tempuri.org/example.MyObserver */
    private String endPointURL;
    private String observerURI;
}

和它对应的 AbstractWebObservable 一样, AbstractWebObserver 负责在被观察对象中注册和取消注册。因为它有自己的 URL 和 URi 的 访问和设置方法(也就是 setter 和 getter 方法), AbstractWebObserver 是独立于位置的可重用代码。 AbstractWebObserver 也独立于所观察的目标,观察目标的 URL 和 URI 是作为参数传入 AbstractWebObserver.register() 方法。因为这两个参数是传递进来的,所以观察者可以观察多个目标。因此,和它对应的被观察目标一样,用户编写的 AbstractWebObserver 类只需在 update() 方法中处理它自己的业务逻辑,而不用关注更多。在谈到 update() 方法时,注意一下清单 4 中的 AbstractWebObserver.onEvent() 方法。我们将看到如何使用它。

Web 服务 DEBA 框架的异常

因为没有编译时期检查机制,有三种方式可以检查 Observer.update() 的存在:

  • 注册期间验证。 在这个时候,观察目标可以检查观察者的 WSDL 文件,测试观察者的回调方法,或者使用策略(Strategy)模式委托真正的校验测试,并且以某种方法进行策略测试,这样就可以减少在后面会遇到的异常。
  • 运行期间 SOAPException 错误。 可以使用一种偷懒的方法来尝试对 observer.update() 方法的回调;如果调用失败,这说明该方法不存在。在这种情况下,错误消息是“Method 'update' is not supported.”。然而,这需要进行一次网络调用。这是一次对 update() 方法调用的失败尝试,在理想的情况下必须避免。
  • 通过 WSDL 扩展。 一种较好的方式是在 WSDL 中包含一个 implements 语句,例如 implements Listener 。这是在生成 WSDL 和 代理的时候加入的,在其中的 Listener WSDL 包含了一个 update() 服务。在这种情况下,如果不支持 update() 方法,被观察目标可以从列表中删除这个观察者。

在 DEBA 框架中对异常的处理具有良好的灵活性。在 DEBA 框架中提供了默认的验证调用机制,减少了观察目标和观察者的可用性问题所导致的异常。

样本应用程序

在 alphaWorks 站点的代码库中包含了一个测试 DEBA 框架的样本应用程序。它也可以作为你实现自己的 DEBA 框架应用程序的基础。在样本应用程序中的类有:

  • MyObservable.java
  • MyObservableProxy.java
  • MyObserver.java
  • MyObserverProxy.java

所有类都在 example Java 包中,也包括了相应的 WSDL 。 example.MyObservable 也是 AbstractWebObservable 的一个子类,而 AbstractWebObservableObservable 的子类。 MyObservable 启动一个线程,读取系统时钟。每秒钟它调用 notifyObservers() 来更新注册观察者的时间为当前时间。在产品 Web 应用程序中,通常是 Web 容器来管理线程,而不是应用程序。而本实例仅仅是用来展示。你可以在清单 5 中验证该代码。

           
package example;
import java.net.URL;
import deba.core.AbstractWebObservable;
import java.util.Date;
public class MyObservable extends AbstractWebObservable implements Runnable 
{
    public MyObservable()
    {
        super();
    }
    public String getState() 
    {
        return "An event occurred at " + time.toString();
    }
    public void run() 
    {
        while(this.shouldRun) 
        {
            time = new Date();
            System.out.println("The observable says : " + time);
            super.setChanged();
            
        this.notifyObservers(getState());
            try 
            {
                thread.sleep(1000);
            } 
            catch (Exception e) 
            {
                e.printStackTrace();
            }
        }
    }
    public void stopClock() 
    {
        this.shouldRun = false;
        this.thread = null;
    }
    public void startClock() 
    {
        if (this.thread==null) 
        {
            this.shouldRun = true;
            this.thread = new Thread(this);
            this.thread.start();
        }
    }
    private static Thread thread = null;
    private static boolean shouldRun = false;
    private Date time = new Date();
}

在清单 5 中,编写了一个使用 DEBA 框架的观察者,你很少需要处理底层的 SOAP 基础结构。唯一需要的代码是那一行对 notifyObservers() 方法的调用。扩展的 AbstractWebObservable 为你完成了剩下的一切工作。

在样本应用程序中,还包括了为 MyObservable MyObserver 而生成的样本 SOAP 测试客户机,可用于 WebSphere Studio Application Developer (请参阅 参考资料)。每一个 Result.jsp 使用其中的一个代理类,通过 SOAP 和 目标实现 MyObserver 或者 MyObservable 进行通信。结合使用测试客户端和样本应用程序,就可以调用 MyObserver Web 服务,注册到 MyObservable Web 服务,启动 MyObservable 线程,然后就可以从系统控制台中看到可能的输出,如清单 6 所示。

        
[12/29/02 17:59:56:265 EST] 4ecbbd62 SystemOut  U The observable says : 
Sun Dec 29 17:59:56 EST 2002
[12/29/02 17:59:56:315 EST] 6d49fd63 SystemOut  U The observer is updated by deba.core.RemoteObservable@4d95fd61, event : 
An event occurred at Sun Dec 29 17:59:56 EST 2002
[12/29/02 17:59:57:346 EST] 4ecbbd62 SystemOut  U The observable says :
 Sun Dec 29 17:59:57 EST 2002
[12/29/02 17:59:57:407 EST] 6d49fd63 SystemOut  U The observer is updated by deba.core.RemoteObservable@5c667d66, event :
 An event occurred at Sun Dec 29 17:59:57 EST 2002
[12/29/02 17:59:58:448 EST] 4ecbbd62 SystemOut  U The observable says :
 Sun Dec 29 17:59:58 EST 2002
[12/29/02 17:59:58:568 EST] 6d49fd63 SystemOut  U The observer is updated by deba.core.RemoteObservable@25ddbd6d, event :
 An event occurred at Sun Dec 29 17:59:58 EST 2002
[12/29/02 17:59:59:610 EST] 4ecbbd62 SystemOut  U The observable says :
 Sun Dec 29 17:59:59 EST 2002
[12/29/02 17:59:59:670 EST] 6d49fd63 SystemOut  U The observer is updated by deba.core.RemoteObservable@61947d62, event :
 An event occurred at Sun Dec 29 17:59:59 EST 2002
[12/29/02 18:00:00:701 EST] 4ecbbd62 SystemOut  U The observable says :
 Sun Dec 29 18:00:00 EST 2002
[12/29/02 18:00:00:761 EST] 6d49fd63 SystemOut  U The observer is updated by deba.core.RemoteObservable@14707d63, event :
 An event occurred at Sun Dec 29 18:00:00 EST 2002

依此类推,为了在 WebSphere Studio 中运行测试客户机,或者提供给外部的 WebSphere Application Server ,那么在 JSP 输入页面需要输入一些值。 DEBA 框架和这个样本应用程序可以工作于 J2EE 应用服务器,可以在 Web 服务运行期使用 Apache SOAP 。如下的修改是必需的:

  • 测试 MyObservable -- 观察目标的值:
    • endPointURL = http://localhost:8080/debaWeb/servlet/rpcrouter
    • observableURI = http://tempuri.org/example.Myobservable
  • 测试 MyObserver --  观察者的值:
    • endPointURL = http://localhost:8080/debaWeb/servlet/rpcrouter
    • observerURI = http://tempuri.org/example.MyObserver
  • 测试 MyObserver.register() :
    • observableEndPointURL = http://localhost:8080/debaWeb/servlet/rpcrouter
    • observableURI = http://tempuri.org/example.Myobservable

值得注意的是,本实例的测试客户机都是运行在同一个服务器,即 WebSphere Studio Application Developer 4.0.3 的内置服务器。如果要同时测试两者,在服务器上运行 MyObservable 测试客户机,设置它的 endPointURLobservableURI 值。然后运行 MyObserver 测试客户机,设置相应的值,然后注册到 MyObservable 。最后,启动 MyObservable 时钟,查看如清单 6 所示的输出。在 alphaWorks 站点提供了关于代码的 FAQ (常见问题回答),对于样本应用程序和内置的 TestClients 提供了更为详细的解释。

DEBA 可行性

目前分布式系统正朝着自管理方向发展, DEBA 组件也是如此。客户机和 Web 服务可以加入到自管理系统,获得体系结构的灵活性。DEBA 架构和网格(Grid)架构结合得非常好,其中的每个组件都可以随意的加入或者离开网格(请参阅 参考资料)。每当添加一个监听程序(Listener),网格便得到了扩展。这种系统本质上是变体的动态系统,每个参与方都负责其自身的参与,但是,如果涉及到多个同类的参与方,它便是可添加的。所以, DEBA 架构本质上是网格计算(Grid computing)的补充。

你可以使用 Web 服务 DEBA 架构实现策略(Strategy)模式。在这种模型中,作为标准,策略提供者可以定期的查找 UDDI ,以获得特定类型的 Web 服务,这种服务称为 RFSS Web 服务(Request for strategy Service, 策略请求 Web 服务)。策略提供者注册这些请求者,其实它们就是策略的消费者。策略提供者是策略消费者的潜在观察者,它会查找 RFSS Web 服务,检查请求者的类型,注册一个回调方法,以执行特定的策略。然后,如果策略消费者需要策略提供者执行特定的策略,策略消费者作为被观察对象,回调提供者,以执行策略操作。按照这种方式,策略消费者可以决定哪种策略执行结果是最适合期望的结果。整体架构便是异步回调事件模式之上的同步 Web 服务,也就是基于事件的分布式 Web 服务架构(DEBA Web 服务架构)。

另外一种可用于 DEBA 架构的设计模式是 Visitor (访问者)模式,将操作外部化,可以对其他对象、对象组或者对象结构执行这些操作。因为操作和它所作用的对象是单独实现的,所以可以在不更改目标对象的前提下更改操作。操作组可以打包城一个对象,称之为 Visitor (访问者)。在目标对象和访问者(Visitor)之间的交互可以用 DEBA 来实现。

结论

DEBA 是指基于事件的分布式 Web 服务架构,提供了 Web 服务之上的异步方法,获得了服务请求者和服务提供者之间的高度松散耦合。DEBA 框架是这种能力的唯一实现。本文种我们解释了 DEBA 架构,如何使用它来构建多种灵活的分布式 Web 服务应用程序。使用 参考资料部分中的链接,可以下载本文用到的 DEBA 框架代码和样本代码。期望你们对它有兴趣,并且能够更好的利用。

参考资料

火龙果软件/UML软件工程组织致力于提高您的软件工程实践能力,我们不断地吸取业界的宝贵经验,向您提供经过数百家企业验证的有效的工程技术实践经验,同时关注最新的理论进展,帮助您“领跑您所在行业的软件世界”。

资源网站: UML软件工程组织