使用 IBM Integration Designer、WebSphere ESB 、WebSphere Process Server 和 Business Process Manager Advanced Edition 在中介模块中跟踪、记录日志并处理错误,第 2 部分

Web 服务中介中的错误处理设计策略

本文介绍使用 IBM® Integration Designer V7.5 创建的中介模块中的不同错误处理技术。这些运行时功能可同等地应用于 WebSphere® Enterprise Service Bus、WebSphere Process Server 和 Business Process Manager Advanced Edition V7.5 中运行的中介。

Frank I. Toth, 软件工程师, IBM

Frank Toth 在 IBM 从事过 4 年软件开发,并拥有 6 年客户支持经验。他的职责包括使用 WebSphere Integration Developer 解决客户报告的软件相关问题。



Dave Screen, 软件工程师, IBM

Dave Screen是一名软件工程师,目前是英国 IBM Hursley Software Lab IBM Software Services for WebSphere (ISSW) 的一名 BPM 顾问。有 9 年的从业经验,涉及领域包括 WebSphere、安全性、事务处理、Jave 运行时以及 Eclipse。



Callum Jackson, 软件工程师, IBM

Callum Jackson 是英国 IBM Hursley Software Lab 的 WebSphere ESB 团队的一名软件工程师。他自 2005 年加入 WebSphere ESB 团队,在那之前他从事电信行业 SOA 应用程序的软件服务。



2012 年 1 月 09 日

简介

中介模块是使用 IBM Integration Designer V7.5(以下简称为 Integration Designer)创建的,并部署在 WebSphere Enterprise Service Bus (ESB) 或 Business Process Manager (BPM) V7.5 Advanced Edition 上。WebSphere ESB 支持企业服务所需的转换和路由功能。ESB 解决方案通常执行消息和协议转换,以遵守服务提供者和客户端定义的接口定义。

本文的 第 1 部分 定义了可用的中介基元(后面简称为基元,它们以大写形式命名,比如 “Trace 基元”)和运行时特性,它们非常适用于日志和跟踪。在某些情况下,可能会有一个灰色区域,因为您可以将基元用于上述目的之一。

本文描述在中介流中处理错误的不同方法。除 V7.5 中新的 Error 流功能外,本文同样适用于 WebSphere Integration Developer、WebSphere ESB 和 WebSphere Process Server 的版本 7。模块和中介模块中的中介流组件也可以部署到 BPM Advanced Edition 中。

当中介流实现逻辑或调用现有提供者时,可以选择处理故障的方式。这些故障可能 “已建模”(例如 WSDL 接口上的已声明故障),也可能 “未建模”(例如系统故障)。本文将探索中介流中处理错误的各种方法,以实现正确的设计和配置。

当我们认为某种技术是领先技术或常用实践时,会在侧栏中对其进行说明。这些推荐技术并不适用于所有情况,本文主要描述可能的情况,以便使您能够选择满足您的要求的技术。


请求者、中介和提供者

中介组件执行的错误处理通常涉及将提供者(通常称为 “后端” 或 “服务提供者”)错误消息转换为业务域上下文中良好定义的消息。中介向请求者公开服务,如图 1 所示。这些组件还可以处理提供者系统错误(例如 “网络不可用”)并向请求者(或 “客户端”)提供更简单的错误信息。相关的术语和其他 ESB 讨论,请参阅文章 再论企业服务总线

图 1. 中介通常位于客户端和现有客户提供者之间
中介通常位于客户端和现有客户提供者之间

每个中介流模块都可能包含若干中介流组件。每个中介流组件都可能实现 WSDL 接口上定义的若干操作。每个操作实现都称为一个中介流。在中介流的执行(简称为 “中介流实例” 或 “流”)过程中,可能会出现不同类型的失败。


同步 Web 服务场景

本文的重点是同步场景,比如 Web 服务集成(参见 错误处理的常见使用模式)。在同步场景中,Export 和 Import 使用了一个 Web 服务绑定,如图 2 所示。

图 2. 通过 Imports 和 Exports 连接到外界的中介流组件
通过 Imports 和 Exports 连接到外界的中介流组件

对于本文,在谈论通过 Import(参见图 2)调用的对象时,我们使用了术语提供者。我们会使用术语中介 描述通过在中介流组件中执行中介流创建的应用程序。Web Service Export 将对请求者公开一个物理 Web 服务端点。

消息和行为的示例是特定于 Web 服务绑定的,比如将 SOAP 用作负载格式。因此,本文描述的大部分中介流设计模式适用于绑定(例如 SCA)或同步性 (synchronicity)。这包括错误处理子流、调用重试、基元和建模故障。

参考资料 部分包含了几个讨论异步场景的资料引用,其中一个场景是消息传递集成。尽管中介流内部的错误处理设计通常相同,但区别往往在于中介流失败时发生的事。对中介进行异步调用会生成一个 Failed Event(取决于配置),而不是会将失败响应发送给请求者。事务性 (transactionality) 超出了本文范围,本文仅在 迫使事务回滚 一节中涉及到这方面的内容。

这个运行时还有其他一些特性没在此处进行介绍,比如 Store、Forward 和 Failed Event Manager。在提供者不可用时,可以使用这些特性来中止处理(并在稍后恢复),或者修改并重播消息。这些特性只适用于以异步方式(比如通过从 JMS 队列使用消息)调用的中介。


错误处理的原因

本节描述执行错误处理的不同原因。

执行错误处理策略

可以将中介遇到的错误(比如提供者网络不可用或其他运行时异常)转换为简单的错误消息。如果原生组件没有记录这些错误,那么您可以使用一些中介层来记录根本原因,同时向请求者返回相应的响应消息,通知请求者临时中断服务。

处理特定于提供者实现的错误代码

