异步 Web 服务的实现和调用对于应用程序开发非常重要。公开 Web 服务功能的 UI 已日益变得更加交互式。因此,异步调用和实现已变得更加有用和有效,并最终帮助提供更好的总体用户体验。本文概述 Web 服务中的异步场景的不同模式,并提供有关如何使用 Apache Axis2 来实现它们的深入见解。<!-- START RESERVED FOR FUTURE USE INCLUDE FILES--><!-- include java script once we verify teams wants to use this and it will work on dbcs and cyrillic characters --> <!-- END RESERVED FOR FUTURE USE INCLUDE FILES-->
最初在将 Web 服务实现作为一种应用程序开发方法引入时,它仅支持使用同步调用来进行请求-响应交互(本文中的同步 是指在同一个执行线程中处理请求和响应)。但是随着越来越多的应用程序采用 Web 服务来公开功能,并且将客户端应用程序设计为与 Web 服务交互,纯粹的同步调用已被视为瓶颈。这是因为,由于服务实现中的各种原因,有些 Web 服务实现要花相当长的时间才能响应请求。例如,如果某个 Web 服务在某个时点需要人工介入或批处理,该 Web 服务可能要花数天时间才能获得结果。这些延迟还导致某些传输机制超时。
![]() |
随 着 Web 服务开发的发展,最终有些 SOAP 堆栈尝试在同步功能之上模拟异步调用。尽管那些努力有点异步调用的感觉,但它们无法提供完整和真正的功能。Apache Software Foundation 推出的开放源代码 Web 服务引擎 Apache Axis2 为异步 Web 服务调用和实现提供了一流的支持。Axis2 具有两种在客户端提供异步性的机制和一种在服务器端提供异步性的机制。下面更详细地研究一下如何使用这些机制实现异步通信。
客户端异步允许在两个不同的线程中处理请求和响应消息。可以采用两种方法在客户端实现这种异步,下面几个部分将对此进行描述。
可以为客户机提供一个非阻塞 API,使其能够将请求移交给 SOAP 引擎并继续做其他工作。存在一个客户机提供的回调对象,SOAP 引擎会在从服务器接收到响应后通知该对象。对于具有 GUI 的交互式应用程序,此方法非常有用。
Axis2 客户机 API 提供了用于常用消息交换模式(Message Exchange Pattern,MEP)的方法。下面使用 IN-OUT 交互来演示异步支持。
当您使用 ServiceClient
API 时,您将使用 sendReceive(OMElement)
或该方法的变体来实现同步调用。但是,sendReceiveNonBlocking(OMElement, Callback)
提供了初步级别的 Axis2 客户机 API 异步性,如清单 1 所示。
// First create the payload of the message. OMElement payload = getPayload(); // add your payload here // Create an instance of service client. ServiceClient serviceClient = new ServiceClient(); // Create an options object to pass parameters to service client. Options options = new Options(); // Set the location of the Web service. options.setTo(new EndpointReference ("http://www.sample-web-service.com/StockQuote/getQuote")); serviceClient.setOptions(options); // Create a callback object. Callback callback = new MyCallback(); // Do an async invocation. serviceClient.sendReceiveNonBlocking(payload, callback); // Continue doing other work without waiting until the response comes. |
当客户机使用非阻塞 API 调用 Web 服务时,它需要向 Axis2 ServiceClient
API 传递一个回调对象。在响应消息到达时,Axis2 引擎将此响应消息移交给该回调对象。然后该回调对象就可以代表客户机处理此响应,或者将其移交给客户机。
下面研究一下应该如何实现 Callback
。该回调接口具有两个方法:
-
onComplete(AsyncResult)
方法,在接收到响应后调用 -
AsyncResult
对象方法,它封装结果 SOAP 信封和消息上下文
客户机可以扩展 Callback
接口以在此方法中处理响应。另一方面,onError(Exception)
方法在调用期间出错的情况下被调用。请在清单 2 中详细观察这一点。
class MyCallback extends Callback { public void onComplete(AsyncResult result) { // Get the response SOAP envelope from the result. SOAPEnvelope envelope = result.getResponseEnvelope(); // Process SOAP envelope. } public void onError(Exception e) { // Write here what you want to do in case of an error. } } |
还可以将 Callback
类用作轮询机制。客户机可以通过调用 Callback.isComplete()
方法连续轮询某个响应。当存在多个 Callback
实例等待响应,或者存在一个中央控制器监视响应并更新正在运行的应用程序时,这是非常方便的。因此,Callback
类可同时用作回调机制或在异步调用期间用作轮询机制。
第二种方法是使用传输级别的异步。可以在 Web 服务调用中使用的所有传输都可大致归类为两种类型的调用:
- 单向传输 提供一个发送或接收消息的通道。
- 双向传输 可以使用同一个通道发送和接收消息。
简单邮件传输协议(Simple Mail Transfer Protocol,SMTP)是单向传输的示例,而 HTTP 则是双向传输。使用单个 HTTP 通道即可与 Web 服务进行请求-响应交互,而使用 SMTP 则需要两个通道。
客 户机具有用于请求-响应交互的不同选项。最容易的方法是使用诸如 HTTP 之类的双向传输。但是此方法不使用传输级别的异步。客户机还可以使用两个单向传输或两个双向传输。例如,可以使用一个 SMTP 通道发送消息,并使用另一个 SMTP 通道接收响应消息。同时,可以使用一个 HTTP 通道发送请求,在发送消息之后关闭它,并使用另一个 HTTP 通道接收响应。但是要使此方法正常工作,应该存在消息相关性机制。Apache Axis2 使用 WS-Addressing 规范实现消息相关性。下面是处理不同传输的方式。
首先,使用任何传输通道发送请求,并使用一个具有同类型传输的不同通道接收响应。Axis2 客户机可以使用 useSeparateListener
标志,以通知其使用单独侦听器来接收响应消息的意图。换句话说,在启用此标志后,您将要求 Axis2 引擎对此调用使用两个不同的传输通道。ServiceClient
API 是 Axis2 中最容易使用的客户机 API,它具有一个用于设置此值的方法 setUseSeparateListener(boolean)
。清单 3 演示了如何使用 useSeparateListener
API 来调用 Web 服务。
清单 3. 使用 useSeparateListener 变量
try { // first create the payload of the message OMElement payload = getPayload(); // add your payload here // create an instance of service client ServiceClient serviceClient = new ServiceClient(); // create an options object to pass parameters to service client Options options = new Options(); // set the location of the web service options.setTo(new EndpointReference ("http://www.sample-web-service.com/StockQuote/getQuote")); // inform Axis2 engine that you need to use a different channel for the response options.setUseSeparateListener(true); serviceClient.setOptions(options); // invoke the service serviceClient.sendReceive(getPayload()); } catch (AxisFault axisFault) { axisFault.printStackTrace(); } |
此时,您要求 Axis2 引擎准备一个传输侦听器,其类型与用于发送请求的传输类型相同;您还要求它接收响应。所使用的正确传输是由端点引用值推断出来的。此示例尝试将消息发送到 http://www.sample-web-service.com/StockQuote/getQuote,并且此调用中使用了两个 HTTP 传输。可以使用 sendReceive
或 sendReceiveNonBlocking
方法来调用该 Web 服务。
下面将介绍如何使用 Axis2 实现服务器端异步。
![]() |
当 SOAP 引擎收到 SOAP 请求时,通常的方法是处理请求并在同一个线程中发送消息。但是此方法并非始终有用。例如,当处理请求要花相当长的时间时,传输通道可能会超时。或者,对于 处理大量流量的系统,有时在该持续时间内占用资源也许不是很好的选择。因此,理想的选择是通知客户机服务器收到了消息,并在稍后使用一个不同的传输通道发 送应答。
Axis2 采用一个消息接收器,后者负责处理 MEP 并将消息移交给请求处理逻辑或服务实现。在同步调用期间,消息接收器调用服务实现类中的适当方法(假设服务实现是用 Java™ 语言编写的),然后在同一个线程中发送响应。但是在异步调用期间,此消息接收器启动一个新线程来调用服务并向客户机发送通知。新线程负责将响应发送到客户 机,并从服务实现类中取得响应。这就是 Axis2 中的服务器端异步的工作方式。下面研究一下如何将其用于您的 Web 服务。
可以采用两种基本方法创建服务:
- 编写 Web 服务描述语言(Web Services Description Language,WSDL),然后生成服务实现类的代码并填充它。
- 编写服务实现类,编写 services.xml,并在 Axis2 中部署它。
Services.xml 包含有关正在部署的 Web 服务的元数据。它列出服务实现类、公开的方法和任何应该传递给 Web 服务的参数。
如果从上述“WSDL-代码-生成”方法开始,您可以要求代码生成器启用对服务的异步调用。然后代码生成器将生成一个异步消息接收器来调用服务。在运行 WSDL2Java 脚本以从给定的 WSDL 文件生成 Java 代码时,还要使用 -a
选项来指示该脚本生成异步代码。如果使用第二种方法,即代码优先 (code-first) 方法,则应该为 services.xml 中的 IN-OUT 调用关联 org.apache.axis2.async.AsyncMessageReceiver
。必须将 IN-OUT MEP 关联到 org.apache.axis2.async.AsyncMessageReceiver
,如清单 4 所示。
清单 4. 将消息接收器与 IN-OUT MEP 变量相关联
<messageReceivers> // other message receivers <messageReceiver mep="http://www.w3.org/2004/08/wsdl/in-out" class= "org.apache.axis2.async.AsyncMessageReceiver"/> </messageReceivers> |
要使服务器端异步正常工作,必须使用 WS-Addressing,因为您生成了一个新线程来发送响应,并向客户机许诺服务器将在稍后发送响应。但是如果请求处理要花一些时间,则客户机使用的 传输可能超时。因此尝试在同一个通道中执行异步调用可能不够审慎。当客户机向异步 Web 服务发送请求时,它还必须提到一个有效的 replyTo
端点,以便服务器发回响应。
从客户机的角度看,当它调用此异步服务时,比如使用 HTTP,它将在同一个传输通道中收到 HTTP 202。HTTP 202 告诉客户机 Web 服务已经收到请求,但是处理还未完成。然后客户机将通过请求消息中放置的 replyTo
路径接收响应。
![]() |
![]() |
异步调用为 Web 服务用户以及编写和托管 Web 服务的人们提供了大量的灵活性。随着更多的 Web 服务变得普遍可用,同时也变得更加复杂,更多的应用程序能够将那些 Web 服务集成到应用程序中。由于大多数完成后的应用程序最终都要与用户交互,所以挑战就是如何提供更好的用户体验。在不同级别(同时在客户端和服务器端)提供 的异步 Web 服务调用可以帮助应对这其中某些最新的挑战。