消息幂等性——排序注意事项

Message idempotence - ordering considerations

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

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

问题如下:

一个例子:假设我们有一个 "User Created" 和一个 "User Deleted" 消息(或任何其他需要处理的事件)命令)。如果我们在 "User created" 之前处理 "User deleted",用户将不会被删除。即使他们是在事件队列中订购的。幂等 processing/idempotent 事件真的可以给已删除的用户吗?

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

非常感谢您的帮助!

How can we design our message processing to be idempotent?

你应该:

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

If we are saving every event in an event store, are there any consideration to be taken into account when designing each event's payload and the events aggregation to get the aggregate state?

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

An example: let's say that we have a "User Created" and a "User Deleted" messages (or any other couple of events which NEED to be processed in order). If we process "User deleted" before "User created" the user won't be deleted. Even if they're coming ordered in the event queue. Can really an idempotent processing/idempotent events give to a deleted user?

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

此解决方案非常依赖域。

Another example. Let's suppose that we have an entity that have a score attribute. An user can modify the score. A second service consumes events of the "score entity" service and if the score reaches 100 the entity (or an entity reference) is inserted by the second service in the "Best category" entity. If the score reaches -20 the second service insert the score entity in the "Worse category". Having multiple instace of the second service can give an impredictable result if the "score 100" and "score -20" events are within a tiny interval of time. Any ideas on how to design the "score x" events or how to process these events?

这种情况可以通过将 in-the/attached-to readmodels(两个集合,bestworse)保留为最后处理事件的 timestamp/order/stream-version/whatever 并忽略每个事件来解决小于或等于该时间戳。这样,如果 "score 100" 在 "score -20" 之后发出但先到达,您应该忽略 "score -20" 因为它具有较低的时间戳,尽管它是最后一个。

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