|
|||
|
Guido Schmutz 和 Ronald van Luttikhuizen
面向服务的环境中故障处理系列文章的第 2 部分
2013 年 5 月
下载
Oracle Service Bus
Oracle SOA Suite
这个关于故障处理与预防的系列文章的第 1 部分讨论了什么是故障处理及其重要性何在。还介绍了与传统系统相比,在面向服务的环境中处理故障所面临的具体挑战。第 1 部分最后展示了一个示例情景 — BPM 和 SOA 环境中实现的一个订单流程,讨论了潜在危险,并介绍了一般的故障预防和恢复模式。
第 2 部分重点介绍集成层中通过 Oracle Service Bus (OSB) 实现的具体故障处理和预防措施。集成域包括典型要素和集成功能,如用于连接后端系统的适配器、路由、转换和筛选。
下面的图 1 重温了第 1 部分中的情景。我们使用 Trivadis 集成架构蓝图表示法 [1]。左侧显示订单流程中的流程步骤,从收到订单请求到订单处理完毕。右侧显示流程为了完成订单请求而需要交互的所有外部系统。中间部分显示流程与后端系统的集成,即使用 OSB 以服务的形式向流程公开后端系统。
红色椭圆表示必须处理的问题,如第 1 部分所述。蓝色椭圆显示集成层中为解决这些问题而实施的措施:
本文其余部分将讨论这几种故障情况的预防和处理措施,并介绍如何通过 Oracle Service Bus 实现这些措施。
产品数据库属于扩展能力有限的旧系统。我们必须小心控制,避免系统请求过载,否则系统可能崩溃。例如,圣诞前夕订单的季节性增加可能导致出现请求高峰。
您可以将特定请求的结果存储在缓存中,而不必为每个产品请求都调用后端系统。然后当请求同一信息的另一请求到达时,使用缓存检索已经请求过的数据。
Oracle Service Bus 允许以声明方式在业务服务中添加结果缓存。通过这种方式,业务服务上的每个请求首先会检查缓存中是否已经有所需信息 (1)。如果有,则将数据直接返回给使用者。如果没有,则将请求发送到后端系统 (2),将响应存储在缓存中 (3) 并返回给使用者。
结果缓存可以在业务服务配置的 Advanced Settings 中激活。启用缓存非常简单,只需选择 Supported 复选框,指定用于缓存令牌的值,并指定缓存中数据的过期时间(图 4)。缓存令牌充当引用数据元素的唯一标识符,使用 XPath 表达式进行配置。在下面的图 4 中,将产品代码指定为驻留在缓存中的产品数据的唯一标识符。
在后台,Oracle Service Bus 使用 Oracle Coherence 实现结果缓存。注意,缓存完全由 OSB 管理,不能通过 API 或 OSB 代理服务的消息处理逻辑访问或更新。
故障操作类型:
应用和注意事项:
影响:
替代实现:
由于请求的数据是动态的,缓存可能并不可行。即使应用缓存 (A1),产品数据库需要处理的请求消息可能还是太多了。例如,当同时订购许多之前未订购过(因此尚未出现在结果缓存中)的不同产品时,就可能出现这种情况。为避免后端系统崩溃,可以通过缓冲来限制同时发送到后端系统的请求数。
可以使用 Oracle Service Bus 通过限制队列来控制(或限制)代理与业务服务之间的消息流。此队列基本上是一个内存中队列,在峰值负载期间保存请求,有效地限制发送到后端系统的并发请求数。
限制是一种操作任务,因此只能通过 OSB 控制台添加。与 Oracle Enterprise Pack for Eclipse (OEPE) 结合使用时,它在开发环境中不可用。图 6 显示了如何使用控制台配置限制。
在本例中,将限制队列配置为最多可保存 100 条消息。并发数限制为 5,意味着同一时间只允许向产品数据库发送 5 条消息。其他消息缓存在队列中,当并发数小于最大值时才提交。Message Expiration 属性用于配置消息在队列中存储的最长时间。当缓存的消息过期时,将向服务使用者返回一个故障。Message Expiration 为 0 表示限制队列中的消息不会过期。
故障操作类型:
应用和注意事项:
影响:
替代实现:
与外部系统通信时,有些不受控制的因素可能会干扰特定服务的稳定性。例如,网络连接可能就没有服务本身那么可靠,从而导致连接问题。只要这些中断是暂时的(几秒钟或更短的时间),我们就能通过重新发送请求或使用服务的另一个运行时实例在集成层处理故障。在这些情况下,使用者可能甚至根本未察觉到发生过问题。
集成层可以通过多次重试出错请求来处理错误,而不必将技术故障发送给服务使用者来指明服务不可用。如果导致原始故障的原因是网络短暂中断,那么第二次或第三次尝试可能会成功。如果重试时间足够长,导致故障的原因是服务实例失败并且设置了自动的服务重启程序,我们甚至可以等待服务重新启动。
Oracle Service Bus 允许您定义业务服务的重试行为。这对调用业务服务的代理服务是完全透明的。代理服务仍需准备处理或传递故障,以防达到重试总数时仍无法成功调用服务。
重试行为是在业务服务的 Transport Configuration 中配置的(图 9)。我们可以配置重试次数 (Retry Count)、两次重试间的等待时间 (Retry Iteration Interval),并指明是否应重试应用程序错误。由于应用程序错误通常代表后端系统中的(功能)故障,所以重试应用程序错误往往解决不了根本问题。
故障操作类型:
应用和注意事项:
影响:
替代实现:
服务本身可能会因为发生故障(例如,服务崩溃)或计划内维护而关闭。因为这些情况是可以预见的,服务提供方可以提供同一服务的多个实例。在这种情况下,服务使用者可以调用另一个仍处于活动状态的服务实例。根据导致原始服务失败的具体原因,这个新实例可能运行在任意位置:在单独的机器上、在与原始实例相同的机器上或在不同的虚拟化环境中。
可以在 Oracle Service Bus 中为特定业务服务定义服务端点池。如果 OSB 在其中一个端点上检测到错误,它将在另一个服务实例上重试请求。可通过以下方式配置服务池:
OSB 能够在服务短点发生错误时将其标记为脱机。这样,就不会再向故障端点发送服务请求。脱机端点在指定时间间隔后可以自动恢复联机,也可以通过 OSB 控制台或 JMX 手动恢复。
服务池的使用对调用业务服务的代理服务是完全透明的。
服务池是在业务服务的 Transport Configuration 中配置的(图 12)。您可以在此配置要使用的负载平衡策略 (Load Balancing Algorithm) 以及将用于调用服务实例的各种服务端点 (Endpoint URI)。服务池适用于 OSB 支持的所有协议(图 12 中使用了 HTTP)。
故障操作类型:
应用和注意事项:
影响:
替代实现:
如本系列第一篇文章中所述,业务故障与技术故障之间存在差异。这种差异可用于在集成层区分可以重试的错误和不能重试的错误。
临时网络故障就是一个可以重试的技术故障。几秒钟后的重试请求可能足以恢复此类故障。解决方案 B1 — 使用重试机制多次发送同一请求中所描述的就是这种情况。如果发生业务故障(如信用卡无效故障),立即重试请求毫无意义 — 该卡仍然无效。我们无法在服务本身内预防或处理此类故障。我们需要告知服务使用者(在本例中为订单业务流程)此故障情况。本系列第三篇文章将介绍如何在流程层处理此类错误。
因此需要一种机制将不能重试的故障回传给服务使用者。原始故障可以在集成层中按原样传递,也可以转换成服务接口中议定的另一种故障消息。
如何将故障传递给使用者取决于所使用的协议。对于基于 SOAP 的 Web 服务,您可以在使用同步请求/响应消息交换模式时声明并返回故障消息。如果我们使用异步通信,则必须将故障作为正常回调消息返回。
对于 RESTful Web 服务,应使用 HTTP 状态代码报告故障情况,使用消息保存有关错误的更多信息。
第一个解决方案需要向服务使用者返回故障消息,包含所有必要信息。然后使用者负责决定如何处理故障。该解决方案讨论了同步和异步两种情况。
在同步 Web 服务操作中,应在接口定义 (WSDL) 中声明功能故障消息。与任何现代编程语言一样,Oracle Service Bus 使用错误处理程序的概念来处理故障情况,比如,服务调用返回的故障消息或代理服务消息流中的问题。图 14 显示:
图 15 显示如何在代理服务消息流设计器中以图形方式为错误处理程序建模。在此情况下,使用 Assign 操作将后端故障转换成服务接口中声明的故障消息。然后由 Reply 操作将故障返回给服务使用者。
针对 Reply 操作指定 With Failure 选项非常重要,这样等待响应的使用者才会在接收消息时将其作为失败而不是成功的响应。
如果在基于 SOAP 的 Web 服务中使用异步请求/响应消息交换模式,则将故障指定为一条额外的回调消息。从技术上来说,报告成功结果的“正常”回调消息与故障回调之间没有区别。
在我们的示例案例中,异步返回产品不再可用故障,因为服务操作是异步的。由于在此故障情况下重试毫无意义,因此必须将故障传递给使用者(订单流程),使用者将处理故障并执行必要的操作。本系列第三篇文章将介绍如何完成此任务。
图 17 显示服务使用者与 OSB 之间以及 OSB 与订单处理系统之间的异步消息交换。由于这些交换涉及两个交换(请求和回调),因此使用了两对代理服务和业务服务。后端订单处理系统向同一响应队列发布成功响应和故障消息。监听响应队列的 OSB 代理服务区分成功响应和故障消息。
在这种情况下,代理服务首先会抛出一个内部错误,这样可以使用错误处理程序将信息转换成适当的故障消息,然后作为故障回调消息返回给使用者。这在图 17 中用红线显示。
服务使用者与服务提供程序(本例中为 OSB)之间议定的接口定义可以预期的功能故障。如果在定义标准回调消息的接口中定义故障回调消息,可以通过同一业务服务将故障返回使用者。否则,需要额外添加一个业务服务。
还可以使用正常响应消息而不是单独定义的消息故障向服务使用者报告故障情况,但是不建议采用这种做法。
如果订单处理系统针对故障响应和成功响应返回的消息不同,那么可能使用了两个代理服务。每个服务将充当队列中的一个选择性使用者,一个只使用成功消息,另一个只使用故障消息。如果在不同接口中声明了故障消息,可以通过同一业务服务或额外的业务服务再次发送返回给使用者的故障消息(图 18)。
如果订单处理系统使用不同的响应队列来分离故障和成功结果,可以使用先前介绍的类似配置。您仍然需要两个代理服务:一个使用成功响应,另一个使用故障响应,如图 19 所示。
解决方案 C1 — 返回故障消息的这些不同实现有一个共同点。它们都假定发送请求的使用者仍在等待响应。在使用者必需等待答复的同步请求/回应交互中,这是很自然的。但在异步情景中,请求可能已经发出了数分钟、数天甚至数周时间,才收到答复。使用者可能早就离开了,再将故障作为回调消息发送毫无意义。在这种情况下,可以使用解决方案 C2 — 在持久存储中存储故障。但在我们的情景中,使用者是 BPEL 流程,它将一直等到收到回调消息(无论是成功还是故障消息)。本系列第三篇文章中也将介绍该流程。
故障操作类型:
应用和注意事项:
影响:
最佳做法是通过设计良好的界面并使用基于 SOAP 的 Web 服务的服务定义 (WSDL) 中可用的故障消息始终向服务使用者报告错误,而不是使用“正常”响应消息中的自定义错误代码。这样,使用端(使用所有现代编程语言中可用的错误处理构造)和服务基础架构级别(如 OSB)的错误处理程序将自动捕获错误。
在异步情景中,发送初始请求的使用者可能不再可用,或者可能对故障消息不再感兴趣。通常来说,此类情景中唯一的替代方案是是将故障存储在持久存储(错误医院)中以便稍后(手动)处理。
图 20 显示通过使用 DB 适配器调用单独的业务服务将故障消息存储在数据库中。
错误医院中的故障处理主要涉及需要手动活动稍后重新提交的错误。必须有人分析故障情况,决定应如何处理故障消息。能否通过重新提交消息纠正该情况?通过更改消息有效负载?是否应完全丢弃该消息,从而不得不通知使用者无法处理订单?一种策略是使用 Oracle SOA Suite 中可用的人员任务服务。
错误医院通常包含一个持久存储(通常是事务性的),如数据库表或 JMS 队列。数据库表在重新提交故障消息时有优势,因为它允许轻松检索所需的故障消息并将其按适当的次序排列 (SQL SELECT),可能按发生顺序排列。故障处理逻辑可能希望通过调用与使用者相同的代理服务将(纠正的)消息反馈回处理逻辑。
故障操作类型:
应用和注意事项:
影响:
替代实现:
OSB 与订单处理后端系统之间的集成通过两个队列异步完成。一个原因可能是希望有附加的分离,这样即使后端系统不可用,仍然可以接受订单。另一个原因可能是旧式系统中的限制不允许与外部系统直接链接。
如图 21 所示,业务流程与订单处理系统之间集成的异步本质增加了丢失消息的危险。在消息处理期间,外部系统或集成层中可能发生错误,例如,消息转换因为请求消息无效而失败,或者无法向使用者提供回调消息。
在所有这些情况中,都必须保证在任何情况下都不会丢失消息。
实现可靠消息传递的一种方式是确保在活动事务中执行消息处理逻辑,从事务资源检索待处理的消息并在处理完消息后将其存储在事务资源中。如果处理成功,事务将成功结束。如果发生错误,将回滚事务。这样,我们可以将处理分成不同的独立步骤,每个步骤作为一个整体进行工作。活动事务必须是分布式事务,因为它涉及多个资源,如 JMS 服务器和 BPEL 流程管理器及其数据库持久性(脱水存储)。这意味着只能将事务资源用作源系统和目标系统。
在本例中,消息处理逻辑的源系统和目标系统是 BPEL 流程管理器或 JMS 队列,因此我们只处理事务资源。如图 22 所示,我们可以将处理逻辑分成三个单独的分布式事务:
这种配置可确保 BPEL 流程将其活动事务上下文传递到 OSB,从而在与 BPEL 相同的事务中执行请求消息的消息流。如果未发生错误,事务将结束并在请求队列中生成一条新消息。之后,后端系统在新事务中使用该消息并执行其消息处理逻辑。如果成功,事务将结束并在响应队列中生成一条消息。在第三个事务中,OSB 上的响应处理消息流程使用来自请求队列的消息,对其进行处理并向 BPEL 流程发送回调消息,传递活动事务上下文,这样 BPEL 流程可以继续进行并重用这第三个事务,直到下一脱水点并提交事务。通过使用正确的事务边界,可确保消息不会丢失。
对于入站消息,Oracle Service Bus 可以启动新事务或参与已经处于活动状态的分布式事务。只需在代理服务上设置一个选项即可。使用业务服务配置的出站消息也是如此。
图 23 显示 Oracle Service Bus 上这种情况的配置。
使用 Service Bus (SB) 协议往来服务使用者(即 Oracle SOA Suite 上运行的 BPEL 流程),我们可以保证将事务上下文从 BPEL 传播到 OSB 以处理请求,以及从 OSB 传回 BPEL 以处理响应。在 BPEL 端,必须使用 SOA 直接绑定,我们将在本系列第三篇文章中对此进行讨论。这种参与全局事务的机制可能不适用于所有传输 — 例如,使用 SOAP 时。
一个重要的注意事项就是将这两个队列放在哪里。为了预防故障,队列应至少与集成层本身(即 OSB)一样可靠和可用。只有这样,我们才能保证即使订单处理系统不可用,OSB 也能写入队列。如果队列的可用性稍差,就要担心不能将消息写入请求队列。达到相同可用性的最简单的方式就是将队列放在运行 OSB 的 Oracle WebLogic Server 上。Oracle WebLogic JMS 是一种非常可靠的 JMS 服务器,因此是一种很好的选择。
如果外部系统无法与 Oracle WebLogic JMS 集成,或者系统已经提供自己的队列服务,OSB 可能就必须与订单处理系统上驻留的外部队列集成。
故障操作类型:
应用和注意事项:
影响:
替代实现:
以上是我们在 OSB 中实现的故障预防和处理措施,涵盖了集成层中各种不同的故障情况。在本系列的下一篇文章中,您将了解可以在 Oracle SOA Suite 的流程层采取哪些措施来处理和预防本示例情景中可能的故障情况。
Guido Schmutz 是 Trivadis 的 SOA 及新趋势的技术经理,还是 Oracle ACE 总监。