服务提供者可以通过不同方法返回错误。这可能涉及 SOAP 故障或消息头和正文中的专有结构等问题。可以采用适当的转换规则,以一种独立于目标服务提供者的统一方式返回错误。

处理敏感信息

出现错误时,消息中通常包含敏感信息,比如使用的协议、服务器 IP 地址等。在跟踪或创建响应消息之前,需要使用适当的规则来过滤敏感信息。


中介流中的错误处理特性

本节将描述中介内部的故障建模和处理。

说明

建议在 WSDL 接口中声明与服务操作本身相关的异常条件,比如无效的客户标识符。

故障概述

请查看 Information Center 主题 中介流组件中的错误处理 中的一个功能说明。首先,我们将简要回顾一些概念。

使用提供者或定义中介时,我们使用 WSDL 接口。这些接口可以声明输入、输出和故障。WSDL 接口上声明的故障对应于定义为服务接口一部分的已知错误案例。

已建模故障和未建模故障

有两种故障类型:已建模(modeled)未建模(unmodeled)(参见图 3)。已建模故障对应于 WSDL 接口上定义的错误。未建模故障对应于没有在 WSDL 接口上声明的错误。

图 3. 包含两个已声明故障的 WSDL 接口
包含两个已声明故障的 WSDL 接口

然而,对于大多数接口定义而言,对与其接口相关的系统问题(例如网络不可用)进行建模是不必要、不建议并且不可能的。请求者或系统会处理这些问题。但是,在 ESB 技术集成层上工作时,有时可能需要将提供者系统问题建模为接口上的已声明故障。例如,防止调用者受到提供者失败影响的聚合器 (aggregator)。

实现中介时,处理好调用提供者时导致的故障非常重要。这可以通过连接节点和基元来实现,下面的小节将详细介绍相关内容(故障小结 小节提供了一个完整列表)。可能需要将故障创建为要公开的服务接口的一部分。相反,这通过连接到某些节点和基元实现。

使用已建模故障

下面介绍如何设计和使用已建模故障。

设计已建模故障

有几种不同的 “不愉快路径” 情况;在这些情况下,需要将故障消息返回给请求者。这些故障的分组方式可能由正在执行的服务合同来决定。尽可能 将故障归为少数几类,但前提是便于请求者处理。这样做是因为随着故障消息类型数量的增加,服务和请求者中需要的逻辑量也会增加。

有一些信息可能是所有故障消息公用的,所以可以将它们放置到一个公共业务对象定义 (XSD) 中,如图 4 所示。

图 3 中的接口中定义的两个故障类型都扩展一个公共类型,因为它们共享相同的故障数据。

图 4. 定义故障消息中使用的 Business Object 类型
定义故障消息中使用的 Business Object 类型

使用 Web 服务时,运行时使用类型信息来区分故障。

说明

  • 要实现最佳结果且在绑定和运行时之间实现最大兼容性,应为每种故障消息定义一个不同的类型。
  • 对每个操作使用一个输入和输出参数也是一个普遍做法。

还要注意,图 3 中的接口将所有输入和输出参数合并为两个业务对象类型。UpdateCustomerRequest 类型包含两个字段,可使用它们来处理客户 iddetail。与故障类型类似,这些输入和输出类型也继承自公共请求或响应类型定义。

创建已建模故障并将其发送给请求者

要发送一个已建模故障响应,流必须到达一个 Input Fault 节点 — 位于请求或响应上。对于 图 3 中的接口,有两个终端的消息类型对应已声明故障。如图 5 所示,单击小符号 “i”(蓝色圆圈部分),就会弹出一个黄色窗口。

图 5. Input Fault 节点上用于创建已建模故障的终端
Input Fault 节点上用于创建已建模故障的终端

消息正文(SMO 的 /body)必须拥有适用于该故障消息的类型。这可以通过一个 XSL 基元来实现,图 6 通过一个 invalidCustomerId 故障对此进行了演示。在这个映射中,三个输出节点已被填充,其中两个继承自 FaultCommon XSD 类型。id 字段直接从请求消息的 customerId 输入字段提取值。reason 字段的值是使用一个自定义 XPath 表达式 concat('Invalid customer Id : ',$customerId) 以及一些文本和相同的 customerId 字段来构建的。code 字段接受一个常量值。

图 6. 连接并映射以创建一个已建模故障
连接并映射以创建一个已建模故障

要显示已建模故障的 SOAP 消息,可以使用 Generic Service Client(选择 Web Service Port for the Export 时该选项位于 Web Services 上下文菜单中)测试这个服务,或者使用 TCP/IP Monitor 视图。

所有已建模故障的 HTTP 状态码都是 500。返回已建模故障并不会迫使事务(如果存在)回滚。

清单 1. invalidCustomerId 故障的示例 SOAP 故障消息(省略了大部分类型声明)
<soapenv:Envelope>
    <soapenv:Body>
        <soapenv:Fault>
            <faultcode>m:Server</faultcode>
            <faultstring>Invalid customer Id : 12345</faultstring>
            <detail>
                <io7:operation1Fault1_invalidCustomerId
                 xmlns:io7="http://Common/ProviderInterface">

                    <reason>Invalid customer Id :  12345</reason>
                    <code>INVALID_ID</code>
                    <id>12345</id>
                </io7:operation1Fault1_invalidCustomerId>
...

注意,消息类型中的故障类型必须用一个 “非 nill(non-nill)” 填充。Web Service 绑定要生成正确的故障信息,必须有一个 xsi:nill 值。这意味着 在一个复杂类型中至少指定一个数据项(如上面的清单 1 所示),或者在一个简单类型(例如故障类型 xsd:string)中填充一个值。

处理来自多个提供者的已建模故障

