【问题标题】:Data consistency between Microservices with RabbitMq使用 RabbitMq 的微服务之间的数据一致性
【发布时间】:2019-09-23 11:40:57
【问题描述】:

我正在尝试为我公司处理订单管理的一些简单内部系统设置一个实用的微服务演示,但是我很难理解大规模微服务之间的数据一致性。

我已经为微服务确定了一个简单的场景 - 我们正在运行的当前应用程序在我们的网站上处理订单并更新客户的“帐户信用”时接受订单 - 基本上是他们可以在他们的帐户之前在我们那里花费的未付款项需要审核。

我试图将这个非常简单的需求分解为几个微服务。它们的定义如下:

API 提供各种不同级别的功能 - 它允许我们创建新客户,这会触发以下操作:

使用SQL,我们可以在数据库内工作时做一些乐观查询,通过扩展微服务(EG:Order Microservice的两个实例,每个微服务,但不微服务的每个实例都有自己的数据库)。

例如,我们可以执行以下操作并假设 SQL 将管理锁定,这意味着当同时处理两个订单时,数字应该以正确的数字结束:

UPDATE [orderms].[customers] SET CreditLimit = CreditLimit - 100, NoOfOrders = NoOfOrders + 1 WHERE CustomerId = 1

根据上述情况,如果信用是 1000,并且处理了 2 个 100 的订单,并且每个订单被分配到“订单”微服务的不同实例,我们应该能够假设正确的数字将出现在订单微服务中的客户表(基于 MSSQL 查询的锁定应自动处理此问题)。

当我们尝试将它们集成回客户微服务时,问题就来了。我们将有两条消息,来自订单微服务的每个实例,作为事件传递,示例如下:

鉴于上述情况 - 我们很可能会按照以下方式更新“客户”SQL 表(这是两个查询):

UPDATE [customerms].[customers] SET CreditLimit = 900.00 WHERE CustomerId = 1
UPDATE [customerms].[customers] SET CreditLimit = 800.00 WHERE CustomerId = 1

但是 - 根据这些“客户”微服务的运行速度,实例 #1 目前可能正在创建多个新客户,因此处理此请求的速度可能比实例 #2 慢,这意味着 SQL 查询将无序执行,因此我们将留下 CreditLimit 为 800(正确)的“Order”数据库和 CreditLimit 为 900(不正确)的客户微服务。

在单体应用程序中,如果确实需要,我们通常会添加一个锁定元素(或可能是互斥锁),否则将根据订单微服务中的功能依赖 SQL 锁定,但由于这是一个分布式过程,这些旧方法都不适用。

有什么建议吗?我似乎无法以某种方式看到过去?

【问题讨论】:

    标签: rabbitmq microservices


    【解决方案1】:

    我认为的解决方案

    在两个微服务中保持初始信用额度。假设初始信用额度为 1000,并将其设置在两个微服务数据库中。处理订单时,首先在订单微服务处减少它,而不是发送信用额度(800、900 或任何金额),而是发送必须从客户微服务信用额度中减少的金额。

    假设您处理了两个订单,每个订单价值 100。首先降低订单微服务的信用额度,然后生成两个事件,每个事件 100 美元将由客户服务消费并在此结束。这样,无论它们到达的顺序如何,您只需减少该数量即可。

    事件溯源(更好的方法)

    带有乐观锁定的事件溯源是您可以采用的方法。在这种模式下,您无需保存实体的特定状态,而是将所有事件保存在数据库中。这是一个仅附加的存储,事件按照它们到达的顺序存储。在这种情况下,您需要重播事件以达到特定状态的信用额度。在这种情况下,您将拥有所有历史记录。请记住,如果出现任何不一致的情况,您没有任何日志可以回退,但有了事件溯源,您就有了。

    【讨论】:

    • 谢谢你 - 我完全同意“我认为的解决方案”部分,这种方法在这个例子中创造了奇迹,但是当我们想要发送一些不是的东西时它不起作用一个数字(例如:日期或字符串),因为我们仍然需要覆盖记录。事件溯源是一种事件驱动微服务的反模式。我将无法扩展“回放”这些事件的服务,因为它们可能以错误的顺序执行,因此假设我有一个永远无法扩展的服务/微服务(只是扩展)
    • 我想我真正需要的是rabbitmq上的一个锁定机制——在大多数例子中,我们只需要锁定相对记录——在这个例子中是CustomerId,其他客户可以并行处理,但是只有在给定 CustomerId 的前一个 Event 已完成处理时,才应处理相同 CustomerId 的记录/事件。
    • 感谢回复,我喜欢健康讨论。事件溯源只有在不符合情况的情况下才是反模式。事件溯源与读取投影(物化视图)和单独的数据库一起使用,如果正确实施,它可以很好地工作和扩展。重播事件的细微变化是您可以按照事件生成的顺序重播事件,并且涵盖了您的日期/字符串问题。锁定更像是绷带,但不是解决方案。同时,如果您有十几个锁,我认为这不是正确的方法。
    • 确实 - 感谢您进一步的 cmets。我想我正试图不离开 RabbitMq 并重新发明轮子。还有许多其他基于微服务的应用程序,他们肯定已经解决了这个简单的问题,而无需借助多个队列/回放实现?
    • 当然可以。让我知道如何解决这个问题
    猜你喜欢
    • 2022-10-12
    • 1970-01-01
    • 2015-09-15
    • 2021-09-17
    • 1970-01-01
    • 2022-10-16
    • 2017-05-29
    • 2022-01-27
    相关资源
    最近更新 更多