kapok

垃圾桶,嘿嘿,我藏的这么深你们还能找到啊,真牛!

  BlogJava :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理 ::
  455 随笔 :: 0 文章 :: 76 评论 :: 0 Trackbacks
http://www.ftponline.com/china/XmlFile.aspx?ID=258

见资源
将异步消息传递(如Java Message Service)与SOAP结合起来就意味着Web services可以有更多的用途。
by David Chappell and Tony Hong

直到最近,Web services的用途一直集中于通过HTTP实现的同步请求/回应模式(request/reply model)。这是很自然的,因为Web services的第一个用例就是集中在Remote Procedure Call(RPC)和分布式组件交互的。然而,Web services的这种应用情况忽视了当今IT前景的一个重大的方面:异步消息传递(asynchronous messaging),它有很大的价值,因为它给数据传输和处理带来了一定的可靠性和灵活性,这些性能是同步消息传递很难实现的。通过异步消息传输机制(如JMS),把异步消息传递同SOAP结合起来就可以将如今的Web services标准应用到更广的领域。

将这种技术结合用于重要的企业集成环境是最受欢迎的了。该领域一直是由Electronic Data Interchange (EDI)、Enterprise Application Integration(EAI)和B2B统治的——然而IT社团一直在寻找成本更低、更高效、更基于标准的方法。在这些领域中,用即时的同步RPC来换取一些功能会让我们更满意,这些功能包括可靠的传递能力、更适合面向企业交易的松散耦合的、粗粒度的(coarse-grained)接口;面向文档的异步交互和对通讯协议和传输机制的支持。

让我们看看JMS是如何成为一种可靠的异步SOAP消息传递的方法的。我们也将探讨如何将JMS用于可靠的Web services调用。(你可以在这里下载样例代码;注意,你需要接受一个许可协议并在www.xmethods.net网站上注册。)


				图1.
图1. 多方同步通讯
许多Web services都运用一个同步的RPC模式,在这个模式中,服务呈现细粒度的(fine-grained)接口,而且一个Web services客户端调用RPC型(RPC-style)的交互。在一个RPC型的环境中,参与一个单独的通讯流程的所有的应用程序和服务都必须是可用的,以使多方通讯成功。在有些情况下,同步模式是必需的,比如当服务传递的信息在本质上是实时的(如详细目录查找)时候。然而,这种同步处理是一种要么全有要么全无的(all-or-nothing)命题。如果由于某种原因不能得到一个下行的(downstream)服务,那么提出请求的应用程序及其上行的(upstream)参与者就需要处理应用程序代码中的错误(见图1)。

可靠的异步通讯是指,为了保持整个环境的健全,系统的所有方面不需要在任何特定的时候都是可用的。即使对于一个简单的两方客户端/服务(client/service)交互来说,运用可靠的异步通讯就意味着,当客户端对服务提出请求时,被调用的服务不需要是可用的。客户端将请求传递到一个可靠的异步传输机制中,该请求暂时不能与服务进行对话。然后客户端本身变成不可用的,当服务成为可用的时,就可以进行服务调用了。

处于等待状态的(pending)交互
异步交互也不需要客户端等待响应。一个Web service端点(end point)可以代表一个应用程序,它需要一些时间来执行计算、查找或转换等任务。对一个服务提出异步请求的客户端可以继续执行其它任务,同时其Web service交互处于等待状态。这就提高了并行性(concurrency)和可扩展性。

细粒度的RPC型的接口需要每个应用程序和服务都知道它们是如何同其它应用程序和服务进行通讯的。例如,一个订单接口上可能有诸如getQuantity()、getTotal()或getBillToAddress()等方法。

粗粒度、文本型(document-style)的接口是指,每个应用程序或服务只需要关心封装的XML文档,该XML文档包含所有相关的信息,服务可以与其它感兴趣者共享这些信息,并可以以SOAP envelope的形式将这些信息发送给他们。同样,在使用服务时,我们可以从XML文档选择相关的数据(例如<ShipTo>地址),进行相应的处理,然后将结果发送到其它目的地。当你将异步交互同文本型、粗粒度的接口结合起来时,你就为消息传递形式的调用做好了主要准备了。


				图2.
图2. 消息队列
得到可靠的异步通讯的最明智的方法就是运用基于JMS的消息队列。JMS是个标准,它提供了一组通用API让应用程序使用,并提供了一组通用的消息传递语义,如确保消息传递、消息一定会被传递且仅传递一次(once-and-only-once delivery)。JMS将其性能和执行原则封装在自己的层中,这个层是同应用程序层分离的。一个将基于JMS消息传递系统作为传输机制的应用程序不需要关心消息是如何传递的。SOAP消息可以在发送者和接收者之间列队等待(见图2)。JMS提供者负责处理异步、消息持续性、交易完整性和接收确认等所有方面。