服务提供者定义的故障可能有不同的风格或命名方式,也可能不同于那些需要从服务返回的故障。图 7 展示了一个流,该流可能从两个不同的提供者接收故障,具体取决于选择调用哪个提供者。

图 7. 映射来自不同提供者接口的故障
映射来自不同提供者接口的故障

另外,必须将故障消息转换为即将公开的服务所需的格式。在图 8 中所示的映射中,映射了两个等价字段,并将一个常量值赋予 code 字段。

图 8. 将来自提供者故障的字段映射到响应故障消息
将来自提供者故障的字段映射到响应故障消息

上面的映射的根设置为 /body,这是因为对于已建模故障,信息包含在 SMO 正文中。由接收清单 2 中的 SOAP 消息导致的 Callout Fault 流的 SMO 跟踪列示如下,展示如何从 SOAP 故障细节填充 SMO 正文。/headers/SMOHeaders/SOAPFaultInfo 中的故障码也被填充,且该故障码与正文消息类型相匹配。

清单 2. 接收到的 invalidCustomerId 故障的 SMO 跟踪(简化版)
<smo>
  <context/>
  <headers>
...
    <SMOHeader>
      <MessageType>Exception</MessageType>
    </SMOHeader>
    <SOAPFaultInfo>
      <faultcode xmlns:ns1="http://Common/ProviderInterface">
          ns1:updateCustomer_invalidCustomerIdMsg</faultcode>
    </SOAPFaultInfo>
  </headers>
  <body xmlns:ns0="wsdl.http://Common/ProviderInterface" 
     xsi:type="ns0:updateCustomer_invalidCustomerIdMsg">
    <io7:operation1Fault1_invalidCustomerId xmlns:io7="http://Common/ProviderInterface" >
      <reason>Invalid customer Id : 12345</reason>
      <code>INVALID_ID</code>
      <id>12345</id>
    </io7:operation1Fault1_invalidCustomerId>
  </body>
</smo>

注意,在清单 2 中的中介流中,断开了两个 Callout Fault 终端的连接。这种行为等同于连接到一个 Stop 基元。

说明

如果本意是在一个输出或故障终端之后停止流,那么明确将终端连接到一个 Stop 基元会使意图更加明确。

但是,用来记录流的贴条 (sticky note) 可能拥有相同的功效。另外,三个基元的失败终端被断开连接。下面将进一步讨论 Stop 基元,以及如何使用 Error 流处理断开的失败终端。

中介流中的错误

处理消息的中介基元有一个失败终端(基元右下方的矩形终端),当基元中有一个失败时,失败终端会随输入消息一起广播异常信息。异常信息存储在消息上下文中的 failInfo 元素中。您必须将中介基元的失败终端连接到另一个基元,以便访问 failInfo,如图 9 所示。基元的失败终端提供关于当前基元的失败信息,最显眼的是,failureString 字段提供一条消息。

图 9. failInfo 结构
failInfo 结构

origin 字段提供基元名称,invocationPath 字段提供失败发生之前流经的基元的列表。

使用 Stop 基元实现简单的错误处理

可以使用提供的一些跟踪和日志基元实现简单错误处理。例如,如果某个基元在输入过程中由于不正确的输入数据或基元的某种处理故障而失败,那么可能只需记录输入数据并停止流程。

图 10. 在映射失败发生时使用一个 Stop 基元
在映射失败发生时使用一个 Stop 基元

在图 10 显示的流中,当映射失败发生时,会将输入消息记录到数据库中,然后使用一个 Stop 基元。对于 Web 服务交互,这样做的效果是返回一个正文为空的 SOAP 响应(HTTP 状态码为 200),前提是假定没有其他路径继续发送响应。使用 Stop 基元并不会导致事务回滚。

说明

如果可能,尽量返回一个有意义的响应,而不是使用 Stop 基元中止流。

注意,将一个输出终端(比如来自 Message Logger)连接到一个 Stop 基元等同于完全不连接它,但进行连接显然是一个良好实践。

合并错误逻辑和子流

“任意消息类型” 终端类型意味着这个子流可用于任何 WSDL 消息(参见图 11)。

在流中进行实例化时,子流看起来就像常规基元。

图 11. 能处理任何消息类型的子流定义
能处理任何消息类型的子流定义

说明

如果需要在多个地方重复一个等效逻辑,比如在同一个流中、同一个中介组件中、甚至在不同的模块中,那么可以考虑使用子流。可以将子流放置到库中,供多个项目使用。

可以提升子流中的每个基元的属性。在上面的示例中,提升 Trace 和 Message Logger 基元的 enabled 属性,该属性允许管理员选择每个子流实例的文件或数据库日志。

默认情况下,粒度水平非常好,但流设计师可以选择组合相关属性,降低粒度水平。这用于降低管理复杂性。流设计师可以将已跨所有子流实例提升的属性(比如 Trace 基元的 enabled 属性)归为一组并赋予相同的别名。这意味着,管理员可以在 Integrated Solutions Console 中在模块上配置一个已提升属性值,禁用嵌入式 Trace 基元的所有实例。

Error 流(V7.5 中的新特性)

您需要连接失败的终端,除非想要将默认技术错误消息与返回给调用者的失败基元相关联。这等同于使用一个 Fail 基元(稍后讨论)。您可以使用 Error 流(V7.5 中的新特性)自动 “连接” 每个未连接的失败终端。

终端被设置为 “任意类型”,这是因为消息可能拥有任何 WSDL 操作类型。泛型日志流(或子流)可用于处理所有失败。

如果想要采取的行为取决于流的哪个部分出错,那么应使用一个 Type 过滤器基元来选择适当操作,如图 12 所示。如果只有少量消息类型需要特殊处理,则这种方法比较实用。

图 12. 在一个 Error 流中处理多个失败
在一个 Error 流中处理多个失败

