如何在事件驱动微服务中创建重放机制

时间:2017-12-22 09:17:08

标签: java architecture transactions microservices event-driven-design

我们有7个微服务通过eventbus进行通信。 我们有一个实时的交易顺序:

服务1-> service2-> service3(依此类推。)直到认为已完成的交易为止

我们必须确保所有交易都已发生。

当然,我们可以在任何时候出现失败。因此,我们正在考虑将机制设备重播为“半成品”交易。

这变得棘手了。我们想到的两种方式:

  1. 拥有另一项服务(主管服务),它将按照我们的实时顺序记录每个部分,并且在交易未完成时(足够时间)足够聪明,以了解我们如何从左点继续进行

    缺点: 在一个中央服务上有很多“智能”逻辑

  2. 对每项服务进行重试机制,同时每个服务都有自己的服务并重播自己的服务,直到成功或完成为止

    缺点:  每个服务上有很多重试重复的代码

  3. 你有什么专家的想法?

    感谢

2 个答案:

答案 0 :(得分:2)

您似乎在谈论的是如何处理分布式架构中的事务。

这是一个广泛的主题,可以写完整本书。您的问题似乎只是重试事务,但我认为单凭这可能不足以解决分布式事务工作流的问题。

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

补偿交易背后的想法是每个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中。确认付款后,订单将显示在用户界面中,以便用户可以最终处理其货件。

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

设计分布式事务工作流

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

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

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

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

答案 1 :(得分:0)

据我所知(您可能也已经知道),您似乎正在尝试实施Circuit Breaker模式以及是将其实现为中央服务还是作为业务事务逻辑的一部分。

决定是否将其作为单独服务更好的一个参数是查看您是否只有一个此类交易或者还有更多?如果有多个,那么将断路器拉出实际业务可能会更好。它可以是包含在不同服务中的一种实用程序组件,也可以是独立的微服务。在独立服务的情况下,选项可以是使用现成的产品/库/框架来执行此操作。我对你的环境和局限性了解不多,但你甚至可以考虑使用Camel或轻型BPM引擎这样的目的。

在我看来,无论如何将这种非业务逻辑与实际的事务业务分开会更好,无论是作为库添加的实用程序组件还是单独的服务。