在 SOAP 消息的增量解析和处理模式下,SOAP 发送者可能生成了一个非常冗长的
SOAP 消息,由于等到完整接收这个冗长的 SOAP 消息后再处理会造成大量的技术时间的浪费,并且将延迟响应速度,因此就需要发送者具备增量发送的能力,而接收者具备增量接收的能力。当
SOAP 接收者在接收消息的时候,使用一个能够增量处理消息体的 SOAP 处理器来实现这一目标 (
一般的,当消息体开始被接收的时候,开始使用一个 SAX 风格的 XML 解析器来处理 )。
这种方法对于内存有限的 SOAP 处理器而言是特别的有效,同时对于那些面向增量的实时数据传输的服务而言也是非常的有效。另外它还适用于这样的应用场景:消息具备冗长的消息体,同时这些消息体能够通过
SOAP 处理器模块直接被转换成应用程序中的数据结构和事件。事实上,此时我们并没有必要为这些数据额外地构造相应的
DOM 模型,SAX 方式的处理更为适合。在增量处理的情况下,对于 SOAP 数据模型的支持仍然是可能的,
如何这些模型是能够被增量地构造的话。
概括地说,本节所描述的 SOAP 消息的增量解析和处理模式需要接收者能够增量地解析和处理
SOAP 消息。这是一个适用于有限内存的处理环境的一个通用模式。如果 SOAP Body 包含了大量的数据,同时在具体应用中适合分段处理,那么通过
SAX 解析器采用增量处理模式是非常贴和需求的。下面的代码展示了一个可分段处理的 SOAP 请求消息示例,其中使用了
BodyDataChunk 元素来表示不同的数据段。
<env:Envelope xmlns:env="http://www.w3.org/2001/12/soap-envelope">
<env:Header>
<!-Set of headers processed before Body -->
</env:Header>
<env:Body>
<b:BodyDataChunk xmlns:s="http://example.org/2001/06/chunking">
<b:DataLength>1024</b:DataLength>
<b:Data>kfkk34jkhfSomeBase64EncodedDatajdsgkjgjajgo34093589uvsjv
…………… jhfjhf350giqhf</b:Data>
</b:BodyDataChunk>
<!- More BodyDataChunk elements -->
<b:BodyDataChunk xmlns:s="http://example.org/2001/06/chunking">
<b:DataLength>1024</b:DataLength>
<b:Data>oqjrj45cmoLastLotOfBase64EncodedData12r9vnhofjhckzlmxjws
…………… skfjk23ogkkjhq</b:Data>
</b:BodyDataChunk>
</env:Body>
</env:Envelope>
此时,如果 SOAP 请求是以流 (stream) 的方式被传输并被增量处理的话,那么相应的响应消息也可以以流的方式被传向初始的请求发送者。在这样的应用实例中,对于接受应用的设计,我们需要在实时性和错误处理方面格外加以重视。
如果 SOAP 请求的 Header 条目引发了错误的话,那么
SOAP Fault 将被插入到响应消息中,同时请求消息的处理也同时被中止。SOAP 消息的接受处理程序将会把每个
BodyDataChunk 元素视为一个原子。处理中,将视每个 BodyDataChunk 元素被成功处理与否,而将积极的或消极的确认加入到
SOAP 响应的流 (stream) 中去。当 SOAP 请求消息被处理完毕之后,SOAP 响应消息的生成处理也同时被完成。另一个可选择的方案是,SOAP
接受处理程序可以当 SOAP 请求被完整接受完毕再处理每个 BodyDataChunk 元素。如果在接受之中发生错误,那么一个
SOAP Body Fault 元素将被加入到 SOAP 响应流中,同时 SOAP 请求的处理被中止。在缓存
(Caching) 模式下,一些应用程序因为效率上的原因,例如为了获得更快的响应时间、占用更小的带宽或一些其他的方面,而进行可能的缓存操作。为了实现这一点,我们需要在相关的环境中设置缓存机制。例如,"读"缓存可以被
SOAP 中间介用于保存消息,以备在请求 / 响应消息交换模式的响应阶段重用。这样的缓存机制可以作用在整个消息的范围,也可以作用在一个
SOAP 模块的范围,或是作用在个别的 SOAP 模块的元素上。类似的,当在请求 / 响应消息交换模式下
( 当然也可以是其他的消息交换模式 ),请求消息不需要被立即转发或者响应的话,"写"缓存就变得有用了。这种缓存机制同样可以作用在整个消息的范围,也可以作用在一个
SOAP 模块的范围,或是作用在个别的 SOAP 模块的元素上。对于能够作用在不同元素上的缓存机制而言,它可以使用一个属性来关联目标缓存元素,具体的,属性可以通过
XML Query 的表达式或是 XPath 的表达式,在消息头或是文档中描述目标元素。同时缓存机制除指定目标结点外,还要制订一些其他的缓存参数,诸如生存时间
( 增量时间 )、失效时间 ( 绝对时间 )、实体有效性、时间有效性、失效服务的订阅以及对象更新和清理等。最后,一些应用程序还要求缓存机制具备描述消息元素之间的依赖和关系的能力。例如,一个响应消息中的元素可能适用于一个较大范围的请求消息,如果能够描述该元素与那些请求元素的关联关系将会对处理有帮助,如此,在应付这些请求的时候,我们就可以使用一个经济的方式
( 使用缓存元素 ) 来给出响应消息。缓存机制经常在分布式系统中作为优化机制而使用。它可以用于避免重复执行相同的计算操作,或是避免当结果集在一段时间内持续有效的前提下重复执行复杂的数据库访问。此时,一系列针对相同信息的请求可以使用被缓存的版本来响应,而无需重复地处理并占用系统开销。缓存机制的另一个用途则是针对数据的传输,应用了缓存机制之后,数据的副本可以存于叶节点服务器以方便本地的服务,而无需重复地访问中央信息库。如此,不仅加快了对信息的访问,同时缩减了对网络带宽的占用,同时还减轻了中央服务器的负载。缓存机制可以以底层传输架构的一部分被提供使用,不过在本节描述的应用模式中,我们假设这个缓存机制是与任何底层传输相独立的。
本应用模式的一个例子是,当一系列请求可以安全地以相同的结果返回的时候,缓存对应于该请求的响应消息。BizCo
是一家销售软件产品的在线商场,它每天上午 8:00 更新它的在线产品报价数据库,那么在上午 8:00
之后访问该商场的 SOAP 报价服务的远程用户都可以缓存报价消息直至第二天上午 8:00。如果远程客户需要重复访问相同产品的报价的话,它就不需要重复地访问远程的
BizCo 的 SOAP 报价服务,而只需要从本地的缓存中使用缓存下的版本。在本地缓存中的所有数据在第二天的更新时间
(8:00) 之前会被全部清空。图 1 展示了一个可能的实现体系架构。
其中,SOAP 应用 A 初始化了一个对产品价格目录的询价请求,具体的
SOAP 消息如下:
<env:Envelope xmlns:env="http://www.w3.org/2001/12/soap-envelope">
</env:Body>
<c:CatalogPriceRequest xmlns:c="http://example.org/2001/06/catalog">
<c:PartNumber>ABC-1234</c:PartNumber>
</c:CatalogPriceRequest>
</env:Body>
</env:Envelope>
缓存中间介 SOAP 应用 B 无法依靠其本地的缓存数据库来满足请求,因此它将该请求转发并最终到达询价目录服务
SOAP 应用 C。该服务处理了这个请求,并且将被请求的报价信息装配在响应消息中。在响应消息中,被添加了一个额外的
SOAP Header 条目,用于在返回的消息路径上控制所有的缓存服务模块。这个 CacheControl
SOAP Header 条目包含了一个 CacheKey 元素和一个 Expires 元素,CacheKey
元素被用于在以后的请求发生时,用于匹配被缓存的响应,而 Expires 元素则设置了本地的副本应当在何时被清理
( 失效 )。这个响应消息将通过缓存中间介被返回。
<env:Envelope xmlns:env="http://www.w3.org/2001/12/soap-envelope">
<env:Header>
<ca:CacheControl xmlns:ca="http://example.org/2001/06/cache">
<ca:CacheKey>ABC-1234</ca:CacheKey>
<ca:Expires>2001-03-09T08:00:00Z</ca:Expires>
</ca:CacheControl>
</env:Header>
<env:Body>
<c:CatalogPriceResponse xmlns:c="http://example.org/2001/06/catalog">
<c:PartNumber>ABC-1234</c:PartNumber>
<c:PartPrice c:currency="EUR">120.37</c:PartPrice>
</c:CatalogPriceResponse>
</env:Body>
</env:Envelope>
在缓存中间介上,CacheControl SOAP Header
条目被用于生成响应消息的本地副本,这个副本可以通过键值 CacheKey 来标识和访问。同时该副本将在
Expires 元素所描述的时间被清理出缓存。然后,CacheControl 元素被中间介从响应消息中剔除,而报价信息则被返回给最初的请求发送者。从最初的
SOAP 发送者到最终的 SOAP 接受者的完整请求 / 响应消息路径即为图 1 中的消息路径 1。
<env:Envelope xmlns:env="http://www.w3.org/2001/12/soap-envelope">
<env:Body>
<c:CatalogPriceResponse xmlns:c="http://example.org/2001/06/catalog">
<c:PartNumber>ABC-1234</c:PartNumber>
<c:PartPrice c:currency="EUR">120.37</c:PartPrice>
</c:CatalogPriceResponse>
</env:Body>
</env:Envelope>
当产品 ABC-1234 的报价信息被缓存在中间介的缓存数据库中后,后继的针对产品
ABC-1234 的询价 SOAP 请求就能够被中间介所满足了,此时,实际的 SOAP 消息路径就变成了图
1 中较短的请求 / 响应消息路径 2。发送非 XML 数据的模式一般适用于这样的应用背景:数码相机期望能够使用
SOAP 消息机制通过无线网络连接来将图象数据传送给远程服务器。在 SOAP 消息中将包含二进制图象数据
( 非 XML)。这个数码相机的应用实例呈现了一个特别的应用情况,此时从接收者到发送者的网络连接是无法建立的,这是因为数码相机这个设备不具备接收连接的能力,当然在其他类似的情形下,可能是因为防火墙的原因。对非
XML 数据的支持在一些相关的技术文献中已经被提及,"SOAP with Attachments"是一个由
W3C 接收的规范草稿,其中描述了如何使用类似 Email 附件的形式在 SOAP 消息交换中传输非
XML 的附件。在 SOAP 规范中也提供了 Base64 编码的数据类型,以进行少量的非 XML
数据的传输。其中"SOAP with Attachments"规范已经被 ebXML
消息服务规范接收为定义具备非 XML 数据支持的消息结构的基础架构。具体的,支持非 XML 数据需要有一些额外的消息包装机制,多片断
MIME 结构是一种适用的包装机制,这种机制将影响和作用于消息与底层通讯协议的绑定。在具体传输时,多个非
XML 的数据流将被表述成多个 MIME 结构,也就是形成了多片断的 MIME 结构。而图中实现的消息装载处理器将生成对这个多片断
MIME 数据包的一组引用标识,每个引用指向一个 MIME 片断部分,在 SOAP 消息正文中,将使用片断标识来引用这些
MIME 片断。
图 2 演示了如何使用多片断 MIME 来包装消息的不同组成部分。其中,最外层的
MIME 信封包装了一组 MIME 片断个体。第一个 MIME 片断包含了一个 SOAP 消息,这个
SOAP 消息包含了一个由消息装载处理器创建的"装载 SOAP Header 条目"。第二个及其后面的
MIME 片断所包含的消息负载可以是 XML 文档,也可以是其他的 MIME 内容类型的内容,诸如图象、声音或是视频数据等。"装载
SOAP Header 条目"可以包含引用独立 MIME 片断的引用标识,这个引用标识与
MIME 片断的内容标识是相一致的。在下面的例子中,我们是使用 XLink 引用来实现的。XLink
role 属性可以被用于进一步区分在消息负载中包含的数据的类型。
<env:Envelope xmlns:env="http://www.w3.org/2001/12/soap-envelope">
<env:Header>
<n:Manifest xmlns:n="http://example.org/manifest">
<n:Reference n:id="image01"
xlink:href="cid:payload-1"
xlink:role="http://example.org/image">
<n:Description>My first holiday photograph</n:Description>
</n:Reference>
<n:Reference n:id="image02"
xlink:href="cid:payload-2"
xlink:role="http://example.org/image">
<n:Description>My second holiday photograph</n:Description>
</n:Reference>
</n:Manifest>
</env:Header>
<env:Body>
-----
</env:Body>
</env:Envelope>