另外,可以通过检查 SMO 中的数据来确定要采取的操作。例如,如果来自调用的结果被请求流存储在一个上下文中,且需要基于该数据选择行为。可以结合使用一个 Message Filter 基元和一些 XPath 条件,选择一个适当的流来处理该失败。请参阅下一节和 故障小结 小节中的 表 2,深入了解 Stop 和 Fail 基元之间的区别。

说明

一种普遍做法是通过使用一个 Error 流或结合使用两种方法来连接中介中的每个失败终端。

处理未建模故障

本节将讨论如何创建并处理未建模故障。

调用提供者时,如果发生一个未建模(没有在接口上声明)的故障,比如一个未处理的运行时错误,那么这通常导致接收一个未建模故障。但是,具体情况取决于提供者的实现方式。

未建模故障表示未处理的错误情况,该错误会广播给(Web Service Export 的)调用者并导致一个事务回滚。

未建模故障被视为一种运行时错误,在日志中生成一个堆栈跟踪。如果用于常规或预期操作,可能会造成性能影响。

说明

建议将未建模故障的创建限制于真正的故障情况。

使用 Fail 基元创建

要使中介返回未建模故障,常用方法是不在基元上连接失败终端。

可以使用 Fail 基元返回一个未建模故障并提供用户提供的错误消息(参见图 13)。请参见 故障小结 小节 表 2 中 Fail 基元与 Stop 基元的比较。

图 13. 用于返回未建模故障和客户错误消息的 Fail 基元
用于返回未建模故障和客户错误消息的 Fail 基元

一条自定义错误消息被定义,占位符 {4} 使用位于 XPATH 位置 /body/updateCustomer/id 的 SMO 的数据填充。默认 Root 路径是 /context/failInfo,它包含来自前面的基元的失败信息。Information Center 主题 Fail 中介基元 记录了其他可用占位符。

清单 3. 表示未建模故障的示例 SOAP 故障消息
<soapenv:Envelope>
  <soapenv:Body>
    <soapenv:Fault>
      <faultcode>m:Server</faultcode>
      <faultstring> javax.xml.ws.WebServiceException: 
       com.ibm.websphere.sca.ServiceRuntimeException: CWSXM0201E: Exception returned by 
       mediation flow for component Unmodeled in module Diagrams: CWSXM3300E: Fail 
       primitive 'Map_Fail', component 'Unmodeled', module 'Diagrams', interface 
       '{http://Common/ProviderInterface}ProviderInterface', operation 'updateCustomer', 
       raised a FailFlowException. The user-supplied error message is 'Map failed for
       customer Id 12345'...

清单 3 中的 SOAP 展示了用户提供的错误消息正被填充。HTTP 状态码为 500。

使用 Fail 基元或使用已建模故障,不可能精确控制 SOAP 消息的格式(比如 faultstring)。如果需要精确控制它,那么应该使用一个 JAX-WS 处理程序来修改位于边界的 SOAP 消息。或者,使用一个服务网关(稍后讨论)来手动构建 SOAP 信封。

处理未建模故障并向请求者发送已建模故障

图 14 中的流展示如何通过从 Service Invoke 基元的失败终端进行连接以处理未建模的故障。这样做的原因有以下几点:

  • 无法发送请求或接收响应(例如,HTTP 状态码 404 表示找不到 URL)。
  • 响应不是 SOAP(例如,纯文本和 HTTP 状态码 500)。
  • SOAP 响应不符合预期的响应格式。
  • 有效的 SOAP 包含一个未建模故障(例如,未建模和 HTTP 状态码 500)。
图 14. 将未建模故障转换为已建模故障
将未建模故障转换为已建模故障

“Provider error” 映射创建了一条适合两个 Input Fault 终端(每个终端都表示一个已建模故障)之一的消息。这个特殊的已建模故障表示一个提供者失败。

未建模故障和失败终端的失败消息包含在 XPATH 位置 /context/failInfo 的 SMO 中。在图 15 中的示例中,failureString 被复制到输出映射的某个字段中,并通过 Input Fault 终端作为一个已建模故障的一部分返回。

图 15. 从 failInfo 结构进行映射
从 failInfo 结构进行映射

接收到的 SOAP 故障消息作为一个字符串记录到 /context/failInfo/failureString 中。向调用者返回完整的 SOAP 故障消息的另一种方法是在一个数据库中记录它,然后向请求者返回一条相关消息。

说明

一种典型方法是同时返回人类可读文本和一个错误码,尽管该方法并非适合所有情况。在用户使用多种书面语言的情况下,使用错误码比较适合。

Callout Response 节点的行为与 Service Invoke 基元相同。

通过处理未建模故障,提供者造成的失败不会导致事务回滚。提供者失败消息是通过设计进行调节的。如果需要保留 SOAP 格式,则需要使用一个服务网关(稍后讨论)。

故障以及 Stop 和 Fail 基元的汇总

本节将小结各种故障的行为,并比较 Stop 和 Fail 基元。

表 1. 已建模和未建模故障之间的区别
故障类型已建模故障未建模故障
在 WSDL 中定义
工作负载 良好定义,在 Service Message Object (SMO) 正文中 未定义,在 SMO 头中
典型用途 业务或用户定义 面向系统
Java 等价物(例如在日志中)ServiceBusinessExceptionServiceRuntimeException
通过在连接源头处进行处理 Callout Fault 节点或 Service Invoke 基元上的每个故障的已命名终端 Callout Fault 节点或 Service Invoke 基元上的失败终端
通过在连接目标处进行创建 Input Fault 节点上的已命名终端 Fail 基元或系统失败,比如网络
如果不处理会出现什么后果 就像一个 Stop 基元,流或分支结束但没有错误 就像一个 Fail 基元。广播到调用者。事务回滚。
用于什么情况 常规 “非愉快路径” 异常情况 仅当需要时用于失败情况
表 2. Stop 和 Fail 基元之间的区别
基元Stop Fail
用途 终止流或流的分支 仅当需要时,用于失败情况
当中介由 Web Service Export 驱动时在流上的效果 终止当前分支。如果当前分支是唯一分支,则创建正文为空的 SOAP 响应消息。 终止所有分支并创建一个未建模故障 SOAP 响应。
与哪种终端具有相同效果 一个未连接的故障或输出终端 一个未连接的失败终端
对事务的影响 回滚
导致一个异常/故障ServiceRuntimeException / 未建模故障