Java 2 Platform、Enterprise Edition(J2EE)架构图总是将JMS看做是个隐藏在防火墙后面的组件,通过它我们在一个JSP servlet引擎和消息驱动的bean之间异步地传递消息。如果你在制作Web页面并将它们同中间层商业逻辑结合起来,那么这个架构就是个有效的用例,但这并不是运用消息传递的唯一的方式。

JMS消息传递机制是跨Internet传递数据、SOAP或其它信息的一个完全可行的解决方案。一个JMS机制可以在哪里部署以及如何部署的细节取决于供应商。许多JMS供应商都提供某种通道性能(tunneling capabilities)。例如,在SonicMQ例子中,JMS客户端到JMS服务器的通讯可以很容易地跨越公开的Internet、跨越防火墙、以一种运用HTTP、HTTPS、SSL或TCP sockets的安全的、可靠的方式进行。

将JMS作为一种SOAP传输机制
自1998年被引进到业界以来,JMS就作为一种基于标准的消息传递方法,在应用程序之间传输SOAP消息。它有五种消息类型,可以传递任何类型的数据(基于XML或不基于XML)。XML数据可以很容易地作为一个JMS BytesMessage中的二元数据或作为TextMessage中的串数据(string data)来传递。

不管对话两端运用何种API,我们把传输层上运用的JMS看做是除了普通的、老的HTTP传输之外的另一种可选方法。在我们的例子中,我们将JMS的API和Apache SOAP结合起来了——运用JMS API来连接、发送和接收消息,运用Apache SOAP来建构(construct)和析构(deconstruct)SOAP envelope。在不久的将来,JMS将作为一个传输层被嵌入到Apache Axis中。JMS将会成为一个部署选项。

JMS也有一个内置的同步请求/回应模式。这个请求/回应模式也可以异步地工作。请求者不需要block请求,等待即时的响应。响应可以在稍后的时间异步地发送。JMS可以使最初的请求消息同相应的响应消息关联起来,即使请求和响应在时间上是分离的,或者即使请求过程在失败后又恢复正常。

XMethods中列出的BabelFish服务给AltaVista的很受欢迎的BabelFish翻译工具提供了一个典型的PRC SOAP接口(见资源)。下面我们来看一下这个Web service的一个异步版本,称为AsynchBabelFish。

从根本上说,该服务主要是异步交换SOAP文档。客户端给服务发送一个AsynchBabelFish转换请求文档,一段时间后,服务将一个AsynchBabelFish转换结果文档发送回客户端。交换是在消息队列上完成的。从这个简单的例子,我们可以看到的一个明显的好处就是,即使服务出了问题,客户端也不会受影响;它可以将请求放到服务队列中,该请求是肯定可以被完成的——即使不是在现在,在稍后时间服务恢复正常时就可以完成。而且,运用一个异步模式也可以给客户端和服务器提供很多的灵活性,使它们可以控制消息的处理速度,例如,服务器可以从服务队列取出消息,按自己的意愿随时处理它们。

在客户端和AsynchBabelFish服务之间可能交换了三个文档:从客户端发送到服务的转换请求文档、从服务发送回客户端的转换结果文档、以及如果请求出了问题或如果转换请求由于某种原因不能完成,发送回客户端的一个替代结果文档的SOAP fault envelope。

要对AsynchBabelFish服务提出一个请求,客户端需要将转换请求文档创建成一个字符串,并将它作为一个TextMessage列在AsynchBabelFish服务请求队列中。Envelope采用的形式见列表1

转换请求envelope的body部分包含SourceText元素,它带有用户想要转换的文本。Xml:lang属性为文本语言指定了编码,traslateTo属性为用户想将文本转换到的语言指定了编码。所支持的语言包括English(en)、German(de)、French(fr)、Italian(it)和Portuguese(pt)。对于源文(source text),有150个字的限制。

注意SOAP header中运用的WS-Security header元素。AsynchBabelFish服务要求对XMethods用户ID/密码数据库进行验证,该元素用来提供请求者的身份凭证(credentials)。(关于WS-Security的更多信息,请参阅资源。)转换结果envelope的body部分包含两个子元素,SourceText和TranslationText。例如:
<?xml version="1.0" encoding=
	"UTF-8"?>
