会话事务消费者或生产者中的消息代理异常处理

Message broker exception handling in session transacted consumer or producer

我想在我的 Spring 引导微服务中使用 SAGA 模式。例如,在客户订单中,当订单创建时,会产生一个像 OrderCreatedEvent 这样的事件,然后在客户微服务中 OrderCreatedEvent 上的侦听器会更新客户信用并产生 CreditUpdateEvent 和...。

我使用 session transacted JmsTemplate 来制作事件。在 JmsTemplate 的 javadoc 中说 JMS 事务在主事务之后提交:

This has the effect of a local JMS transaction being managed alongside the main transaction (which might be a native JDBC transaction), with the JMS transaction committing right after the main transaction.

现在我的问题是如何处理以下情况:

主事务已提交(例如已提交的订单已提交)但系统无法提交 JMS 事务(出于任何原因)。

我想使用 SAGA 而不是两阶段提交,但我认为 SAGA 将问题从订单和客户服务转移到订单服务和 JMS 提供商。

SAGA 提示问题:

There are also the following issues to address:

...

  • In order to be reliable, a service must atomically update its database and publish an event. It cannot use the traditional mechanism of a distributed transaction that spans the database and the message broker. Instead, it must use one of the patterns listed below.

...

The following patterns are ways to atomically update state and publish events:

  • Event sourcing
  • Application events
  • Database triggers
  • Transaction log tailing

Event Sourcing is special in this list as it brings radical change on how your system stores and processes data. Usually, systems store only the current state of the entities. Some systems add explicit support for historical states with validity periods and/or bitemporal data.

基于事件溯源的系统以允许它从事件重建状态的方式存储事件序列而不是实体状态。只有一个事务资源需要维护——事件存储——所以不需要协调事务。

列表中的其他模式通过要求事件生成器代码将所有更改(实体状态和事件(作为实体))提交到单个数据存储来避免事务协调问题。然后实现一个专用但独立的机制——事件发布者——从数据存储中获取事件并将它们发布给事件消费者。

事件发布者需要跟踪已发布/未发布的事件,这通常会带来协调交易的问题。这就是事件消费者暴露出来的幂等性。事件发布者将从最后已知位置重播事件,而消费者将忽略重复项。

您还可以颠倒事件生产者和事件消费者的主动/被动方面。事件生产者将实体状态和事件(作为实体)存储到单个数据存储中,并提供允许事件消费者访问事件流的端点。每个事件消费者都会跟踪已处理/未处理的事件——出于幂等性的原因,它无论如何都需要这样做——但仅限于它感兴趣的事件流。 REST in Practice 一书第 7 章和第 8 章对这种方法给出了很好的解释。

对于 SAGA,您希望分 3 个阶段拆分或重新排序您的交易 (tx) 步骤:

  1. 您可以执行补偿操作的 Tx 个步骤。对于每个 T1..N 你有一个 C1..N
  2. Tx 无法补偿的步骤。如果他们失败了那么你之前触发 定义 C1..N
  3. 始终成功的可重试 Tx 步骤。

SAGA 不是 ACID,只是 ACD。您需要实现自己的隔离,以防止脏读。通常带锁。

为什么选择 SAGA?避免同步运行时耦合和倒可用性。您等待最后一个参与者提交。

这是一个相当高的代价。

机会很小,但您仍然可能会遇到可能用于获取聚合的不一致事件。