高级主题

本节描述能在更复杂的集成场景中提供有帮助的主题。

重试一个 Web 服务调用

WebSphere ESB 内置了重试失败的服务调用的功能。Service Invoke 基元和 Callout 节点有一些选项,允许在指定的最高次数限制内重试已建模故障或未建模故障(参见图 16)。

图 16. 配置 Service Invoke 基元上的重试
配置 Service Invoke 基元上的重试

这里的属性都可以得到提升。这意味着可以向管理员公开它们,以便在应用程序运行时进行相应的动态修改。

在正常操作中,中介流的请求和响应跨越的总时间不应超过服务器上定义的事务超时(默认值为 120 秒)。这包括每个中介流实例中的调用、重试延迟和其他处理。

说明

如果可能,应尽量将中介流实例设计为短暂存续的。这能减少资源争用,比如线程占用和数据库连接。

使用替代端点

如果您想在故障发生时使用替代端点(Web 服务是 URL),那么必须在调用之前使用这些替代端点填充 SMO(参见图 17)。

图 17. 调用之前在 SMO 中设置替代端点
调用之前在 SMO 中设置替代端点

运行时循环通过这些端点,首先从默认 /headers/SMOHeader/Target/address 开始,然后是 “替代端点”,直到成功或失败。

标准方法是结合使用 Endpoint Lookup 基元和 WebSphere Service Registry and Repository 来检索端点。

在特定的已建模故障之后重试调用

有时,内置功能不足以满足您的要求。例如,当您想在接收某个特殊已建模故障(例如描述一个临时或未知提供者失败的故障)而不是其他已建模故障之后重试一个调用时。

您可以使用 Fan Out 基元进行循环,直到条件满足,或者到达指定的次数上限。在本例中,它用于重试调用。

在本例中,假设我们想重试任何未建模故障和某个特定的已建模故障 UnknownSystemException,该故障已在提供者的 WSDL 接口中进行声明,表示一个可能比较短暂的提供者错误。我们最多会拥有三个调用,也就是说,最多有两次重试机会。

在图 18 中,针对两个突出显示的路径进行了一次重试。上面的路径用于 UnknownSystemException 已建模故障,下面的路径用于接收到的未建模故障。

图 18. 以迭代模式使用 Fan Out 基元以实现重试
以迭代模式使用 Fan Out 基元以实现重试

重试路径被连接到 Fan In 基元的 “in” 输入终端。该终端是导致 Fan Out 再次触发并循环的终端。如果三次迭代完成且数据流已经到达 Fan In 基元的 “in” 终端,则会触发 “out” 终端。在这个场景中,这意味着已经有三次不成功的调用。

其他路径(OK 响应和剩余的已建模 “Business” 故障)被连接到 Fan In 基元的 “stop” 终端。这个终端会导致 Fan Out 迭代停止(到达迭代总次数时或之前),并触发 “incomplete” 输出终端。在这个场景中,这意味着一次成功的调用或重试尝试。

要使上面的示例起作用,则需要一点 “引导(bootstrapping)”。这是因为 Fan Out 基元的迭代功能只设计用于循环一个输入数组(对于一个汇总场景)。要将其用于循环目的(比如最多循环三次),需要构造一个长度为 3 的数组,将它作为 Fan Out 基元的输入。使这个示例工作的步骤如下:

  1. 声明一个带有字段 loop 的 BO,这是一个字符串(或任意类型)列表或数组。
  2. 将这个 BO 设置为临时上下文的类型。
  3. 在 Properties 中,将 Fan Out 设置为针对 XPath 表达式 /context/transient/loop 中的每个元素而触发输出终端。将 Fan In 设置为在相关 Fan Out 基元迭代完所有消息时触发输出终端。
  4. 使用 Message Element Setter 来填充 loop 字段,使用的数组元素的数量为调用所需的最大数量(参见图 19)。这需要在 Fan Out 基元之前完成。
图 19. 填充驱动 Fan Out 迭代的输入数组
填充驱动 Fan Out 迭代的输入数组

Fan Out 循环可以通过改进的属性(例如总重试)变得更动态。loop 只需使用任何配置的绝对最大调用数量填充数组即可(Java™ 代码也有效)。

另一种选择是直接连接重试,以便在流上拥有多个 Service Invoke 基元(重试数量 + 1)。这在一次重试之后会变得非常混乱。

错误处理网关

有时,来自提供者的消息的特殊格式与 ESB 不相关。可能的情况是,所有失败消息(HTTP 状态码 500)都应该以类似的方式处理,Service Gateway 模式(打开 Patterns Explorer 视图)支持这种处理方式,无需导入和连接到各个服务提供者的所有 WSDLs 和架构(参见图 20)。

图 20. 动态 Service Gateway 模式
动态 Service Gateway 模式

这个网关位于请求者和提供者之间,通常通过使用入站消息的一个著名属性(比如一个 SOAP Action HTTP 头部)来进行路由决策。它可以充当不同服务应用程序的网关,无需服务接口上的依赖项。

广播所有消息而不进行更改

