【问题标题】:Message idempotence - ordering considerations消息幂等性 - 排序注意事项
【发布时间】:2018-10-05 16:33:54
【问题描述】:

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

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

问题如下:

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

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

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

非常感谢您的帮助!

【问题讨论】:

    标签: events messaging event-sourcing idempotent


    【解决方案1】:

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

    你应该:

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

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

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

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

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

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

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

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

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

    【讨论】:

    • 所以,基本上一切都取决于你在做什么。您实施的每个解决方案都是不同的,不是吗?最后一个问题。在竞争消费者模式中,当两个事件(包含时间戳元数据)一个接一个地发出但它们被乱序接收时,它会改变你之前所说的任何内容吗?从我的角度来看,无序处理或无序接收(或两者)在竞争消费者模式中给出了相同的情况,对吗?
    • @ChristianPaesante 通常,您需要在每个用例中思考并选择最佳解决方案。我观察过两种情况:这取决于这两个事件是相互依赖的还是独立的。例如,一个可能是由另一个(直接或间接)引起的。独立事件在无序接收时不应引起问题。应根据用例对相互依赖的事件进行特殊处理。您可以忽略较早出现的较新事件(例如在 DeleteUser 之后出现的 CreateUser),或者您的 readmodel 可能会在旧事件出现晚于正常情况时赶上
    • @ChristianPaesante 总之,我认为这个无序问题没有通用的解决方案。
    猜你喜欢
    • 2010-11-14
    • 2018-08-14
    • 2011-07-09
    • 2017-01-24
    • 1970-01-01
    • 1970-01-01
    • 2020-06-23
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多