精彩的人生

好好工作,好好生活

BlogJava 首页 新随笔 联系 聚合 管理
  147 Posts :: 0 Stories :: 250 Comments :: 0 Trackbacks

#

  Web services 是一种很有前途的技术,在面向服务的架构( Service Oriented Architectures , SOA )中起着重要的作用。这种正在兴起的技术的一个关键方面就是提供了异步服务的能力。尽管现在的 web service 标准规范中包括了提供异步服务的内容,但客户端应用程序前景的细节还有一些混乱和模糊。 Web services 回调是实现这些异步服务的一个重要因素。这篇文章为创建带有回调操作的 Web services 的客户应用程序提供了实践指导。这篇文章中所有的代码段都来自于您可以下载的例子。这些例子包含了这些代码段的完整实现和使用指导。

术语

  在开始讨论支持回调的客户端之前,阐明相关术语是很重要的。下图就显示了客户端使用带有回调操作的 Web service 时所涉及到的主要实体。

Figure 1
图 1. 调用 Web service 的客户端

  上图描述了客户端对 Web service 的调用。 Web service 能够在一段时间后通过对客户端的回调做出响应 。因此,包含回调操作的 Web service 客户端的特别之处在于,客户端本身必须提供一个端点。我们调用这一回调端点 ,并将这个端点定义为由 URI 确定的唯一地址, SOAP 请求消息将发送到这个 URI 。

  将 SOAP 消息发送到Web service 端点 之后,客户端本身开始与 Web service 进行交互。由客户端发送给 Web service 的相关请求消息及其相关响应消息构成了客户端初始化操作 。如前所述,客户能够处理 Web service 发送到回调端点的请求消息。相关的请求和响应消息一起被称为一个回调 操作。

  理解这些术语之后,让我们走近一步考察 Web service 回调的概念,以及它与会话式 Web services 和异步 Web service 调用之间的关系。

异步、回调和会话

  异步 Web service 调用的概念有时容易与 Web services 回调和会话式 Web services 相混淆。虽然这三个概念很相关,但却不同。

  异步 Web services 调用 是指在不阻塞接收服务器发来的相应响应消息的情况下,客户端能够发送 SOAP 消息请求 。 BEA WebLogic Platform 8.1 web services 上进行异步 Web service 调用的过程已经详细地 记录在软件文档中了 ,因此在本文中不作进一步的讨论。

  Web services 回调 是指 Web services 提供者向客户端发回 SOAP 消息的情况。 Web Services Description Language (WSDL) specifications 将这种操作定义为一种“请求 / 响应”。支持回调操作的 Web services 客户端本身必须有一个 Web services 端点,这样 Web service 就可以利用这个 Web services 端点在任意时间发送回调请求,也就是说,可以是异步的。注意,这跟我们上面讨论的异步调用 没有关联。

  如果一系列可在两个端点之间来回传送的消息可以被唯一会话 ID 追踪,而这个 ID 又由特定的操作来标识消息流的始末,这种 Web services 就是 会话式 的。提供回调的 Web services 也定义为会话式的。这是因为正常情况下如果 Web services 能够对客户端进行回调访问,它就必须有它自己的回调端点信息。这种情况只有客户端做出了初始调用以后才会发生。因此,至少由客户启动的初始化操作和由 Web services 做出的回调操作是相互关联的,并且由唯一的会话 ID 跟踪。如果不是这样,客户端就无法分辨与不同初始调用相关联的回调操作。

  我们现在将考虑这些问题并创建支持回调的客户端,就像我们刚才所看到的,这些客户端构成了请求 - 响应和会话式 Web services 的基础。

创建支持回调的客户端

  正如前面讨论的,支持回调的 Web services 客户端需要提供一个能够异步接收和处理回调操作消息的回调端点。为避免必须提供回调端点这类复杂的事情,一种叫做 polling (轮询)的技术可以作为替代技术。然而这种技术要求客户端周期性地调用服务端以校验回调事件。如果这种事件很少发生,那么调用的开销就太大了。如果客户端提供一个回调端点并直接处理回调操作,这种开销就可以避免。

  我们还应该注意到,尽管可以通过用 JMS 上的 Web services (如果提供了这种连接)创建支持回调的客户端,但这种方法有一些限制,其中一个重要的限制就是客户端要被迫采用与 Web services 提供者相同的 JMS 实现。因此我们将集中于经过 HTTP 传输来完成我们的任务。有两个领域需要创建这样的客户端:创建适当的客户端启动 SOAP 消息,以及处理回调操作。

  当客户端启动有回调的 Web service 操作时,它需要以某种方式包含回调端点的 URI ,使其在请求消息中监听。 Web Services Addressing SOAP Conversation Protocol 规范都定义了 SOAP 头部元素,允许您实现这一目标。从理论上说,用于规定回调端点的规范并不重要。但是大多数 Web services 容器(包括 BEA WebLogic Server 8.1 )都还没有包含 Web services Addressing 规范的实现形式。当前, BEA WebLogic Workshop 8.1 的 Web services 支持 SOAP Conversation Protocol 规范,我们将在例子客户端中使用。

  根据 SOAP Conversation Protocol , SOAP 头部在会话的不同阶段是不同的。对于会话中的第一个客户端启动(开始)操作,我们需要通过 callbackLocation 头部元素 提供有回调端点的 Web service 。所有操作(包括第一个操作)也将需要唯一的 ID ,这个 ID 在整个会话过程中都用在我们的 SOAP 消息里。这可通过 conversationID 元素 来完成。下面是一个启动支持回调会话的 SOAP 请求消息的例子:

