elasticsearch CQRS:在ElasticSearch读取模型中投影无序通知
我们有一个微服务架构,并应用了CQRS模式。发送到微服务的命令触发应用程序状态更改,并在我们的Kafka总线上发出相应事件。我们将这些事件投影到使用ElasticSearch构建的读取模型中 到目前为止,一切顺利 我们的微服务最终是一致的。但在任何特定时间,它们都不是(必要的)。因此,它们发送的事件也并不总是彼此一致
elasticsearch CQRS:在ElasticSearch读取模型中投影无序通知,
elasticsearch,apache-kafka,domain-driven-design,microservices,cqrs,
elasticsearch,Apache Kafka,Domain Driven Design,Microservices,Cqrs,我们有一个微服务架构,并应用了CQRS模式。发送到微服务的命令触发应用程序状态更改,并在我们的Kafka总线上发出相应事件。我们将这些事件投影到使用ElasticSearch构建的读取模型中 到目前为止,一切顺利 我们的微服务最终是一致的。但在任何特定时间,它们都不是(必要的)。因此,它们发送的事件也并不总是彼此一致 此外,为了保证应用程序状态更改和相应事件的发射之间的一致性,我们在同一事务中保留新状态和相应事件(我知道我们可以使用事件源,避免完全保留状态)。然后,异步工作者负责在Kafka总线
此外,为了保证应用程序状态更改和相应事件的发射之间的一致性,我们在同一事务中保留新状态和相应事件(我知道我们可以使用事件源,避免完全保留状态)。然后,异步工作者负责在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”
- 删除C
- 将B绑定到C
具体地说,我们在ElasticSearch文档中预测这些事件时遇到困难,因为这些事件有时引用不再存在或还不存在的聚合。任何帮助都将不胜感激。我不认为您提出的问题仅限于系统的投影部分-它也可能发生在微服务A、B和C之间 通常,投影仪与B同时创建
C
。只有这样,B才能将自己绑定到C,这使得您提到的特定顺序不可能发生在投影仪上
但是,您可以说消息可能以错误的顺序到达,例如,如果B和C之间的网络通信比C和投影仪之间的网络通信快得多
我从未遇到过这样的问题,但我想到了几个选择:
- 不要在读取模型级别强制执行“外键”。存储B和它的C引用,即使您现在对C知之甚少。换句话说,使
并使B绑定到C
C创建的
可交换
- 向您的事件中添加一个。这允许客户端识别和处理无序消息。您可以选择自己的策略—拒绝、等待因果事件到来、无论如何都要尝试处理,等等。不过,这并不是很容易实现的
- 消息传递平台可以保证在特定条件下订购。你提到卡夫卡,在同一个主题和分区下。RabbitMQ,我认为,有更强大的先决条件 我不是一个消息传递专家,但看起来微服务间通信场景的可行性是有限的。这似乎也与当前的最终一致性趋势背道而驰,即我们倾向于使用交换操作(参见CRDT)而不是确保总顺序