如何在故障情形下基于事件的消息驱动的微服务架构中恢复状态

Don*_*ler 17 messaging event-based-programming reactive-programming microservices

在微服务架构的背景下,消息驱动,异步,基于事件的设计似乎越来越受欢迎(参见此处此处的一些示例,以及Reactive Manifesto - 消息驱动特性)而不是同步(可能是REST)基于)机制.

考虑上下文并想象一个过度简化的订购系统,如下所示:

订购系统

和以下消息流程:

  • 订单来自某些来源(网络/移动等)
  • 订单服务接受订单并发布 CreateOrderEvent
  • InventoryService会对其做出反应CreateOrderEvent,做一些库存InventoryUpdatedEvent事务并在完成时发布
  • 然后,Invoice服务对其作出反应InventoryUpdatedEvent,发送发票并发布EmailInvoiceEvent

所有服务都很好,我们很乐意处理订单......每个人都很高兴.然后,库存服务由于某种原因而下降

假设事件总线上的事件流向"非阻塞"庄园.即消息被发布到一个中心主题,并且如果没有服务正在从中读取消息,则不会堆积在队列中(我想要传达的是事件总线,如果事件在总线上发布,它将会流"直通"而不排队 - 忽略此时使用的消息平台/技术).这意味着如果库存服务停机5分钟,那么CreateOrderEvent在此期间通过事件总线的现在已经"消失"或者没有被库存服务看到,因为在我们过度简化的系统中,没有其他系统感兴趣那些事件.

我的问题是:库存服务(以及整个系统)如何以不丢失/未处理订单的方式恢复状态?

小智 10

好问题!所以这里基本上有三种力量在起作用:

  1. 如果服务中断,则可能已经错过的任何事件都需要重播以保持一致
  2. 事件,因为它们发生在"时间",有一个"这发生在那之前"命令他们
  3. 可能有(但不一定是)另一方有兴趣监督一系列事件,以确保达到某种状态.

对于#1和#2,您需要某种持久的事件日志.传统的消息队列/主题可能会提供这一点,但您必须考虑消息可能无序处理到事务/异常/故障行为的情况.像Apache Bookkeeper,Apache Kafka,AWS Kinesis等更简单的日志可以按顺序存储/保存这些类型的事件,并让消费者按顺序处理/过滤掉重复/分区流等.

对我来说,3号是状态机.但是你实现状态机真的取决于你.基本上,这个状态机基于其他系统中的事件跟踪发生了什么事件并转换到允许状态(并且可能参与发射事件/命令).

例如,当您尝试关闭房屋时,真实世界的用例可能看起来像"托管".托管公司不仅处理金融交易,而且通常他们与房地产经纪人合作,协调订购文件,签署文件,转移资金等.每次事件后,托管变为"等待买方签名"状态"等待卖家签名"到"等待资金"到"封闭式成功"......他们甚至有这些事件发生的最后期限等,如果钱没有被转移到"交易结束",可以转移到另一个州没完成"还是什么的.

在您的示例中,此状态机将侦听pub/sub通道并捕获此状态,运行计时器,发出其他事件以进一步涉及所涉及的系统等.它本身并不一定"编排"它们,但它确实跟踪在必要时进展并执行超时和补偿.这可以作为流处理器,作为流程引擎实现,或者(最好的起点)只是一个简单的"托管"服务.

实际上,如果"托管"服务出现故障或失败,它将如何处理重复项,如何处理意外事件(如果它处于状态状态,它如何导致重复事件等等),那么实际上还有更多内容需要跟踪...希望足以开始.


Gre*_*mer 2

我将给出建筑师的答案,而不是深入探讨细节。我希望你不介意。

第一个建议是解耦所有概念:事件、消息、总线和/或队列和异步。如果您尚未决定用于实现总线的软件,这就带来了可能性。

从架构的角度来看,如果您需要“必须交付”类型的场景,那么您将在服务失败时保留消息。是的,当事情发生时,您可能需要对系统进行某种清理,但首先要关注保证交付问题。我看到两个可以扩展的基本选项(可能还有更多,但这些足以开始思考问题)。

  • 库存服务处理从队列中提取消息。在此方法中,服务会重新启动并查找任何消息。
  • “巴士”保证交付。当出现故障时,它会等待直到服务恢复(可以 ping 查看是否再次恢复或服务可以重新注册为订阅者(企业服务总线类型的场景)。

仅仅因为系统是异步的并且基于事件并不意味着您无法实现某种类型的保证交付。队列是一种选择(您似乎放弃了这个想法?),但在失败时持续存在并在订阅者再次启动后重试的总线是另一种选择。而且你可以坚持下去而不会阻塞。

另一个问题是消息使用什么令牌将它们同步回手头的业务功能,但我假设您已经在系统中以某种方式处理了这个问题。您可能没有的唯一概念是让系统都尊重令牌并尊重其他系统在发生故障时返回消息。

请注意,从业务角度来看,异步通信并不意味着在联系时即发即忘。您可以返回消息,而无需对每一位信息使用异步方法。我的意思是,启动的库存系统可能会处理一条消息并发送到 UI 端的应用程序,并且它可以返回“算了,你太慢了”,因此交易返回到其原始状态(不存在?) 。

我没有足够的信息(或时间?)来建议哪种方法最适合您的架构,因为细节仍然有点太高了,但希望这能引起一些思考。

希望这是有道理的,因为我基本上是在多动症状态下进行了大脑到键盘的操作。;-)