<soapenv:Envelope soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" 
    xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" 
    xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xmlns:enc="http://schemas.xmlsoap.org/soap/encoding/" 
    xmlns:env="http://schemas.xmlsoap.org/soap/envelop/" 
    xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/">
  <soapenv:Header>
    <con:StartHeader soapenv:actor="http://schemas.xmlsoap.org/soap/actor/next" 
     soapenv:mustUnderstand="0" 
     xmlns:con="http://www.openuri.org/2002/04/soap/conversation/">
      <con:conversationID>[123456]:192.168.1.100:8181</con:conversationID>
         <con:callbackLocation>
          http://192.168.1.100:8181/StockNotificationCallback
         </con:callbackLocation>
      </con:StartHeader>
 </soapenv:Header>
  <soapenv:Body>
    <n1:registerForThresholdNotif xmlns:n1="http://www.openuri.org/">
      <n1:stockTicker>CCC</n1:stockTicker>
      <n1:threshold>10</n1:threshold>
    </n1:registerForThresholdNotif>
  </soapenv:Body>
</soapenv:Envelope>

  现有的大多数 Java Web service 工具包(例如 BEA WebLogic 的 clientgen 、 Apache Axis 和 JWSDP )都允许您创建一个代理库,客户端程序可以容易地用它来调用 Web services 操作。但是,这些框架中没有一种支持回调操作,主要问题是它们的代理不生成所需的头部。在能提供这种支持以前,通过扩展它们对回调操作的支持来利用这些框架(例如复杂类 XML 映射),这种益处还是很需要的。一种用来达到这种效果的技术就是应用 SOAP 消息处理程序

  上面提到的所有 Web services 工具包都能支持 SOAP 消息处理程序。消息处理程序是一种 Java 类,它实现了 javax.xml.rpc.handler.GenericHandler 界面,并且还包含一种称为先送出(或后接收) SOAP 消息的方法。这里介绍我们客户端中的消息处理程序,它能够找出一个特定会话的当前阶段,并据此扩展带有所需头部的请求消息。

  注意到这一点是很重要的,客户端 SOAP 消息处理程序必须能确定消息属于会话的哪个阶段,以便创建合适的头部元素。生成会话 ID 也是客户端处理程序要完成的一个任务。

  一旦 Web services 端点收到扩展的请求消息,它就会将请求消息发送到由开始消息的 callbackLocation 元素规定的回调端点。在大多数情况下,客户端过程自身就需要提供这个端点,并且恰当地处理回调消息。如果客户端在 Web services 的容器中运行,这项工作就可以通过把有回调操作的 Web services 部署在同一个容器内来完成。然而,如果客户端不是正在容器中运行,这项工作就要靠在一个嵌入在客户端过程本身的轻量级容器中执行回调端点来完成。这使我们能够调用客户端生成的操作,并且处理同一过程上下文中的传入回调操作。注意在回调端点背后(和在客户端中)的过程实体要求不仅能够分配对适当域的代码操作,而且还能处理 XML 映射。

  当然,客户端也可以选择简单地设置恰当的 callbackLocation 头部元素来规定一个在容器中的回调端点,而这个容器与访问过程相分离。

  正如我们已经看到的,为带回调操作的 Web services 创建客户端并不是毫无意义的,它包含了复杂元素,比如处理 SOAP 消息、管理会话(包括阶段和 ID )、参数映射以及操作分配。当 Web service 通过 BEA WebLogic Workshop Service Control 访问时,这些复杂性就都不存在了,它会自动为用户执行所有操作。

使用服务控件创建支持回调的客户端

  通过 WebLogic Workshop Service Control 访问 Web services 在 软件文档 中已经做了详细描述。如果客户端实体能够使用控件(也就是 Java Process Definition 业务流程或其他控件; Page Flows 不能使用控件回调),这个解决方案就是最简单的使用支持回调的 Web services 的方案了。采用这种方法,所有涉及访问支持回调的 Web service 的复杂性就都迎刃而解了。

股票通知服务例子

  本文的例子包括一个股票通知( Stock Notification )的会话式 Web service 和一个能阐明概念的示例客户端。 Web service 提供的操作允许客户端注册股票接收机并设置一个相关的阈值价格。然后服务端就监视股票,只要股票价格超过了阈值价格就通过一个回调( onThresholdPassed() )通知客户端。

Figure 2
图 2. 本图说明了这个例子的配置

  很显然,轮询技术不是创建客户端的最佳解决方案:股票价格有可能经常超过阈值价格也可能极少超过阈值价格。轮询间隔短就会造成很多不必要的访问,而另一方面,轮询间隔长又可能导致股票价格超过阈值价格很长时间后客户端才被告知。

  客户端应用程序中回调端点的实现应该是一种更好的解决方案。让我们首先来看一下例子客户端是如何将操作传递给 StockNotification Web service 的。我们已经采用两种不同的技术实现了客户端。第一种技术使用 WebLogic Workshop 生成的代理库,并由 StockNotificationClient 类的 makeCallUsingBEAProxy() 方法来实现:

