Ods*_*dsh 5 domain-driven-design cqrs elasticsearch apache-kafka microservices
我们有一个微服务架构并应用CQRS模式.发送到微服务的命令触发应用程序状态更改以及在Kafka总线上发出相应事件.我们将这些事件投射到使用ElasticSearch构建的读取模型中.
到现在为止还挺好.
我们的微服务最终相互一致.但在任何时候,它们都不是(必然).因此,他们发送的事件也不总是彼此一致.
此外,为了保证应用程序状态更改与相应事件的发布之间的一致性,我们在同一事务中保持DB新状态和相应事件(我知道我们可以使用事件源并避免完全持久化状态).然后,异步工作人员负责在Kafka总线上发送这些事件.此模式保证每个状态更改至少发送一个事件(这不是问题,因为我们的事件是幂等的).但是,由于每个微服务都有自己的事件表和异步工作程序,因此我们无法保证事件将按照各自微服务中发生相应状态更改的顺序发送.
编辑:澄清一下,每个微服务都有自己的数据库,自己的事件表和自己的工作者.特定工作程序按照它们在相应事件表中持久化的顺序处理事件,但不同事件表上的不同工作程序(即不同的微服务)不提供此类保证.
在同一ElasticSearch文档中从不同的微服务中投射这些不连贯或无序事件时会出现问题.
一个具体的例子:让我们想象一下由不同的微服务管理的三种不同的聚合A,B和C(在域驱动设计意义上的聚合):
其中一个用例是通过ElasticSearch查找绑定到聚合B的所有聚合A,聚合B又绑定到具有特定名称的聚合C.
如上所述,单独的事件表和工作人员可能在来自不同微服务的事件发射之间引入可变延迟.创建A,B和C并将它们绑定在一起可能会导致以下事件序列:
一批事件的另一个例子:假设我们最初有聚合B和C,并且同时发出两个命令:
这可能导致事件:
具体而言,我们无法在ElasticSearch文档中投影这些事件,因为事件有时会引用不再存在或尚不存在的聚合.任何帮助,将不胜感激.
我不认为你提出的问题是系统的投影部分所独有的 - 它也可能发生在微服务 A、B 和 C 之间。
通常情况下,投影仪C created与 B 的时间相同。只有这样B才能将自己绑定到C,这使得你提到的特定顺序不可能发生在投影仪上。
但是,您的说法是对的,例如,如果 B 和 C 之间的网络通信比 C 和投影仪之间的网络通信快得多,则消息可能会以错误的顺序到达。
我从未遇到过这样的问题,但我想到了一些选择:
不要在读取模型级别强制执行“外键”。即使您目前对 C 知之甚少,也请存储 B 及其 C 引用。换句话说,makeB bound to C和C created可交换的。
向您的事件添加因果关系 ID。这允许客户端识别并处理无序消息。您可以选择自己的策略 - 拒绝、等待因果事件到达、尝试处理等。不过,实现起来并不容易。
消息平台可以在一定条件下保证订单。您在同一主题和分区下提到了 Kafka。我认为 RabbitMQ 有更强大的先决条件。
我不是消息传递专家,但看起来可行的微服务间通信场景是有限的。这似乎也违背了当前最终一致性的趋势,我们倾向于支持交换操作(参见 CRDT)而不是确保总顺序。
| 归档时间: | 
 | 
| 查看次数: | 270 次 | 
| 最近记录: |