首页 > 解决方案 > 消息幂等性 - 排序注意事项

问题描述

假设我们有一个系统,有一个生产者将消息排入队列,并且同一消费者的多个实例处理此事件。由于我们处于竞争消费者模式,我们知道不再保证订购。这意味着我们必须确保我们的消息是幂等的。

根据我在此处阅读的内容(在消息排序要点下),我们必须确保消息处理是幂等的。

以下是问题:

一个例子:假设我们有一个“用户创建”和一个“用户删除”消息(或任何其他需要按顺序处理的事件)。如果我们在“用户创建”之前处理“用户已删除”,则不会删除用户。即使他们在事件队列中排序。真的可以将幂等处理/幂等事件提供给已删除的用户吗?

另一个例子。假设我们有一个具有score属性的实体。用户可以修改分数。第二个服务使用“得分实体”服务的事件,如果得分达到 100,则该实体(或实体引用)由第二个服务插入“最佳类别”实体中。如果分数达到-20,则第二个服务将分数实体插入“更差类别”中。如果“score 100”和“score -20”事件发生在很短的时间间隔内,那么拥有第二个服务的多个实例可能会产生不可预测的结果。关于如何设计“分数 x”事件或如何处理这些事件的任何想法?

非常感谢你的帮助!

标签: eventsmessagingevent-sourcingidempotent

解决方案


我们如何设计我们的消息处理是幂等的?

你应该:

  • 忽略您已经“看到”的消息;这意味着消费者应该有一种检测方式;例如,它可以保留已处理的消息 ID 列表(这意味着每条消息都应该有一个唯一的 ID)。
  • 如果消息没有改变状态,则不抛出异常;例如,如果您收到第二个DeleteUser事件(因此用户已被删除,第二次删除应该没有副作用),那么您将忽略它。并非每个事件都可以是幂等的,例如UpdateUserName不应该是幂等的。

如果我们将每个事件保存在事件存储中,在设计每个事件的有效负载和事件聚合以获取聚合状态时是否需要考虑任何因素?

您应该根据您的领域设计事件;有效载荷不应包含超过域所需的信息。如果附加信息会使您的读取模型更容易实现,那么您可以将其添加到有效负载中,但要小心以某种方式将其标记为冗余。

一个例子:假设我们有一个“用户创建”和一个“用户删除”消息(或任何其他需要按顺序处理的事件)。如果我们在“用户创建”之前处理“用户已删除”,则不会删除用户。即使他们在事件队列中排序。真的可以将幂等处理/幂等事件提供给已删除的用户吗?

在这种特殊情况下,您可以拥有额外的已删除用户集合;您只能保留他们的 ID。当事件到达时,您可以通过查看集合CreateUser来检查用户是否已被删除,如果用户在那里,则忽略它。DeletedUsers您可以忽略该用户的所有其他事件。

此解决方案非常依赖于域。

另一个例子。假设我们有一个具有 score 属性的实体。用户可以修改分数。第二个服务使用“得分实体”服务的事件,如果得分达到 100,则该实体(或实体引用)由第二个服务插入“最佳类别”实体中。如果分数达到-20,则第二个服务将分数实体插入“更差类别”中。如果“score 100”和“score -20”事件发生在很短的时间间隔内,那么拥有第二个服务的多个实例可能会产生不可预测的结果。关于如何设计“分数 x”事件或如何处理这些事件的任何想法?

这种情况可以通过保持 in-the/attached-to readmodels(两个集合bestworse)时间戳/顺序/流版本/最后处理的事件的任何内容并忽略每个小于或等于该事件的事件来解决时间戳。这样,如果在“score -20”之后发出“score 100”但首先到达,则您应该忽略“score -20”,因为它具有较低的时间戳,尽管它排在最后。

此解决方案是通用的,但它依赖于某些排序的存在。


推荐阅读