<soap:Envelope xmlns:soap=
	"http://schemas.xmlsoap.org/
	soap/envelope/">
	<soap:Body>
		<SourceText xml:lang="en"
			xmlns:xml=http://www.w3.org/
			XML/1998/namespace
			xmlns="http://xmethods.net/
			babelfish">hello</SourceText>
		<TranslationText xml:lang="fr" 
			xmlns:xml=
			"http://www.w3.org/XML/1998/
			namespace"
			xmlns="http://xmethods.net/
				babelfish">bonjour
		</TranslationText>
	</soap:Body>
</soap:Envelope>

SourceText只是重复传递到请求的内容。TranslationText包含转换的文本及其语言的xml:lang指示符。如果请求envelope有问题,从而不可能完成请求,就会返回一个标准的SOAP fault envelope来替代转换结果文档。下面的例子显示的是SOAP fault envelope;如果请求消息带有的TextMessage不是格式规范的XML,那么这个特殊的fault就会被发送回客户端:
<?xml version='1.0' encoding=
	'UTF-8'?>
<soap:Envelope xmlns:soap=
	'http://schemas.xmlsoap.org/
	soap/envelope/'>
	<soap:Body>
		<soap:Fault>
			<faultcode>soap:Client
			</faultcode>
			<faultstring>Incoming text 
				is not XML</faultstring>
		</soap:Fault>
	</soap:Body>
</soap:Envelope>

通过一个基于JMS的Web service(如AsynchBabelFish),客户端和服务器就不是直接进行交互的了;取而代之的是,SOAP请求可以通过消息队列在客户端和服务器之间进行传递,而不受时间的影响(time-independent)。

现在,让我们看看一个消息是如何从客户端传递到服务器的(见图3)。客户端创建一个转换请求envelope,并将它放在该服务的请求队列中。服务的所有客户端都可以运用这个众所周知的公用队列,如同基于HTTP的服务运用固定的、众所周知的HTTP端点URL一样。客户端也给请求消息提供了一个reply-to JMS header,它包含一个响应队列的名字,从而让服务知道将响应消息发送到哪里。AsynchBabelFish服务过程是否立即完成同该步骤(将请求放入队列)是无关的。


				图3.
图3. 请求消息的过程
然后,服务从队列得到请求。该步骤可以是在客户端放置请求后立即实现的,也可以在稍后时间完成,取决于服务器什么时候做好准备接收请求。然后服务器处理请求。如果请求在某些方面是无效的——例如,它可能不是一个有效的请求文档,或它可能用了不被支持或无效的语言编码——那么它就会生成一个SOAP fault envelope,其中详细说明出了什么问题。否则,服务就执行转换并生成适当的结果文档。如果在转换过程中出现与请求本身的有效性无关的技术错误,那么请求就保留在队列中,服务定期重试它们。最后,服务器就通过响应队列给客户端返回一个响应消息(见图4)。

尝试
服务将转换结果文档(或SOAP fault)放在JMS reply-to指定的响应队列中。它包含JMS请求消息的ID,并设置相关的响应消息ID来匹配它,因此客户端就可以根据其意愿进行相应的请求/响应操作了。然后,在需要的时候,客户端就可以从响应队列得到完成的转换结果文档了(或SOAP fault)。


				图4.
图4. 处理响应消息的过程
你自己可以尝试调用AsynchBabelFish服务。(要访问AsynchBabelFish的client package和相关的JMS client libraries,请参阅www.xml-mag.com上的在线资源。)这个包(package)包括客户端源代码、二进制代码和它所依赖的第三方类库(libraries)。要运用这个客户端,你也需要创建一个XMethods帐号并请求你自己的响应队列(XMethods给它的用户提供了一个作为messagebox的响应队列,它是对该服务以及未来引发的异步服务所做的SOAP响应)。客户端下载包所包含的指南对这些步骤的每一步都提供了更详细的说明。Client package中的源程序文件在表1中有简要的说明。

TranslatorRequestor可以让你创建请求并将它发送到AsynchBabelFish请求队列中(见列表2)。其运行如下所示:
java asynchBabelFish.
	TranslatorRequestor <username> 
	<password> <text> <fromLanguage> 
	<toLanguage> <responseQueue>

让我们来看看TranslatorRequestor的一些重点。首先,程序实例化一个到AsynchBabelFish服务队列的JMS连接。然后实例化一个RequestEnvelope对象:
RequestEnvelope requestEnv=
	new RequestEnvelope(userID,
	password,sourceText,
	sourceLanguage,toLanguage);

RequestEnvelope对象带有你传递的参数并在内部构造一个SOAP envelope。然后,当你调用它的toString()方法时,它就会以字符串形式给你返回一个相应的转换请求SOAP文档。我们用这个字符串创建一个JMS TextMessage对象:
TextMessage message=
	queueSession.createTextMessage(
	requestEnv.toString());