由于 Service Gateway 模式将 SOAP 消息作为纯文本处理,所以非常适合用它将故障消息从提供者直接返回给请求者而不进行任何更改。图 21 显示了这样一个网关的响应流,网关的用途是记录对几个提供者的失败调用。这里的关键优势是提供者可以拥有不同的接口,网关不需要导入 WSDL。

图 21. 使用一个 Dynamic Service Gateway 记录所有失败消息
使用一个 Dynamic Service Gateway 记录所有失败消息

Dynamic Service Gateway 不对其调用的服务使用 WSDL 接口定义。这意味着没有已建模故障的概念,有效的 SOAP 消息描述的所有失败(HTTP 代码 500)都出现在 Callout Fault 节点的 gatewayMessage 终端上。

如果有一个系统出现相关失败(例如 HTTP 代码 404)或没有返回有效的 SOAP 消息,则会触发 Callout Response 节点的 Fail 终端。在图 21 中,Fail 终端最终连接到某个 Fail 基元,后者用于将一个未建模故障发送给请求者。

捕获所有失败消息

Service Gateway 模式的另一个用途是有条件地提供直通功能(pass through)。这里描述的模式充当成功的服务提供者响应调用的直通功能。但是,它将通过转换中介调用故障和 SOAP 故障消息。这对于防止提供者失败信息很有用,比如网络细节或返回客户端的异常跟踪。在某些情况下,可能需要使用统一的消息格式处理所有失败。

失败的提供者调用的网关响应的选项包括:

  • 正文为空的 SOAP 响应(HTTP 200)
  • 带有一个已定义消息类型的常规 SOAP 响应(HTTP 200)
  • SOAP 故障响应

第一个选项可以使用一个 Stop 基元轻松实现。

第二和第三选项在网关场景中比较难以实现,这是因为流中可用的 ServiceGateway 接口不适合工作负载的强定义类型。这通常能体现 Service Gateway 的好处。SMO 包含以 “字符串/XML” 格式表示的 SOAP 信封原始数据。

有两种机制可用于处理每种相应类型:

  • 手动构造
  • 将 Service Gateway 消息自动解析为具体消息

以下步骤展示更具冒险精神的开发人员如何手动构造响应。

手动构造:从 Callout Fault 创建令人愉快的 SOAP 响应

要创建与已定义的 WSDL 接口匹配的 SOAP 响应,首先必须填充匹配操作类型的消息正文,然后将其序列化为适合 Input Response 节点的字符串形式。

图 22 中的示例根据一个 SOAP 故障创建了一个常规响应。XSL 映射的输出类型匹配接口 MyGateway 上的响应操作(operation1ResponseMsg)。

图 22. 创建并填充一个已建模 SMO 正文类型
创建并填充一个已建模 SMO 正文类型

然后,Data Handler 将 SMO 正文序列化为一个 XML 字符串,该字符串形成了网关响应的基础,如图 23 所示。

图 23. 带有一个 TextBody 消息字段的 ServiceGateway 的 SMO
带有一个 TextBody 消息字段的 ServiceGateway 的 SMO

