elasticsearch CQRS:在ElasticSearch读取模型中投影无序通知,elasticsearch,apache-kafka,domain-driven-design,microservices,cqrs,elasticsearch,Apache Kafka,Domain Driven Design,Microservices,Cqrs" /> elasticsearch CQRS:在ElasticSearch读取模型中投影无序通知,elasticsearch,apache-kafka,domain-driven-design,microservices,cqrs,elasticsearch,Apache Kafka,Domain Driven Design,Microservices,Cqrs" />

elasticsearch CQRS:在ElasticSearch读取模型中投影无序通知

elasticsearch CQRS:在ElasticSearch读取模型中投影无序通知,elasticsearch,apache-kafka,domain-driven-design,microservices,cqrs,elasticsearch,Apache Kafka,Domain Driven Design,Microservices,Cqrs,我们有一个微服务架构,并应用了CQRS模式。发送到微服务的命令触发应用程序状态更改,并在我们的Kafka总线上发出相应事件。我们将这些事件投影到使用ElasticSearch构建的读取模型中 到目前为止,一切顺利 我们的微服务最终是一致的。但在任何特定时间,它们都不是(必要的)。因此,它们发送的事件也并不总是彼此一致 此外,为了保证应用程序状态更改和相应事件的发射之间的一致性,我们在同一事务中保留新状态和相应事件(我知道我们可以使用事件源,避免完全保留状态)。然后,异步工作者负责在Kafka总线

我们有一个微服务架构,并应用了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有一个属性“name”
其中一个用例是通过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文档中预测这些事件时遇到困难,因为这些事件有时引用不再存在或还不存在的聚合。任何帮助都将不胜感激。

    我不认为您提出的问题仅限于系统的投影部分-它也可能发生在微服务A、B和C之间

    通常,投影仪与B同时创建
    C
    。只有这样,B才能将自己绑定到C,这使得您提到的特定顺序不可能发生在投影仪上

    但是,您可以说消息可能以错误的顺序到达,例如,如果B和C之间的网络通信比C和投影仪之间的网络通信快得多

    我从未遇到过这样的问题,但我想到了几个选择:

    • 不要在读取模型级别强制执行“外键”。存储B和它的C引用,即使您现在对C知之甚少。换句话说,使
      B绑定到C
      并使
      C创建的
      可交换

    • 向您的事件中添加一个。这允许客户端识别和处理无序消息。您可以选择自己的策略—拒绝、等待因果事件到来、无论如何都要尝试处理,等等。不过,这并不是很容易实现的

    • 消息传递平台可以保证在特定条件下订购。你提到卡夫卡,在同一个主题和分区下。RabbitMQ,我认为,有更强大的先决条件

      我不是一个消息传递专家,但看起来微服务间通信场景的可行性是有限的。这似乎也与当前的最终一致性趋势背道而驰,即我们倾向于使用交换操作(参见CRDT)而不是确保总顺序


    “但是,由于每个微服务都有自己的事件表和异步工作程序,我们无法保证事件将按照其各自微服务中相应状态更改发生的顺序发送”-为什么?工作人员不是按照事件被持久化的顺序处理事件吗?@Odsh您的问题似乎是索引器/投影仪没有像其他微服务一样在同一时间收到事件通知。也就是说,如果我正确解释了您的事件序列-是吗?@Constantin,每个微服务都有自己的数据库、自己的事件表和自己的工作程序。特定的工作进程按照事件被持久化的顺序处理事件,但不同事件表上的不同工作进程会这样做