【发布时间】:2018-05-11 00:25:44
【问题描述】:
我们有一个微服务架构并应用 CQRS 模式。发送到微服务的命令会触发应用程序状态更改并在我们的 Kafka 总线上发出相应的事件。我们将这些事件投射到使用 ElasticSearch 构建的读取模型中。
到目前为止,一切都很好。
我们的微服务最终彼此一致。但在任何给定时间,它们都不是(必要的)。因此,它们发送的事件也不总是彼此一致。
此外,为了保证应用程序状态更改和相应事件的发射之间的一致性,我们在数据库中将新状态和相应事件持久保存在同一事务中(我知道我们可以使用事件源并避免持久化整个国家)。然后一个异步工作者负责在 Kafka 总线上发送这些事件。这种模式保证每次状态变化都会发送至少一个事件(这不是问题,因为我们的事件是幂等的)。但是,由于每个微服务都有自己的事件表和异步工作器,我们不能保证事件会按照各自微服务发生相应状态变化的顺序发送。
编辑:澄清一下,每个微服务都有自己的数据库、自己的事件表和自己的工作者。特定的工作人员按照事件在其相应事件表中的持久化顺序处理事件,但不同事件表上的不同工作人员,即不同的微服务,不提供这样的保证。
当从同一个 ElasticSearch 文档中的不同微服务中投射这些不连贯或无序的事件时,就会出现问题。
一个具体的例子:让我们想象三个不同的聚合 A、B 和 C(领域驱动设计意义上的聚合)由不同的微服务管理:
- A 和 B 之间存在多对多关系。聚合 A 引用了他绑定到的聚合根 B,但 B 不知道它与 A 的关系。当 B 被删除时,管理 A 的微服务会监听相应的事件并撤消 A 与 B 的绑定。
- 类似地,B 和 C 之间存在多对多关系。B 知道所有相关的 C 聚合,但反之则不成立。当 C 被删除时,管理 B 的微服务会监听相应的事件,并解除 B 与 C 的绑定。
- C 有一个属性“名称”。
其中一个用例是通过 ElasticSearch 查找绑定到聚合 B 的所有聚合 A,而聚合 B 又绑定到具有特定名称的聚合 C。
如上所述,单独的事件表和工作器可能会在不同微服务的事件发射之间引入可变延迟。例如,创建 A、B 和 C 并将它们绑定在一起可能会导致以下事件序列:
- B 已创建
- B 绑定到 C
- 用名称 XYZ 创建的 C
- 创建一个
- A 绑定到 B
另一个批处理事件的例子:假设我们最初有聚合 B 和 C,并且同时发出两个命令:
- 删除C
- 将 B 绑定到 C
这可能会导致以下事件:
- C 已删除
- B 绑定到 C
- B 未与 C 绑定(响应事件 1)
具体而言,我们无法将这些事件投射到 ElasticSearch 文档中,因为这些事件有时会引用不再存在或尚不存在的聚合。任何帮助将不胜感激。
【问题讨论】:
-
“但是,由于每个微服务都有自己的事件表和异步工作者,我们不能保证事件会按照各自微服务发生相应状态更改的顺序发送” - 为什么?工作人员不是按照事件被持久化的顺序处理事件吗?
-
@Odsh 您的问题似乎是索引器/投影仪没有像其他微服务一样同时收到事件通知。也就是说,如果我正确地解释了你的事件顺序 - 我是吗?
-
@Constantin,每个微服务都有自己的数据库、自己的事件表和自己的工作者。特定的工作人员按照它们被持久化的顺序处理事件,但不同事件表上的不同工作人员不提供这样的保证。
-
@Guillaume,我明白你的意思了。如果管理 B 的微服务仅在收到“C 创建”事件且没有“C 删除”事件的情况下才允许将 B 绑定到 C,那么确实只有在索引器/投影仪在不同的顺序。我们不做那种验证。但即使我们这样做了,除非微服务 B 和 C 将他们的事件发布在同一个 Kafka 主题和分区中(这甚至可能/推荐吗?),我相信是的,这仍然可能发生。
-
是的,这正是我的意思。不过有些事情让我很烦——如果 B 的微服务没有收到“C created”事件,它怎么会知道它应该将 B 绑定到 C?这似乎是事件驱动架构中的异常情况。
标签: elasticsearch apache-kafka domain-driven-design microservices cqrs