在事件驱动的世界中处理异常

时间:2018-04-21 12:01:57

标签: event-driven event-driven-design

我试图了解如何在使用微服务的事件驱动的世界中处理异常(使用apache kafka)。例如,如果您采用以下订单方案,则需要在订单完成之前执行以下操作。

  • 1)向付款服务提供商授权付款
  • 2)从库存中保留物品
  • 3.1)使用付款服务提供商
  • 获取付款
  • 3.2)订购商品
  • 4)发送接收带收据的订单的电子邮件通知

在此方案的任何阶段,都可能出现以下故障:

  • 该商品已不再有货
  • 付款信息不正确
  • 收款人使用的帐户没有可用资金
  • 外部呼叫(例如支付服务提供商的呼叫)失败,例如停机

如何跟踪每个阶段的召唤和/或完成情况?

您如何处理出现的问题?你如何通知失败的前端?

1 个答案:

答案 0 :(得分:2)

您描述的一些内容不是错误或异常,而是您应该在分布式体系结构中考虑的替代流程。

例如,商品缺货是您业务流程中完全有效的替代流程。一个可能需要人为干预的人。您可以将消息移动到单独的队列中,并提供一些UI,人工操作员可以在其中处理问题,解决问题并使事件流继续。

可以说你描述的付款问题类似。如果订单无法成功解决,人工操作员将需要调查案例并解决。就此而言,您的设计必须考虑替代流程作为其中的一部分,并使其成为人类可以以某种方式干预,当消息最终在队列中需要人员审查它们时。

应将这些案例与程序抛出的错误或异常区分开来。根据具体情况,这些情况实际上可能需要将消息移动到死信队列(DLQ),以便工程师查看它们。

这是一个非常广泛的主题,整本书都可以写出来。

我相信你可能会从对以下概念的更多理解中受益:

补偿交易背后的想法是每个ying都有它的阳性:如果你有一个可以下订单的交易,那么你可以通过取消该订单的交易来撤销它。后一个事务是补偿事务。因此,如果您执行了大量成功的交易,然后其中一个交易失败,您可以追溯您的步骤并补偿您所做的每一次成功交易,从而恢复其副作用。

我特别喜欢本书REST from Research to Practice中的一章。第23章(迈向RESTful服务的分布式原子事务)深入解释尝试/取消/确认模式

一般而言,它意味着当您执行一组事务时,在事务协调员确认所有事务都成功之前,它们的副作用无效。例如,如果您在Expedia预订并且您的航班有两条航线与不同的航空公司,则一笔交易将预订与美国航空公司的航班,另一笔交易将保留与美国联合航空公司的航班。如果您的第二次预订失败,那么您想补偿第一次预订。但不仅如此,您还要避​​免第一次预订有效,直到您能够确认两者。因此,初始交易会进行预订,但会保留其副作用等待确认。而第二个保留也会这样做。一旦事务协调器知道一切都被保留,它就可以向所有各方发送确认消息,以便他们确认他们的预订。如果在合理的时间窗口内未确认预订,则受影响的系统会自动撤销预订。

本书Enterprise Integration Patterns有一些关于如何实施这种事件协调的基本想法(例如,参见process manager pattern并与routing slip pattern进行比较,这些是类似的想法到微服务世界的orchestration vs choreography

如您所见,根据您的分布式工作流程的复杂程度,能够补偿交易可能会很复杂。流程管理员可能需要跟踪每个步骤的状态,并知道整个事情何时需要撤消。这几乎就是微服务世界中 Sagas 的想法。

本书Microservices Patterns有一整章称为“管理Sagas的交易”,详细介绍了如何实现这种解决方案。

我通常还会考虑以下几个方面:

<强>幂等性

我认为在分布式系统中成功实施服务事务的关键在于使它们成为idempotent。一旦你可以保证给定服务是幂等的,那么你可以安全地重试它而不用担心引起额外的副作用。但是,只是重试失败的交易无法解决您的问题。

瞬态与持续性错误

在重试服务交易时,您不应该重试,因为它失败了。您必须首先知道它失败的原因,并根据错误重试或不重试。某些类型的错误是暂时的,例如,如果一个事务由于查询超时而失败,则重试可能很好,并且很可能第二次会成功;但是如果你遇到数据库约束违规错误(例如因为DBA在字段中添加了检查约束),那么重试该事务没有意义:无论你尝试多少次都会失败。

将错误视为替代流程

正如我在回答的开头所提到的,并非一切都是错误的。有些事情只是替代流程。

在服务间通信(计算机到计算机之间的交互)的情况下,当工作流程的给定步骤失败时,您不必撤消在之前步骤中执行的所有操作。您可以将错误视为工作流程的一部分。对错误的可能原因进行编目,并使它们成为仅需要人为干预的替代事件流。这只是完整编排的另一个步骤,需要一个人进行干预以做出决定,解决与数据的不一致或只是批准走哪条路。

例如,也许当您处理订单时,付款服务会因您没有足够的资金而失败。因此,撤消其他所有内容毫无意义。我们所需要的只是将订单置于某个问题解决方案可以在系统中解决它的状态,一旦修复,您就可以继续使用其余的工作流程。

交易和数据模型状态是关键

我发现这种类型的事务工作流程需要对模型必须经历的不同状态进行良好设计。与尝试/取消/确认模式的情况一样,这意味着最初应用副作用而不必使数据模型可供用户使用。

例如,当您下订单时,可能会将其添加到数据库中的&#34;待定&#34;状态不会出现在仓库系统的UI中。确认付款后,订单将显示在用户界面中,以便用户可以最终处理其货件。

这里的困难在于发现如何设计事务粒度,即使事务工作流程的一个步骤失败,系统仍然处于有效状态,一旦纠正了故障原因,您就可以恢复该状态。

设计分布式事务工作流

因此,正如您所看到的,设计以这种方式工作的分布式系统比单独调用分布式事务服务要复杂一些。现在,每个服务调用都可能由于多种原因而失败,并使您的分布式工作流处于不一致状态。并且重试交易可能并不总能解决问题。并且您的数据需要像状态机一样建模,以便应用副作用但在整个编排成功之前不会确认。

这就是为什么整个事情可能需要以与通常在单片客户端 - 服务器应用程序中执行的方式不同的方式进行设计。在解决冲突时,您的用户现在可能成为设计解决方案的一部分,并考虑到事务性编排可能需要数小时甚至数天才能完成,具体取决于他们的冲突如何解决。

正如我最初所说,这个话题过于宽泛,需要一个更具体的问题来讨论,或许,只是详细讨论这些方面中的一个或两个。

无论如何,我希望以某种方式帮助你进行调查。