public void makeCallUsingBEAProxy() {
    try {
      // Get the proxy instance so that we can start calling the web service operations
      StockNotificationSoap sns = null;
      StockNotification_Impl snsi = new StockNotification_Impl();
      sns = snsi.getStockNotificationSoap();

      // Register our conversational handler so that it can properly set our
      // our conversation headers with a callback location
      QName portName = new QName("http://www.openuri.org/",
                                 "StockNotificationSoap");
      HandlerRegistry registry = snsi.getHandlerRegistry();
      List handlerList = new ArrayList();
      handlerList.add(new HandlerInfo(ClientConversationHandler.class, null, null));
      registry.setHandlerChain(portName, handlerList);

      // Register, call some methods, then sleep a bit so that our callback
      // location can receive some notifications and finally finish the conversation.
      sns.registerForThresholdNotif("AAAAA", "100");
      sns.addToRegistration("BBBBB", "5");
      StockList stocks = sns.getAllRegisteredStocks();

      ...

      sns.endAllNotifications();
    }
    catch (Exception e) {
      throw new RuntimeException(e);
    }
}

  正如从上述代码段中看到的,我们可以使用 clientgen 生成的类。与没有回调的 Web service 调用相比,这段代码唯一强调的就是,在方法一开始就实例化和注册了客户端 SOAP 消息处理程序。这个处理程序截取了发出的 SOAP 消息,并加上必要的会话头部。每当客户端过程发出 SOAP 请求消息时,消息处理程序的 handleRequest() 方法就被调用,它利用了一个包含了输出 SOAP 请求信息的 MessageContext 对象。下面是例子客户端消息处理程序的代码段:

package weblogic.webservice.core.handler;

...

public class ClientConversationHandler
    extends GenericHandler {

public boolean handleRequest(MessageContext ctx) {  
  ...
  if (phase.equals("START")) {
    headerElement
              = (SOAPHeaderElement) header
              .addChildElement("StartHeader",
                               "con",
                               "http://www.openuri.org/2002/04/soap/conversation/");

    headerElement.addChildElement("con:callbackLocation")
              .addTextNode(CALLBACK_URI);
  }
  else if (phase.equals("CONTINUE") || phase.equals("FINISH")) {
    headerElement
              = (SOAPHeaderElement) header
              .addChildElement("ContinueHeader",
                               "con",
                               "http://www.openuri.org/2002/04/soap/conversation/");
  }

  headerElement.addChildElement("con:conversationID").addTextNode(convID);
  ...
}
}

  BEA clientgen 工具生成的代理库已经用了一个缺省的客户端 SOAP 消息处理程序,可创建会话式 StartHeader 头部元素。不幸的是,目前的 clientgen 输出不支持回调操作,因此缺省的处理程序不会创建所需的 callbackLocation 次级元素。所以我们必须保证自己的消息处理程序重写缺省的会话式处理程序,以便管理创建 callbackLocation 头部的额外任务。我们通过确保处理程序在一个与缺省处理程序相同的程序包中创建来做到这一点,这个程序包就是 weblogic.webservice.core.handler ,并且它还有具体的相同类名称 ClientConversationHandler 。注意,这种技术涉及到重写现有会话消息处理程序,但它并不能保证是向前兼容的。这个例子展示了消息处理程序如何用于创建回调头部,以及这种概念如何应用于任何其他支持 SOAP 消息处理程序的 Web services 框架中。

  有一种使用 SOAP 消息处理程序和代理类的可选技术,它使用 JAXM API 直接创建 SOAP 请求消息,并把它们送往 Web service 。这是我们例子客户端采用的第二种技术,它在 StockNotificationClient 类的 makeCallsUsingJAXM() 和 callMethodFromFile() 方法中实现:

public void makeCallsUsingJAXM() {
      callMethodFromFile("C:\\registerForNotifSOAP.xml");
      ...
      callMethodFromFile("C:\\endAllNotifSOAP.xml");
}

private void callMethodFromFile(String fileName) {

    try {
      // Create a SOAP connection object
      SOAPConnectionFactory scf = SOAPConnectionFactory.newInstance();
      SOAPConnection conn = scf.createConnection();

      // Create the SOAP request message from the specified file
      MessageFactory mf = MessageFactory.newInstance();
      SOAPMessage msg = mf.createMessage();
      SOAPPart sp = msg.getSOAPPart();

      // Read the file content into a string and replace the conversation ID with
      // the current one.
      String fileContent = readFileIntoString(fileName);
      fileContent = fileContent.replaceFirst(CONV_ID_HOLDER,
                                             ConvIDGenerator.getInstance().
                                             getConversationID());

      StreamSource prepMsg = new StreamSource(new StringBufferInputStream(
          fileContent));
      sp.setContent(prepMsg);
      msg.saveChanges();

      // Make the actual call
      conn.call(msg, TARGET_SERVICE_URI);
      conn.close();
    }
    catch (Exception ex) {
      throw new RuntimeException(ex);
    }
}   

  callMethodFromFile() 方法从指定的文件读取 SOAP 消息,用客户端的当前会话 ID 取代消息会话 ID ,最后生成实际调用。当使用 JAXM 方法时, Java 到 XML 的映射操作参数和返回值必须由客户端来完成。通过使用从文件中预先生成的 SOAP 消息(它已经包含了所需的参数值),我们已经避免了这一任务。尽管从概念上来说这种方法很好,但对于比较复杂的客户端端来说,这不是一种可行的解决方案。这些客户端必须从头开始通过编程方式使用 JAXM 来创建消息。

  既然我们已经回顾了客户端在生成 Web services 操作中的作用,下面让我们转向客户端的回调端点的实现方式和对来自 Web services 回调消息的处理。因为回调端点需要能够处理输入的 HTTP 请求, servlet 就是一种合适的选择。进一步说,我们非常需要一个 servlet ,它能够从输入的 HTTP 请求中提取 SOAP 消息,并以一种容易使用的方式提供给我们。 javax.xml.messaging 程序包里的 ReqRespListener 接口和 JAXMServlet 类提供给我们这个功能。只要安排合理,应用了这种接口和类的 servlet 将把所有的目标 HTTP post 请求自动转换成 SOAPMessage 实例,并通过 onMessage() 方法传递给我们。下面的代码显示了 onMessage() 方法如何帮助例子客户端实现这些功能。