创建这个流的步骤如下:

  1. 创建一个 XSL 基元,从项目或其依赖项中可用的 WSDL 接口将 Output 终端类型设置为想要的响应消息类型。
    1. 使用 root=/body(或者 root=/,如果您想访问 SOAP Fault 代码的话)创建一个映射,以填充目标。

      或者,将 SOAP Fault 信息从 /headers(比如 /headers/SOAPFaultInfo/faultstring)复制到目标。

  2. 创建一个 Data Handler 基元:
    1. 将 Data Handler Configuration 设置为 UTF8XMLDataHandler
    2. 将正文 /body/message 调整为 {http://com.ibm.wbiserver.gateway/schema}TextBody 的实际字段类型。这是包含名为 value 的字符串字段的类型。
    3. 选择 Action,以便 “从 Business Object 转换到原生数据格式”。
    4. 源和目标 XPaths 分别为 /body/body/message/value

自动解析 Service Gateway 消息:从 Callout Fault 创建令人愉快的 SOAP 响应

在一个 Service Gateway 内,您可能想将入站消息从其一般 TextBody 结构扩展为具体的业务对象(没有 DataHandler 基元)。该任务在以下情况下是有可能实现的:在将一个包含 XML 数据的 TextBody 结构编码到网关消息(例如来自一个 Web 服务绑定的消息)内部 ,而且 Integration Developer 在中介流编辑器中的任一输入节点上选择了 Automatically convert the ServiceGateway message 复选框(参见图 24)。

图 24. 自动反序列化和序列化网关消息
自动反序列化和序列化网关消息

这种处理的一个要求是架构信息可用,以便将消息反序列化为具体的业务对象。

这种机制在处理故障消息时尤其有用,因为 SOAP Fault 的结构可能比较复杂,不适合手动处理。

例如,一条包含清单 4 中所示信息的 SOAP 1.2 故障消息。

清单 4. 表示未建模故障的 SOAP 故障消息示例
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<soapenv:Envelope xmlns:soapenv="http://www.w3.org/2003/05/soap-envelope">
  <soapenv:Body>
    <soapenv:Fault xmlns:axis2ns1="http://www.w3.org/2003/05/soap-envelope">
      <soapenv:Code>
        <soapenv:Value>soapenv:Receiver</soapenv:Value>
        <soapenv:Subcode>
          <soapenv:Value xmlns:m="http://Customer">m:CustomerIdFault</soapenv:Value>
        </soapenv:Subcode>
      </soapenv:Code>
      <soapenv:Reason>
        <soapenv:Text xml:lang="en"> Invalid customer Id : 12345</soapenv:Text>
      </soapenv:Reason>
      <soapenv:Detail>
        <io7:operation1Fault1_invalidCustomerId xmlns:io7=
            "http://Common/ProviderInterface">
           <reason>Invalid customer Id : 12345</reason>
           <code>INVALID_ID</code>
           <id>12345</id>
       </io7:operation1Fault1_invalidCustomerId>
      </soapenv:Detail>
    </soapenv:Fault>
  </soapenv:Body>
</soapenv:Envelope>

当该消息到达网关时,您希望将细节区域自动反序列化到 Service Message Object 的正文中,剩下的信息填充到 SOAPFaultInfo 区域中。根据上面的入站消息,这正是所发生的情况。这将生产下面清单 5 中所示的 Service Message Object。

清单 5. 表示未建模故障的示例 SOAP 故障消息
<p:smo xmlns:p="http://www.ibm.com/websphere/sibx/smo/v6.0.1">
  <context/>
  <headers>
    <SMOHeader>
      <MessageUUID>3E3BB9A4-0131-4000-E000-1F1809B4A751</MessageUUID>
      <Version>
        <Version>7</Version>
        <Release>0</Release>
        <Modification>0</Modification>
      </Version>
      <MessageType>Exception</MessageType>
      <Operation>requestResponse</Operation>
      <Action>http://Customer/customer/customer/updateCustomer</Action>
      <SourceNode>ServiceGatewayImport1</SourceNode>
      <SourceBindingType>WebService</SourceBindingType>
      <Interface>wsdl:http://www.ibm.com/websphere/sibx/ServiceGateway</Interface>
    </SMOHeader>
    <SOAPFaultInfo>
      <faultcode xmlns:ns0="http://www.w3.org/2003/05/soap-envelope">ns0:Receiver
       </faultcode>      <faultstring>Invalid customer Id :
                12345</faultstring>
      <extendedFaultInfo>
        <Code>
          <ns0:Value xmlns:ns0="http://www.w3.org/2003/05/soap-envelope">ns0:Receiver
           </ns0:Value>
          <ns0:Subcode xmlns:ns0="http://www.w3.org/2003/05/soap-envelope">
            <ns1:Value xmlns:ns0="http://Customer" xmlns:ns1="http://www.w3.org/2003/05/
             soap-envelope">
             ns0:CustomerIdFault</ns1:Value>
          </ns0:Subcode>
        </Code>
        <Reason>
    <ns2:Text xmlns:ns2="http://www.w3.org/2003/05/soap-envelope"
            xml:lang="en"> Invalid customer Id : 12345</ns2:Text>
        </Reason>
      </extendedFaultInfo>
    </SOAPFaultInfo>
    <HTTPHeader>
      <control>
        <ns3:URL xmlns:ns3="http://www.ibm.com/xmlns/prod/websphere/http/sca/6.1.0">
         http://localhost:9080/MyGateway_GatewayWeb/sca/ServiceGatewayExport1</ns3:URL>
      </control>
    </HTTPHeader>
  </headers>
  <body xmlns:ns0="wsdl.http://Customer/customer" xmlns:xsi="http://www.w3.org/2001/
    XMLSchema-instance" xsi:type="ns0:updateCustomer_invalidCustomerIdMsg">
   <io7:operation1Fault1_invalidCustomerId xmlns:io7="http://Common/ProviderInterface" >
           <reason>Invalid customer Id : 12345</reason>
           <code>INVALID_ID</code>
           <id>12345</id>
        </io7:operation1Fault1_invalidCustomerId>
  </body>
</p:smo>

这允许 Integration Developer 使用标准转换基元来更改消息的内容并生成一条有效的响应消息,如图 25 所示。

图 25. 创建一条常规响应消息
创建一条常规响应消息

注意,需要使用最后的 SetMessageType 基元将消息重置(从工具化角度看)为一个 Gateway 消息结构,而不是重置为所生成的具体类型。运行时只在 InputResponse 节点中而不是在 SetMessageType 基元处使用这个逻辑。

手动构造:根据失败创建 SOAP 故障响应

要返回故障响应,需要将来自 http://schemas.xmlsoap.org/soap/envelope/ 架构(假设您使用 SOAP 1.1)的类型为 Fault 的 BO 序列化为 TextBody 的值字段。当提供者调用失败且返回 HTTP 代码 404 时,可以使用图 26 中的示例返回一个已建模故障。

图 26 中的流首先使用想要的故障类型(operation1_serviceProviderFaultMsg)的一个映射填充一个 SMO 正文。然后,它将这个正文复制到临时上下文中定义的 SOAP Fault 结构的 detail 元素中。最后,它使用一个 Data Handler 将这个 SOAP Fault 序列化为文本。

图 26. 使用 Fault 创建一个 SOAP 信封
使用 Fault 创建一个 SOAP 信封

创建的流程步骤如下:

  1. http://schemas.xmlsoap.org/soap/envelope/ 架构导入您的项目或引用库。这个架构(soap-1.1.xsd)位于我的工作空间的 .metadata\.plugins\com.ibm.ccl.soa.test.common.core 目录中。如果找不到这个架构, 或者无法访问我的工作空间,还可以使用 Integration Designer 的 Import WSDL 特性,通过 HTTP 导入它。
  2. 根据导入的架构创建一个新的 BO 类型,它带有一个名为 “fault”、类型为 Fault 的字段。将其声明为临时上下文变量。
  3. 创建一个 XSL 基元,从一个 WSDL 接口将 Output 终端类型设置为想要的故障消息类型。根据需要填充映射。
  4. 创建一个 Message Element Setter,使用它将 /body 复制到 /context/transient/fault/detail
  5. 创建一个 Data Handler 基元,将故障结构序列化到网关消息正文中。
    1. 单击 Browse 按钮创建一个新的 Data Handler Configuration。选择 XML 并将 Document root name 指定为 Fault,将 Document root namespace 指定为 http://schemas.xmlsoap.org/soap/envelope
    2. 将这个新配置用于 Data Handler,这是正确序列化 SOAP Fault 所必需的。
    3. 将正文 /body/message 调整为 {http://com.ibm.wbiserver.gateway/schema}TextBody 的实际字段类型。
    4. 选择 Action,以便从 Business Object 转换到原生数据格式
    5. 源和目标 XPaths 分别为 /context/transient/fault/body/message/value

TextBody 中的值字段通过一个字符串填充,该字符串是一个序列化的 SOAP Fault,以 &lt;en:Fault 开头,在一个内部 &lt;detail 元素内包含已建模故障数据。

上面的示例构造了一个已建模故障响应,但您可以使用这种技术来创建任意 SOAP 响应。

自动解析 Service Gateway 消息:根据失败创建 SOAP 故障响应

从 Callout Fault 创建令人愉快的 SOAP 响应 小节类似,可以使用类似的方式更改故障部分的内容,允许 WebSphere ESB 自动处理反序列化和序列化。如果需要生成一个 SOAP Fault,只需连接连到相应的 InputFault 即可,如图 27 所示。

图 27. 创建一个故障响应
创建一个故障响应

如果更改了 Service Message Object 中的 SOAPFaultInfo 结构的内容,那么这些更改会自动重新序列化到 SOAP Fault 消息中。这种方法极大地简化了故障处理的设置和配置。

优雅地响应但强制执行事务回滚

如前所述,未处理的基元故障或使用 Fail 基元会导致未建模故障,而且,这还会导致事务回滚。使用 Stop 基元或返回已建模故障不会导致事务回滚。

有时,服务可能想向请求者返回一个 “愉快” HTTP 200 响应(或一个已建模故障),但还要回滚事务(无论本地还是全局,只要适用)。您可以使用下面的 Java 代码,强制执行事务回滚:

javax.transaction.TransactionManager tm = com.ibm.ws.Transaction.TransactionManagerFactory
	.getTransactionManager();
tm.setRollbackOnly();

您可以将事务回滚放置到响应节点前面的某个中介流中位于任何位置的自定义中介基元中。

Service Invoke 基元与 Callout 节点的比较

Service Invoke 基元与响应流上的 Callout Fault 节点有类似的功能。对于在合作伙伴接口上声明的每个故障,都拥有一个故障终端。

对于错误处理,使用 Service Invoke 基元还是 Callout 节点通常无关紧要。通常,选择基于其他原因处理错误。Service Invoke 用于更复杂的集成(比如汇总)中,Callout 节点用于更常见的 “请求者-提供者” 场景。Service Invoke 基元的一个相对好处是,可以使用一个 Fail 终端连接到单向(“触发并遗忘” 或 “仅仅请求”)接口调用服务(参见图 28)。这允许失败处理调用一个单向服务。

图 28. 使用 Service Invoke 基元处理单向调用失败
使用 Service Invoke 基元处理单向调用失败

Service Invoke 基元的一个小缺点是,如果故障在开发过程中被修改,则需要在画布上重新实现该基元。

另一个区别是,在默认情况下,当触发 Callout Response 节点的 Fail 终端时,它不包含来自请求流的消息。但是,Callout Response 节点上有一个复选框属性,如果需要,可用于保留请求消息,但需要付出一些性能代价。触发 Fail 终端时,Service Invoke 基元总是保留来自 In 终端的消息。


结束语

在开发过程中遇到失败时,用户可以调试并调查问题,然后进行重试。在进入测试或生产阶段所使用的解决方案中,记录正确的信息并按照预期的方式通过规范来处理失败非常重要。这有助于支持团队快速解决技术问题,或者识别导致问题的数据。

要创建一个完整的错误处理策略,需要考虑到整个系统。例如,在将消息从一个队列交付给某个中介的场景中,流实例失败时需要考虑其他问题。可以选择立即重试消息交付,通过管理员(使用 Failed Event Manager)进行手动重试,或者将消息移动到一个失败队列。请参见 参考资料 部分的链接,了解异步场景和总体策略。

本文介绍了 Integration Designer 和 WebSphere ESB 中的一些可用构建块,通过一些示例展示了如何在同步 Web 服务交互中结合使用它们。

致谢

本文作者衷心感谢 Andy GarrattSergiy FastovetsGabriel TelermanKim Clark 对本文的审阅!

参考资料

学习

获得产品和技术

讨论

条评论

developerWorks: 登录

标有星(*)号的字段是必填字段。


需要一个 IBM ID?
忘记 IBM ID?


忘记密码?
更改您的密码

单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件

 


在您首次登录 developerWorks 时,会为您创建一份个人概要。您的个人概要中的信息(您的姓名、国家/地区,以及公司名称)是公开显示的,而且会随着您发布的任何内容一起显示,除非您选择隐藏您的公司名称。您可以随时更新您的 IBM 帐户。

所有提交的信息确保安全。

选择您的昵称



当您初次登录到 developerWorks 时,将会为您创建一份概要信息,您需要指定一个昵称。您的昵称将和您在 developerWorks 发布的内容显示在一起。

昵称长度在 3 至 31 个字符之间。 您的昵称在 developerWorks 社区中必须是唯一的,并且出于隐私保护的原因,不能是您的电子邮件地址。

标有星(*)号的字段是必填字段。

(昵称长度在 3 至 31 个字符之间)

单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件.

 


所有提交的信息确保安全。


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=WebSphere
ArticleID=784284
ArticleTitle=使用 IBM Integration Designer、WebSphere ESB 、WebSphere Process Server 和 Business Process Manager Advanced Edition 在中介模块中跟踪、记录日志并处理错误,第 2 部分
publish-date=01092012