代码之家  ›  专栏  ›  技术社区  ›  rayman

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

  •  8
  • rayman  · 技术社区  · 7 年前

    我们有7个通过eventbus通信的微服务。 我们有一个实时事务序列:

    服务1->服务2->服务3(等等)直到交易被视为已完成

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

    当然,我们可以在任何时候失败。因此,我们正在考虑将“半生不熟”的事务重播到完成的机制。

    事情变得越来越棘手了。我们考虑了两种方式:

    1. 拥有另一个服务(主管服务),该服务将记录我们实时序列中的每个部分,并在事务未完成时足够智能(timedout),以了解我们如何从左端继续

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

    2. 在每个服务上都有重试机制,而每个服务都会处理自己的服务并重播自己的服务,直到成功或退出

      缺点: 每个服务上都有大量重复的重试代码

    专家们怎么看?

    感谢

    2 回复  |  直到 7 年前
        1
  •  9
  •   Community CDub    3 年前

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

    这是一个广泛的话题,整本书都可以写这方面的内容。您的问题似乎只是关于重试交易,但我认为仅此一项可能不足以解决 分布式事务工作流 .

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

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

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

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

    这本书 Enterprise Integration Patterns 对如何实现这种 活动协调 (例如,参见 process manager pattern 并与 routing slip pattern 这些想法与 orchestration vs choreography 在微服务领域)。

    正如您所看到的,能够补偿事务可能会很复杂,这取决于分布式工作流的复杂程度。流程经理可能需要跟踪每个步骤的状态,并知道何时需要撤消整个过程。这就是 传奇故事 在微服务领域。

    这本书 Microservices Patterns 有一整章叫做使用传奇管理事务,详细探讨了如何实现这种类型的解决方案。

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

    幂等性

    我相信,在分布式系统中成功实现服务事务的关键在于 idempotent . 一旦您可以保证给定的服务是幂等的,那么您就可以安全地重试它,而不必担心会产生额外的副作用。然而,仅仅重试失败的事务并不能解决您的问题。

    暂时性错误与持续性错误

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

    将错误视为备选流程

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

    例如,当您处理订单时,支付服务可能会失败,因为您没有足够的资金。因此,没有必要撤销其他一切。我们所需要的只是将订单置于某种状态,以便一些问题解决者可以在系统中解决它,一旦修复,您就可以继续工作流的其余部分。

    事务和数据模型状态是关键

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

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

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

    设计分布式事务工作流

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

    这就是为什么可能需要以不同于通常在单片客户机-服务器应用程序中所采用的方式来设计整个东西的原因。在解决冲突时,您的用户现在可能是设计的解决方案的一部分,并且考虑到事务性编排可能需要几个小时甚至几天才能完成,这取决于他们的冲突是如何解决的。

    正如我最初所说的,这个话题太宽泛了,需要一个更具体的问题来讨论,也许只是其中一个或两个方面的细节。

    无论如何,我希望这对你的调查有所帮助。

        2
  •  0
  •   zaerymoghaddam    7 年前

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

    决定是否将其作为单独服务更好的一个参数是查看您是否只有一个或更多这样的事务?如果不止一个,那么最好从实际业务中拔出断路器。它可以是一种包含在不同服务中的实用程序组件,也可以是一个独立的微服务。对于独立服务,可以选择使用现成的产品/库/框架。我对您的环境和局限性了解不多,但您甚至可以考虑为此使用Camel或轻型BPM引擎。

    在我看来,不管怎样,最好将这种非业务逻辑与实际的事务性业务分开,要么作为作为库添加的实用程序组件,要么作为单独的服务。