public class CallBackHandlerServlet
    extends JAXMServlet
    implements ReqRespListener {


  public SOAPMessage onMessage(SOAPMessage message) {
    try {
      // We have just received a SOAP message at the callback
      // endpoint. In this example we will just assume that
      // the message received is in fact the onThresholdPassed
      // callback request. However, a servlet handling
      // multiple callbacks cannot make this assumption and
      // should process the SOAP message to see what callback
      // it relates to and do as appropriate for each.
      String stockTicker = extractOnThresholdPassedArgument(message);

	System.out.println("[DEV2DEV CALLBACK EXAMPLE]: Received treshold notification 
	                                for: " + stockTicker);

      // Now we have to create a proper response to the callback
      // request. Returning this response as part of the onMessage()
      // method will ensure that the SOAP response gets back to the server.
      SOAPMessage response = createThresholdPassedResponse();

      return response;
    }
    catch (Exception e) {
      throw new RuntimeException(e);
    }
  }
  ...
}

  一旦回调请求消息被 onMessage() 方法所接收,客户端就从股票接收机中提取参数,将它输出到标准设备上,并生成一条相应的响应消息,它还会通过 servlet 返回到 Web service 。使用带有多个回调操作的 Web services 客户端也需要把传入的请求分发给客户端代码的合适部分,这个过程是基于相应的回调操作的。

  尽管 servlet 包含了合适代码来处理来自 Web services 的传入 onThresholdPassed() 回调消息,但它需要在监听回调 URI 的 servlet 容器中部署完成,这样才能完成回调端点的实现。 Jetty 开源项目有一个轻量级 servlet 容器,它可以嵌入在我们的客户端中。将 CallBackHandlerServlet servlet 部署在一个嵌入式 Jetty 容器中,允许我们将回调端点驻留在客户端 Java 进程中。 StockNotificationCallbackProcessor 类是一种客户端使用的公共类,它用于启动 Jetty 服务器和部署CallBackHandlerServlet servlet :

public class StockNotificationCallbackProcessor {

  public static final String CALLBACK_SERVER_PORT = ":8181";
  public static final String CALLBACK_SERVLET_CLASS = 
       "asynchwsconsumer.CallBackHandlerServlet";
  public static final String CALLBACK_SERVLET_NAME = "CallBackHandlerServlet";

  private HttpServer server;

  public StockNotificationCallbackProcessor() {}

  public void startListening() {

    try {
      // Configure the server so that we listen on the callback URI
      server = new Server();
      server.addListener(CALLBACK_SERVER_PORT);
      ServletHttpContext context = (ServletHttpContext) server.getContext("/");
      context.addServlet(CALLBACK_SERVLET_NAME, "/*",
                         CALLBACK_SERVLET_CLASS);

      // Start the http server
      server.start();
    }
    catch (Exception e) {
      throw new RuntimeException(e);
    }
  }

  public void stopListening() {
    try {
      server.stop();
    }
    catch (Exception e) {
      throw new RuntimeException(e);
    }
  }
}

  startListening() 方法把 CallBackHandlerServlet 部署在端口 8181 上并配置嵌入式 servlet 容器,这样所有 HTTP 请求都将指向这个端口。这种方法还启动了嵌入式 servlet 容器。

  现在我们已经考察了 Web service 操作的创建和回调操作的处理,下面我们来看看例子客户端的 main() 方法是如何使用这些元素的:

public static void main(String[] args) {

    StockNotificationClient snClient = new StockNotificationClient();

    snClient.snCallbackProcessor.startListening();

    snClient.makeCallUsingBEAProxy();

    ConvIDGenerator.getInstance().resetConversationID();

    snClient.makeCallsUsingJAXM();

    snClient.snCallbackProcessor.stopListening();
}

  正如您所看到的,这种方法开始于建立一个回调端点(正如前面所看到的,它包括启动嵌入式 servlet 容器和部署回调过程)。该方法接着使用 BEA clientgen 代理来调用股票通知的 Web service 。这样,这种与 Web services 的交互就通过调用 endAllNotifications() 端操作来完成会话,客户端需要改变会话 ID 以启动新会话。客户端使用 ConvIDGenerator 单一类来管理会话 ID ,而 ConvIDGenerator 类又使用 java.rmi.dgc.VMID 类来创建合适的格式化唯一会话 ID 。

  一旦会话 ID 发生了变化,客户端就会用 JAXM 再一次调用股票通知 Web services 。最后,客户端终止嵌入式 servlet 容器,这个过程就结束了。

  我们现在已经完成了对例子客户端的分析,该例子客户端可作为本文的一部分下载。当您运行客户端和股票通知 Web services 的时候,客户端和 Web service 都将把消息显示在标准输出设备上,用以描述会话的不同阶段,包括对回调操作的处理。

结束语

  本文展示了为包含回调操作的 Web services 创建独立客户端过程的步骤。这种客户端需要管理 Web services 调用的会话方面,并提供一个端点用于 Web service 进行回调操作。

下载

CallbackExample.zip (1.7Mb) ——这个文件包括了本文讲述的完整实例应用程序。

参考资料

原文出处

http://dev2dev.bea.com/pub/a/ 2005/03/c allback_clients.html

posted @ 2006-05-07 16:19 hopeshared 阅读(1299) | 评论 (0)编辑 收藏

摘要:

到目前为止,web service交互作用是独立同步的,同时本质上是应答式的。不过,很显然同步应答类型在基于消息的应用中只是一个很小的子集。消息在耦合松散系统中是非常重要的,因此这种限制很关键。Web service规范,例如WS-addressing和WSDL,已经融入了消息的概念并且为包含一个相当大范围的消息应用奠定了基础。Apache Axis2 架构既不基于任一个消息交换模式,也不基于同步/异步行为。这篇文章解释了消息概念和Axis2在几种众所周知的消息场合中怎样被扩展使用。

关于Apache Axis2的Web service消息

作者:Srinath Perera, Ajith Ranabahu

07/27/2005

翻译:doomsday

版权声明:可以任意转载,转载时请务必以超链接形式标明文章原始出处和作者信息及本声明
英文原文地址:
http://www.onjava.com/pub/a/onjava/2005/07/27/axis2.html
中文地址:
http://www.matrix.org.cn/resource/article/43/43723_Apache_Axis2.html
关键词: Apache Axis2 Web service

  
     到目前为止,web service交互作用是独立同步的,同时本质上是应答式的。不过,很显然同步应答类型在基于消息的应用中只是一个很小的子集。消息在耦合松散系统中是非常重要的,因此这种限制很关键。Web service规范,例如WS-addressing和WSDL,已经融入了消息的概念并且为包含一个相当大范围的消息应用奠定了基础。Apache Axis2 架构既不基于任一个消息交换模式,也不基于同步/异步行为。这篇文章解释了消息概念和Axis2在几种众所周知的消息场合中怎样被扩展使用。

消息的简单介绍

    贯穿计算历史,分布式运算是其中的一个很大的挑战:当资源是分布式时,进程间的通信变得相当困难,研究人员仍然在寻找更好的解决方案。有趣的是,几乎所有关于分布式计算机计算能力问题的解决方案来源于两种概念基础: 远程过程调用(RPC) 和消息传递。

    毫无疑问,使用RPC在开发人员中是非常流行的技术,部分原因是是本地过程调用与它的类似之处。本地过程调用在程序员中很有人气,所以在分布式系统中使用RPC是很自然的选择,而另一方面,消息传递不是非常流行,当它被提起时很少有开发人员关注它。不过,在某些场合使用消息相比RPC系统有更好的优势。

RPC和消息框架的基本差异如下所示:

●消息完全不懂客户端和服务器,因为一个消息框架(通常所说的面向消息的中间件,或message-oriented middleware,即MOM)集中于传递消息,所有接收和散发消息的节点身份平等,术语称之为对等体。RPC始终有服务请求者 (AKA client) 和服务提供者 (AKA server)的概念。
●消息对于一个特定范畴是时间独立的。没有任何对等体希望实时接收消息--当对等体可用时MOM关注于传递一个消息到相应的对等体,然而,RPC在失去一方时立即失效。
●消息可被复制并且轻易的传递到众多对待体。RPC本质上是一种一对一的交流方式,而消息更灵活,并且毫不费力地传递同一消息的拷贝到多种对等体。

Web service消息

    Web service是在XML消息的基础上定义的,下面3个因素描述了一个给定的Web service的消息交互。

●消息交换模式
●同步和异步客户端API
●单向和双向传送行为

    从最抽象的角度来讲,,web service消息传递建立在发送和接收消息基础上,一个给定的消息被一方发出,并且被另一方接收。消息可能相互关联,识别这些相互关联的消息群中的最常见的应用场合是非常重要的,这些消息群被定义为消息交换模式(message exchange patterns),简称MEPs.

    过渡时期下在两种相关消息间的一个服务请求者的行为在客户端API定义了消息同步/异步行为。同步场合下,客户端请求将会阻塞,在相关消息到达目的地后前一直等待,在非阻塞场合下,客户端请求不会阻塞,当相关消息到达时,它与之前的消息相互联系。

    传送分类为单向或双向,基于单方或双方行为。类似SMTP和JMS传送即是单向传送的,不会阻塞,另一方面,类似HTTP和TCP即是双向传送,相关消息可能在回路中返回,实际上,在Web service消息中,双向传送可能被用作单向传送,但是在这些场合下,它们可被有效的处理为单向方式。

消息交换模式

    根据W3C建议,消息交互模式就是一个为交流双方构建消息交换的一个模板,一个MEP将相关消息确定为一组。MEPs根据服务请求者和服务提供者来定义,需要注意,为了清晰,MEPs以服务提供者的消息特性来命名。为方便理解,所有的命名都可以用request代替in, 用response代替out。

例如,我们看看两个有名的MEPS

1.In-only/"发后不理:" 服务请求者发送消息给服务提供者但是不关心任何后继相关消息
2.In-out/"应答式:" 服务请求者发送消息给服务提供者并希望返回结果

    MEPS概念仍在扩展中,模式的数目是没有限制的,所以Web service中间件应用强制使用几种选定的MEPs,"发后不理"和 "应答式" 是被明确使用的,其它大多数的模式可由这两种组合使用。

客户端API同步/异步行为

    同步/异步(或阻塞/非阻塞)行为是基于在web service请求的线程,同步服务将会阻塞,等待相关消息到达。另一方面,异步请求仅仅返回,等待相关消息被后台另一个不同线程执行。

    这两种途径有典型的用例。考虑一下银行事务,其需要一定数量的消息来来回传递。银行事务本质是连续的,当结果到达时后执行下一步骤,因此同步等待结果很有意义。另一方面,设想一个航班预约程序,其需要搜集多种数据来源,根据这些结果再匹配。这个案例中,异步行为发挥作用,因为程序可以提交所有结果并且当数据到达时工作。考虑到网络响应,异步方式获得较好的结果。

    同步请求很简单:请求在相关消息到达前等待,并且可以像本地过程调用一样被编码。但是异步消息的相互关系就比较复杂,客户端必须处理这种复杂性。尽管如此,通过一些额外工作来处理这种复杂情况仍是必要的。

传输层的行为

    传输层的行为是个关键因素,决定了Web service消息的发生,传输根据依据其行为分类为单向和双向。
    单向传送减少web service消息的复杂性,因为相关消息必须来源于各自的通道。另一方面,如果传送是双向的,消息有机会选择使用单向还是双向,例如,传输是HTTP时,相关消息来自HTTP连接的返回路径,或者这么讲web service提供者可以写HTTP 200来指出没有来自同一连接的响应,在这种案例下回应经由独立的HTTP连接发送。

Web service寻址角色

    webs ervice寻址框架(也被称为WS-addressing)是为了在同一web service交互活动中交换不同信息部分,下面的5个因素定义了交互活动:

    1.消息交换模式
    2.可被访问的服务传送
    3.相关消息传送
    4.传送的行为
    5.客户端API的同步/异步行为

    服务提供者申明了头两个,客户端定义了其余因素,客户端级别的同步/异步行为对服务提供者是完全透明的,客户端使用WS-addressing来解释web service消息。

    在其它大多数结构中,WS-addressing定义了四种标题:To, ReplyTo, RelatesTo, FaultTo以及一个被称为匿名地址的特定地址,当一个服务提供者接收到SOAP消息时,它会查找在to地址上的目标服务并且调用服务。如果有结果,即被发送到ReplyTo地址,错误被发送到FaultTo地址,如果以上任一个的标题没有指定或具有匿名值,结果通过双向传送返回路径返回(因为匿名决定双向传送返回路径)。

    传送和WS-addressing一起定义了查找相关消息的机制,消息是相关缘于它们共享了相同的传送通道,也可能因为它们都共享把它们互相链接的公共信息。web service RelateTo标题正好提供了这种关系。

下面的表显示的明确定义的消息交互活动的寻址标题的不同值



Axis2客户端API概念

    Axis2客户端API处理了In-Only和In-OutMEPs,所有的消息结合在下面的章节讨论。MEPs的空间是无限的,因此,Axis2强制提供了支持任意消息交换模式的核心,并且提供了两种常被使用的模式In-Only和In-Out的API,有两种方法实现更多的复杂模式:组合In-Only和In-Out来完成希望的模式,或者对希望的模式写新的扩展。因为Axis2为任意MEP提供核心级别的支持,实现是显而易见的。In-Only和In-OutMEPS被InOnlyMEPClient和InOutMEPClient类支持,下两节即做具体描述。

In-Only MEP 支持: InOnlyMEPClient

    InOnlyMEPClient类对发送不理消息提供了支持,所有的传送类型作为单向传送对待,InOnlyMEPClient和InOutMEPClient真正的差别是寻址参数起先没有锁定,并且寻址参数随后被Axis2控制。作为可被控制的寻址参数,InOnlyMEPClient可被用作消息API,并且在此基础上构建更复杂的消息交互。

In-Out MEP 支持: InOutMEPClient

    InOutMEPClient和继承了InOutMEPClient的调用类为应答式消息提供了支持,Axis2关注完整的操作,除了To地址外的所有的寻址属性都在Axis2的控制下

    用户可以配置InOutMEPClient 来表现不同,利用以下的四个参数。

1.发送者传输
2.监听者传输
3.是用单独监听
4.使用阻塞



    客户端API当前提供了针对HTTP和SMTP传输的支持,下面的表格显示了这些参数可能的组合以及它们怎样结合来提供不同特效。

举例

    下面的代码实例显示了怎样使用Apache Axis2做几个定义明确的交互作用,用户可以在客户端API简单的转换属性从而转换不同的交互作用,客户端Axis2 API仅仅支持XML级别的消息和代表大块XML的OME元素。

调用单向消息

    单向MEP简单之处在于在仅有一个消息来回传送时它能表现正确的单向,这些消息被异步对待并且传送是单向的。

应答式消息

    可以表现四种方式的应答式消息

    1.双向In-Out 同步
    2.双向In-Out 异步
    3.单向In-Out 同步
    4.单向In-Out 异步

    下面的代码实例说明这些案例怎样被Axis2寻址,注意客户端API的四种属性怎样被使用。

    1.In-Out同步,HTTP作为双向传输方式

OMElement payload = .... 
Call call = new Call();
call.setTo(
        new EndpointReference(AddressingConstants.WSA_TO,
                "HTTP://...));
call.setTransportInfo(Constants.TRANSPORT_HTTP,
       Constants.TRANSPORT_HTTP, false);
OMElement result =
        (OMElement) call.invokeBlocking(
        operationName.getLocalPart(), payload);


    这里,SOAP消息经由同一个HTTP连接传播,地址属性没有指定,所以它们在服务器方缺省为匿名,客户端API将被锁定直到回复消息到达。

    2.In-Out异步,HTTP使用HTTP作为双向传送

//this is the payload goes on the body of SOAP message 
OMElement payload = ....
Call call = new Call();
call.setTo(
        new EndpointReference(AddressingConstants.WSA_TO,
                "HTTP://...));
call.setTransportInfo(Constants.TRANSPORT_HTTP,
              Constants.TRANSPORT_HTTP, false);

Callback callback = new Callback() {
    public void onComplete(AsyncResult result) {
        //what user can do to result
    }
    public void reportError(Exception e) {
       //on error
    }
};
call.invokeNonBlocking(operationName.getLocalPart(),
       payload, callback);


    和前面相同,SOAP消息经由同一个HTTP连接传输并且不需要寻址,一旦回复消息到达客户端API不会阻塞并且回调将被执行。

    3.In-Out, 异步HTTP 作为单向传输

OMElement payload = .... 
Call call = new Call();
call.setTo(
        new EndpointReference(AddressingConstants.WSA_TO,
                "HTTP://...));
call.setTransportInfo(Constants.TRANSPORT_HTTP,
    Constants.TRANSPORT_HTTP, true);
Callback callback = new Callback() {
        public void onComplete(AsyncResult result) {
        ....
        }

        public void reportError(Exception e) {
        ...
        }
};
call.engageModule(new Qname("addressing"));
call.invokeNonBlocking(operationName.getLocalPart(), method, callback);


    在这个案例中,SOAP消息通过两个HTTP连接传输,寻址是强制的,ReplyTo标题出现指示服务器端经由单独的通道发送回应。客户端没有阻塞,当回应消息到达时,唤起回调。

4.In-Out, 同步 HTTP 作为单向传送

OMElement payload = .... 
Call call = new Call();
call.setTo(
new EndpointReference(AddressingConstants.WSA_TO,
        "HTTP://...));
call.setTransportInfo(Constants.TRANSPORT_HTTP,
    Constants.TRANSPORT_HTTP, true);
OMElement result =
        (OMElement) call.invokeBlocking(
           operationName.getLocalPart(), payload);


    在这种场合下使用"In-Out,异步HTTP作为单向传送"类型,在结果到达第二种连接时唤起阻塞,执行并返回结果。

总结

    总而言之,web wervice消息行为建立在三种因素上:消息交互模式,客户端同步异步模式和传送行为。Asis2建立核心在不一定要任何MEP类型,不过为MEPs的广泛支持:单向和应答提供了客户端API支持,这篇文章解释Axis2消息支持概念和客户端API的使用。

资源
  
Apache Axis2的官方站点
●W3C讨论稿, "WSDL Version 2.0 Part 2: Message Exchange Patterns"
"Why Messaging? The Mountain of Worthless Information," Ted Neward的博客, 星期三,2003年5月9日
"Introduction to WS-Addressing,"  作者Beth Linker, dev2dev, 2005年1月31日
"Async," Dave Orchard的博客, 2005年5月5日
"Programming Patterns to Build Asynchronous Web Services,"  Holt Adams, IBM开发者文集, 2002年6月1日

Srinath Perera是Apache Axis2的主要架构师
Ajith Ranabahu是Apache Axis2项目的成员并且在基于Web service的项目里工作了3年


posted @ 2006-05-07 16:05 hopeshared 阅读(689) | 评论 (0)编辑 收藏

     摘要: 深入学习Web Service系列之 异步开发模式 ——《深入学习 Web Service 系列》之一 Terrylee , 2005 年 12 月 4 日 ...  阅读全文
posted @ 2006-05-07 15:28 hopeshared 阅读(754) | 评论 (0)编辑 收藏

在确定是否适合在您的应用程序中采用异步 Web 方法时,有几个问题需要考虑。首先,调用的 BeginXXX 函数必须返回一个 IAsyncResult 接口。IAsyncResult 是从多个异步 I/O 操作返回的,这些操作包括访问数据流、进行 Microsoft&reg; Windows&reg; 套接字调用、执行文件 I/O、与其他硬件设备交互、调用异步方法,当然也包括调用其他 Web 服务。您可以从这些异步操作中得到 IAsyncResult,以便从 BeginXXX 函数返回它。您也可以创建自己的类以实现 IAsyncResult 接口,但随后可能需要以某种方式包装前面提到的某个 I/O 操作。

对于前面提到的大多数异步操作,使用异步 Web 方法包装后端异步调用很有意义,可以使 Web 服务代码更有效。但使用委托进行异步方法调用时除外。委托会导致异步方法调用占用进程线程池中的某个线程。不幸的是,ASMX 处理程序为进入的请求提供服务时同样要使用这些线程。所以与对硬件或网络资源执行真正 I/O 操作的调用不同,使用委托的异步方法调用在执行时仍将占用其中一个进程线程。您也可以占用原来的线程,同步运行您的 Web 方法。

下面的示例显示了一个调用后端 Web 服务的异步 Web 方法。它已经使用 WebMethod 属性标识了 BeginGetAge 和 EndGetAge 方法,以便异步运行。此异步 Web 方法的代码调用名为 UserInfoQuery 的后端 Web 方法,以获得它需要返回的信息。对 UserInfoQuery 的调用被异步执行,并被传递到 AsyncCallback 函数,后者被传递到 BeginGetAge 方法。这将导致当后端请求完成时,调用内部回调函数。然后,回调函数将调用 EndGetAge 方法以完成请求。此示例中的代码比前面示例中的代码简单得多,并且还具有另外一个优点,即没有在与为中间层 Web 方法请求提供服务的相同线程池中启动后端处理。

[WebService]

public class GetMyInfo : System.Web.Services.WebService
{

[WebMethod]
public IAsyncResult BeginGetAge(AsyncCallback cb, Object state)
{

// 调用异步 Web 服务调用。
localhost.UserInfoQuery proxy
= new localhost.UserInfoQuery();
return proxy.BeginGetUserInfo("用户名",
cb,
proxy);
}

[WebMethod]
public int EndGetAge(IAsyncResult res)
{
localhost.UserInfoQuery proxy
= (localhost.UserInfoQuery)res.AsyncState;
int age = proxy.EndGetUserInfo(res).age;
// 在此对 Web 服务的结果进行其他
// 处理。
return age;
}
}

发生在 Web 方法中的最常见的 I/O 操作类型之一是对 SQL 数据库的调用。不幸的是,目前 Microsoft&reg; ADO.NET 尚未定义一个完好的异步调用机制;而只是将 SQL 调用包装到异步委托调用中对提高效率没有什么帮助。虽然有时可以选择缓存结果,但是也应当考虑使用 Microsoft SQL Server 2000 Web Services Toolkit(英文)将您的数据库发布为 Web 服务。这样您就可以利用 .NET Framework 中的支持,异步调用 Web 服务以查询或更新数据库。

通过 Web 服务调用访问 SQL 时,需要注意众多的后端资源。如果您使用了 TCP 套接字与 Unix 计算机通信,或者通过专用的数据库驱动程序访问其他一些可用的 SQL 平台,甚至具有使用 DCOM 访问的资源,您都可以考虑使用众多的 Web 服务工具包将这些资源发布为 Web 服务。

使用这种方法的优点之一是您可以利用客户端 Web 服务结构的优势,例如使用 .NET Framework 的异步 Web 服务调用。这样您将免费获得异步调用能力,而您的客户端访问机制会与异步 Web 方法高效率地配合工作。

使用异步 Web 方法聚合数据

现在,许多 Web 服务都访问后端的多个资源并为前端的 Web 服务聚合信息。尽管调用多个后端资源会增加异步 Web 方法模型的复杂性,但最终还是能够显著提高效率。

假设您的 Web 方法调用两个后端 Web 服务:服务 A 和服务 B。从您的 BeginXXX 函数,您可以异步调用服务 A 和服务 B。您应该向每个异步调用传递自己的回调函数。在从服务 A 和服务 B 接收到结果后,为触发 Web 方法的完成,您提供的回调函数将验证所有的请求都已完成,在返回的数据上进行所有的处理,然后调用传递到 BeginXXX 函数的回调函数。这将触发对 EndXXX 函数的调用,此函数的返回将导致异步 Web 方法的完成。

小结

异步 Web 方法在 ASP.NET Web 服务中提供了一个有效的机制,可以调用后端服务,而不会导致占用却不利用进程线程池中的宝贵线程。通过结合对后端资源的异步请求,服务器可以使用自己的 Web 方法使同时处理的请求数目达到最大。您应该考虑使用此方法开发高性能的 Web 服务应用程序。


原文地址:http://ewebapp.cnblogs.com/articles/237372.html
posted @ 2006-05-07 15:22 hopeshared 阅读(507) | 评论 (0)编辑 收藏

为举例说明异步 Web 方法,我从一个名为 LengthyProcedure 的简单同步 Web 方法开始,其代码如下所示。然后我们再看一看如何异步完成相同的任务。LengthyProcedure 只占用给定的毫秒数。

[WebService]

public class SyncWebService : System.Web.Services.WebService
{

[WebMethod]
public string LengthyProcedure(int milliseconds)
{
System.Threading.Thread.Sleep(milliseconds);
return "成功";
}
}

现在我们将 LengthyProcedure 转换为异步 Web 方法。我们必须创建如前所述的 BeginLengthyProcedure 函数和 EndLengthyProcedure 函数。请记住,我们的 BeginLengthyProcedure 调用需要返回一个 IAsyncResult 接口。这里,我打算使用一个委托以及该委托上的 BeginInvoke 方法,让我们的 BeginLengthyProcedure 调用进行异步方法调用。传递到 BeginLengthyProcedure 的回调函数将被传递到委托上的 BeginInvoke 方法,从 BeginInvoke 返回的 IAsyncResult 将被 BeginLengthyProcedure 方法返回。

当委托完成时,将调用 EndLengthyProcedure 方法。我们将调用委托上的 EndInvoke 方法,以传入 IAsyncResult,并将其作为 EndLengthyProcedure 调用的输入。返回的字符串将是从该 Web 方法返回的字符串。下面是其代码:

[WebService]

public class AsyncWebService : System.Web.Services.WebService
{
public delegate string LengthyProcedureAsyncStub(
int milliseconds);

public string LengthyProcedure(int milliseconds)
{
System.Threading.Thread.Sleep(milliseconds);
return "成功";
}

public class MyState
{
public object previousState;
public LengthyProcedureAsyncStub asyncStub;
}

[ System.Web.Services.WebMethod ]

public IAsyncResult BeginLengthyProcedure(int milliseconds,
AsyncCallback cb, object s)
{
LengthyProcedureAsyncStub stub
= new LengthyProcedureAsyncStub(LengthyProcedure);
MyState ms = new MyState();
ms.previousState = s;
ms.asyncStub = stub;
return stub.BeginInvoke(milliseconds, cb, ms);
}

[ System.Web.Services.WebMethod ]
public string EndLengthyProcedure(IAsyncResult call)
{
MyState ms = (MyState)call.AsyncState;
return ms.asyncStub.EndInvoke(call);
}
}


原文地址:http://ewebapp.cnblogs.com/articles/237375.html
posted @ 2006-05-07 15:18 hopeshared 阅读(281) | 评论 (0)编辑 收藏

仅列出标题
共30页: First 上一页 8 9 10 11 12 13 14 15 16 下一页 Last