将响应队列的句柄(handle)添加到消息的JMS reply-to header:
 // Get a handle to the reply-to 
// queue
Queue replyToQueue=queueSession.
	createQueue(replyToQueueName);

// and add it to the reply-to 
// field
message.setJMSReplyTo(
	replyToQueue);

形成的结果消息就会在控制台打印出来并发送到服务队列。在这个例子中,你可以看到,被调用的TranslatorRequestor程序用了一个我们设置的测试帐号“joe”和一个测试响应队列(testResponseQueue)(见图5)。


				图5.
图5. 请求转换
然后服务器处理消息,返回一个转换结果文档或一个SOAP fault。ReplyProcessor程序负责处理响应消息(见列表3)。在这个例子中,我们用了一个与请求程序不同的程序来处理响应消息,尽管这并不是必需的。ReplyProcessor的调用方式如下所示:
java asynchBabelFish.
	ReplyProcessor <username> 
	<password> <responseQueue> 

程序首先建立一个到命令行参数所指定的响应队列的JMS连接。然后它将自己注册为那个队列的一个listener。在典型的JMS消息传递模式中,传送的消息被反馈到ReplyProcessor 的onMessage()方法。

得到有趣的信息
对于进入响应队列的每个新消息,onMessage()都从消息中读取SOAP envelope(以一个TextMessage的形式),然后将来自TextMessage的字符串传递到ResponseEnvelope构造器中:
ResponseEnvelope response=
	new ResponseEnvelope( 
	((TextMessage)message).
	getText());

ResponseEnvelope(见列表4)是个辅助类(helper class),它可以让我们更容易地处理转换结果文档。它的构造器以字符串形式读取SOAP XML,并实例化一个Apache SOAP Envelope对象,它用get()方法处理这个对象来提取有趣的参数(源文本、转换文本等等)。它的toString()方法也可以让你得到SOAP消息的源XML文档。然后响应消息和SOAP文档本身的相关ID(correlation ID)就被打印出来了:
// The correlation ID matches the 
// JMS Message ID of the original 
// request message
System.out.println("\nResponse: 
	JMS Correlation ID = "+message.
	getJMSCorrelationID()+"\n");

// Print out the response SOAP 
// envelope
System.out.println(response);

最后,从转换结果文档中打印出我们感兴趣的四个参数:源文本、源语言、转换文本和转换语言:
 // And print out a summary of the 
// translation.
System.out.println("\""+response.
	getSourceText()+"\" (
	"+response.getSourceLanguage()
	+") translates to \""+response.
	getTranslationText()+"\"    (
	"+response.
	getTranslationLanguage()
	+")\n\n");;

如果传送的消息的确产生了一个SOAP fault,而不是一个有效的转换结果文档,那么要实例化ResponseEnvelope就会抛出一个异常,会从SOAP fault中形成错误字符串,并打印出对那个异常的堆栈跟踪(stack trace)(见列表5)。当程序对我们先前用TranslatorRequestor发送的请求的响应文档进行处理时,你就可以看到程序的结果(见图6)。


				图6.
图6. 处理响应消息
RPC机制限制了Web services的有效性。异步消息传递以及它同Web services整合的价值就在于,SOAP和更高级的Web services协议是独立于传输机制(transport-neutral)的;对于分布式计算(distributed computing)来说,没有通用的(one-size-fits-all)方法。JMS提供了可靠的异步传输,这种传输可以与其它受欢迎的SOAP堆栈一起(或隐藏在这些SOAP堆栈下)跨Internet实现。

不管你选择哪种SOAP客户端类库,你都需要考虑如何运用异步消息传递来在企业内部或跨企业实现有效的数据传输。AsynchBabelFish例子可能并不是你在日常工作中使用或实现的那种服务,但它的确说明了SOAP是如何同JMS整合在一起的。


关于作者:
David Chappel是Sonic Software的副总和主要技术传播者。他也是Java Web Services (O’Reilly & Associates, 2002)、Professional ebXML Foundations (Wrox, 2001)和The Java Message Service (O’Reilly & Associates, 2000)的合著者。最近,他获得了Java Pro杂志的“Java Community个人杰出贡献奖”(请访问www.sonicsoftware.com)。Tony Hong是Xmethods的合著者,是Web services目录的出版商(请访问www.xmethods.net)。Dave的联系方式是chappell@sonicsoftware.com,Tony的联系方式是thong@xmethods.net
posted on 2005-08-22 09:19 笨笨 阅读(2490) 评论(0)  编辑  收藏 所属分类: ALLWeb Services

只有注册用户登录后才能发表评论。


网站导航: