在SOA中实现异常2010-04-11 infoq Boris Lublinsky 译者:胡键理想情况下,服务调用总能成功完成并返回需要的结果。不幸的是,在现实中,服务可能而且也会失败。这种失败可以有一大堆的问题引起。它可以是由服务本身引起,如入参验证失败,或只是服务实现的一个bug,或通信问题(如服务不可达或实现不可访问底层的数据库)。最后,失败可以由部署问题引起,如软件升级之后,必需的一个库没有被正确地部署。一种被广泛采用的失败处理机制是异常处理,包括捕获并记录错误,以及在失败发生时选择一个备选执行路径。在应用程序和组件实现中,它已经成为一种标准机制。问题在于,它特别依赖于应用程序设计者和开发者对可能异常情况的预见能力,以及在运行时正确的利用代码1 处理它们的能力。这种方法基于以下假设:应用程序作为一个整体完全地被设计,包括所有可能的异常状况。这意味着应用程序所有的执行路径被全部定义,结果是,它可以被应用程序团队完全地测试。应用程序在单台机器(或有限数量的机器)上运行,使用标准化的异常报告模式,在本地文件日志文件中报告所有的异常。应用组件中发生的变更被集中管理,这意味着应用程序开发团队可以对变更进行完整的控制。在分布式系统的情况下,试图实现这种异常处理方法明显变得复杂,因为在这种情况下,异常不仅可以由应用程序代码本身,也可以由基础设施(如网络)故障引起,这使得分析所有可能的异常场景变得更加困难。此外,在此情形下,异常日志在多台物理机间传播,也使得协调它们明显更复杂。在面向服务架构(SOA)上下文中的特性,如松耦合(组织性和技术性),自治以及依赖已有实现业务功能的应用使得异常处理变得更加复杂。由于每个服务是自行设计、实现和维护,并可用到多个企业解决方案(在服务设计时,它们可能是未知的)中,特定服务的异常处理实现围绕3点进行:处理异常;向本地服务实现记录异常;在它们不能被本地解决时,将它们向服务消费者报告为。如果没有采取特殊的度量,这将导致“异常处理孤岛”(见图1)2。

图1 异常处理孤岛以下的复杂问题在单片应用程序中并不是什么大问题,为了支持SOA下的异常处理,必须被处理[2]。SOA的分布式和异构的天性使得它特别容易失败,在多个层次上引起异常[3]。系统级的异常,由消息传递、通信和其他基础设施失败引起。应用级异常,由于错误的消息语义或应用中的逻辑错误导致。业务级异常,由于违反最佳实践、合规法律、规章制度,或业务经理要求的业务政策引起。后者甚至可能对服务执行本身不可见,可能需要服务管理解决方案适当的检测它们。有一种异常情况是任何参与者中的异常处理无法检测的,即与横跨不同业务过程的一个或多个服务(有时横跨多个公司)的特定业务事务相关的异常情况。在此情形下,异常处理可能需要聚合业务事务级别的异常信息。这意味着特定服务的异常信息必须被使用该服务的业务事务割断。此外,这种隔断的需要还因为隐私、健康保险携带和责任法案(Health Insurance Portability and Accountability Act,HIPAA)和其它的合规要求而加强。单个服务缺少整个解决方案(业务过程)的视角。缺少过程范围的视角,将使得错误修正变得困难。因此,服务实现一旦检测到异常,并不总能选择一条可选执行路径,同时还不得不通知消费者,因为它可能有修正这种情形所需要的上下文。然而,SOA递归组合的特性使得情况进一步的复杂化:消费者常常是另一个服务,因此也不是解决该问题的合适位置[4]。不是所有的异常都能被自动处理,有时唯一处理失败的办法就是通过人工干预。要做到这些,就需要决定谁是合适的人,并通知他们关于异常的事情。松耦合、异构的服务对于异常的发现和处理常常各不相同。有些可能会使用一些特殊的组件,如log4j、log4ne、.NET企业库等等,其它则使用专有解决方案。此外,正如[1]中所定义的,包装现有应用程序的功能是当前服务实现普遍的做法。这些传统应用可以以不同的方式检测、记录和通知异常。对异常处理实现应用SOA原则,是SOA中一种优雅的异常处理解决方案。这将导致所有主要的异常管理元素“服务化”[4],(即,日志,异常解决和通知)。图2显示了异常日志、解决和通知的总体架构。

图2 异常日志、解决和通知的统一架构