如何在事件驱动微服务中创建重放机制
How to create replay mechanism within event-drive microservice
我们有 7 个微服务通过事件总线进行通信。
我们有一个实时交易顺序:
服务1->服务2->服务3(依此类推。)直到交易被视为完成
我们必须确保所有交易都已发生。
当然,我们随时都可能遇到失败。所以我们正在考虑重播 "half-baked" 交易完成的机制。
越来越棘手了。我们考虑了两种方式:
有另一个服务(主管服务)将在我们的实时序列中记录每个部分,并且在交易未完成(超时)时足够聪明,以了解我们如何从左点继续
缺点:
一个中央服务上有很多 "smart" 逻辑
在每项服务上都有重试机制,同时每个服务都自己处理并重播它自己直到成功或耗尽
缺点:
每个服务上的大量重试重复代码
各位专家怎么看?
感谢
据我所知(您可能也已经知道),您似乎正在尝试实施 Circuit Breaker 模式,以及是将其实施为中央服务还是作为业务交易逻辑的一部分。
决定将其作为一项单独的服务是否更好的一个参数是查看您是否只有一个这样的交易,还是有更多?如果不止一个,那么或许把熔断器从你的实际业务中拉出来会更好。它可以是包含在不同服务或独立微服务中的一种实用程序组件。在独立服务的情况下,一个选项可能是使用现成的 product/library/framework 来做到这一点。我不太了解您的环境和限制,但您甚至可以考虑为此目的使用 Camel 或轻型 BPM 引擎。
在我看来,最好将此非业务逻辑与您的实际交易业务分开,作为作为库添加的实用程序组件或单独的服务。
你说的好像是在分布式架构中如何处理事务
这是一个广泛的话题,可以写整本书。您的问题似乎只是关于重试事务,但我认为仅靠此可能不足以解决分布式事务工作流.
的问题
我相信您可能会受益于对以下概念的更多理解:
补偿交易背后的想法是,每个阴都有阳:如果你有一个可以下订单的交易,那么你可以用一个取消订单的交易来撤销它命令。后一项交易是 补偿交易 。因此,如果您执行了许多成功的交易,然后其中一个失败了,您可以追溯您的步骤并补偿您所做的每一个成功的交易,从而恢复它们的副作用。
我特别喜欢书中的一个章节REST from Research to Practice。它的第 23 章(Towards Distributed Atomic Transactions over RESTful Services)深入解释了 Try/Cancel/Confirm 模式。
一般来说,这意味着当您进行一组事务时,它们的副作用只有在事务协调器确认它们全部成功后才会生效。例如,如果您在 Expedia 进行预订并且您的航班有两条不同航空公司的航班,那么一笔交易将预订美国航空公司的航班,另一笔交易将预订联合航空公司的航班。如果您的第二次预订失败,那么您想补偿第一次预订。但不仅如此,您还想避免第一次预订有效,直到您能够确认两者。因此,初始交易进行了预订,但保留其副作用 等待确认 。第二个预订也会做同样的事情。一旦事务协调器知道一切都已保留,它可以向所有各方发送确认消息,以便他们确认他们的保留。如果未在合理时间内确认预订 window,受影响的系统会自动撤销预订。
《微服务世界》一书 Enterprise Integration Patterns has some basic ideas on how to implement this kind of event coordination (e.g. see process manager pattern and compare with routing slip pattern which are similar ideas to 。
如您所见,能够补偿事务可能很复杂,具体取决于分布式工作流的复杂程度。流程管理器可能需要跟踪每个步骤的状态,并知道何时需要撤消整个操作。这几乎就是微服务世界中 Sagas 的想法。
这本书 Microservices Patterns 有一整章称为使用 Sagas 管理事务,详细研究了如何实施此类解决方案。
我通常还考虑的其他几个方面如下:
幂等性
我认为在分布式系统中成功实施服务事务的关键在于使它们 idempotent。一旦您可以保证给定的服务是幂等的,那么您就可以安全地重试它而不必担心造成额外的副作用。但是,仅重试失败的交易并不能解决您的问题。
暂时性错误与持久性错误
在重试服务事务时,您不应该因为失败就重试。您必须首先知道失败的原因,并根据错误重试或不重试是否有意义。某些类型的错误是暂时的,例如,如果一个事务由于查询超时而失败,那么重试可能没问题,而且很可能第二次会成功;但是如果您遇到数据库约束违规错误(例如,因为 DBA 向字段添加了检查约束),那么重试该事务就没有意义:无论您尝试多少次,它都会失败。
拥抱错误作为替代流程
在服务间通信(计算机到计算机交互)的那些情况下,当工作流的给定步骤失败时,您不一定需要撤消在前面步骤中所做的所有操作。您可以将错误作为工作流程的一部分。对可能的失败原因进行分类,并使它们成为仅需要人工干预的替代事件流。这只是整个编排中的另一个步骤,需要一个人进行干预以做出决定、解决与数据的不一致或只是批准要走的路。
例如,也许当您正在处理订单时,由于您没有足够的资金而导致支付服务失败。因此,撤消其他所有内容毫无意义。我们所需要的只是将订单置于某个问题解决者可以在系统中解决的状态,一旦解决,您就可以继续其余的工作流程。
事务和数据模型状态是关键
我发现这种类型的事务性工作流需要对模型必须经历的不同状态进行良好设计。与 Try/Cancel/Confirm 模式的情况一样,这意味着最初应用副作用而不一定使数据模型对用户可用。
例如,当您下订单时,也许您将其以“待定”状态添加到数据库中,这种状态不会出现在仓库系统的 UI 中。确认付款后,订单将出现在 UI 中,以便用户最终可以处理其发货。
这里的难点在于发现如何以一种方式设计交易粒度,即使您的交易工作流程的一个步骤失败,系统仍保持有效状态,一旦失败的原因被纠正,您就可以从该状态恢复。
分布式事务工作流设计
因此,如您所见,设计以这种方式工作的分布式系统比单独调用分布式事务服务要复杂一些。现在每次服务调用都可能由于多种原因而失败,并使您的分布式工作流处于不一致的状态。并且重试交易不一定总能解决问题。并且您的数据需要像状态机一样建模,以便应用副作用但在整个编排成功之前不确认。
这就是为什么整个事情可能需要以不同于您通常在单一客户端-服务器应用程序中所做的方式进行设计的原因。在解决冲突方面,您的用户现在可能是设计解决方案的一部分,并考虑到事务编排可能需要数小时甚至数天才能完成,具体取决于他们的冲突是如何解决的。
正如我最初所说的,这个话题太宽泛了,可能需要一个更具体的问题来详细讨论这些方面中的一两个方面。
无论如何,我希望这对您的调查有所帮助。
我们有 7 个微服务通过事件总线进行通信。 我们有一个实时交易顺序:
服务1->服务2->服务3(依此类推。)直到交易被视为完成
我们必须确保所有交易都已发生。
当然,我们随时都可能遇到失败。所以我们正在考虑重播 "half-baked" 交易完成的机制。
越来越棘手了。我们考虑了两种方式:
有另一个服务(主管服务)将在我们的实时序列中记录每个部分,并且在交易未完成(超时)时足够聪明,以了解我们如何从左点继续
缺点: 一个中央服务上有很多 "smart" 逻辑
在每项服务上都有重试机制,同时每个服务都自己处理并重播它自己直到成功或耗尽
缺点: 每个服务上的大量重试重复代码
各位专家怎么看?
感谢
据我所知(您可能也已经知道),您似乎正在尝试实施 Circuit Breaker 模式,以及是将其实施为中央服务还是作为业务交易逻辑的一部分。
决定将其作为一项单独的服务是否更好的一个参数是查看您是否只有一个这样的交易,还是有更多?如果不止一个,那么或许把熔断器从你的实际业务中拉出来会更好。它可以是包含在不同服务或独立微服务中的一种实用程序组件。在独立服务的情况下,一个选项可能是使用现成的 product/library/framework 来做到这一点。我不太了解您的环境和限制,但您甚至可以考虑为此目的使用 Camel 或轻型 BPM 引擎。
在我看来,最好将此非业务逻辑与您的实际交易业务分开,作为作为库添加的实用程序组件或单独的服务。
你说的好像是在分布式架构中如何处理事务
这是一个广泛的话题,可以写整本书。您的问题似乎只是关于重试事务,但我认为仅靠此可能不足以解决分布式事务工作流.
的问题我相信您可能会受益于对以下概念的更多理解:
补偿交易背后的想法是,每个阴都有阳:如果你有一个可以下订单的交易,那么你可以用一个取消订单的交易来撤销它命令。后一项交易是 补偿交易 。因此,如果您执行了许多成功的交易,然后其中一个失败了,您可以追溯您的步骤并补偿您所做的每一个成功的交易,从而恢复它们的副作用。
我特别喜欢书中的一个章节REST from Research to Practice。它的第 23 章(Towards Distributed Atomic Transactions over RESTful Services)深入解释了 Try/Cancel/Confirm 模式。
一般来说,这意味着当您进行一组事务时,它们的副作用只有在事务协调器确认它们全部成功后才会生效。例如,如果您在 Expedia 进行预订并且您的航班有两条不同航空公司的航班,那么一笔交易将预订美国航空公司的航班,另一笔交易将预订联合航空公司的航班。如果您的第二次预订失败,那么您想补偿第一次预订。但不仅如此,您还想避免第一次预订有效,直到您能够确认两者。因此,初始交易进行了预订,但保留其副作用 等待确认 。第二个预订也会做同样的事情。一旦事务协调器知道一切都已保留,它可以向所有各方发送确认消息,以便他们确认他们的保留。如果未在合理时间内确认预订 window,受影响的系统会自动撤销预订。
《微服务世界》一书 Enterprise Integration Patterns has some basic ideas on how to implement this kind of event coordination (e.g. see process manager pattern and compare with routing slip pattern which are similar ideas to
如您所见,能够补偿事务可能很复杂,具体取决于分布式工作流的复杂程度。流程管理器可能需要跟踪每个步骤的状态,并知道何时需要撤消整个操作。这几乎就是微服务世界中 Sagas 的想法。
这本书 Microservices Patterns 有一整章称为使用 Sagas 管理事务,详细研究了如何实施此类解决方案。
我通常还考虑的其他几个方面如下:
幂等性
我认为在分布式系统中成功实施服务事务的关键在于使它们 idempotent。一旦您可以保证给定的服务是幂等的,那么您就可以安全地重试它而不必担心造成额外的副作用。但是,仅重试失败的交易并不能解决您的问题。
暂时性错误与持久性错误
在重试服务事务时,您不应该因为失败就重试。您必须首先知道失败的原因,并根据错误重试或不重试是否有意义。某些类型的错误是暂时的,例如,如果一个事务由于查询超时而失败,那么重试可能没问题,而且很可能第二次会成功;但是如果您遇到数据库约束违规错误(例如,因为 DBA 向字段添加了检查约束),那么重试该事务就没有意义:无论您尝试多少次,它都会失败。
拥抱错误作为替代流程
在服务间通信(计算机到计算机交互)的那些情况下,当工作流的给定步骤失败时,您不一定需要撤消在前面步骤中所做的所有操作。您可以将错误作为工作流程的一部分。对可能的失败原因进行分类,并使它们成为仅需要人工干预的替代事件流。这只是整个编排中的另一个步骤,需要一个人进行干预以做出决定、解决与数据的不一致或只是批准要走的路。
例如,也许当您正在处理订单时,由于您没有足够的资金而导致支付服务失败。因此,撤消其他所有内容毫无意义。我们所需要的只是将订单置于某个问题解决者可以在系统中解决的状态,一旦解决,您就可以继续其余的工作流程。
事务和数据模型状态是关键
我发现这种类型的事务性工作流需要对模型必须经历的不同状态进行良好设计。与 Try/Cancel/Confirm 模式的情况一样,这意味着最初应用副作用而不一定使数据模型对用户可用。
例如,当您下订单时,也许您将其以“待定”状态添加到数据库中,这种状态不会出现在仓库系统的 UI 中。确认付款后,订单将出现在 UI 中,以便用户最终可以处理其发货。
这里的难点在于发现如何以一种方式设计交易粒度,即使您的交易工作流程的一个步骤失败,系统仍保持有效状态,一旦失败的原因被纠正,您就可以从该状态恢复。
分布式事务工作流设计
因此,如您所见,设计以这种方式工作的分布式系统比单独调用分布式事务服务要复杂一些。现在每次服务调用都可能由于多种原因而失败,并使您的分布式工作流处于不一致的状态。并且重试交易不一定总能解决问题。并且您的数据需要像状态机一样建模,以便应用副作用但在整个编排成功之前不确认。
这就是为什么整个事情可能需要以不同于您通常在单一客户端-服务器应用程序中所做的方式进行设计的原因。在解决冲突方面,您的用户现在可能是设计解决方案的一部分,并考虑到事务编排可能需要数小时甚至数天才能完成,具体取决于他们的冲突是如何解决的。
正如我最初所说的,这个话题太宽泛了,可能需要一个更具体的问题来详细讨论这些方面中的一两个方面。
无论如何,我希望这对您的调查有所帮助。