【问题标题】:Microservices: how to handle foreign key relationships微服务:如何处理外键关系
【发布时间】:2017-12-05 19:55:59
【问题描述】:

微服务架构建议每个服务都应该处理自己的数据。因此,任何依赖于其他服务(服务 B)拥有的数据的服务(服务 A)都不应通过直接 DB 调用,而是通过第二个服务(服务 B)提供的 api 来访问这些数据。

那么微服务最佳实践对检查外键约束有何建议。

示例:我正在为产品开发一个交付功能(微服务 1),并且某些产品只能交付到产品表中提到的某些位置,该产品表仅可访问产品微服务(微服务 2)。

如何确保微服务 1(即交付功能)不会将订单送到无人服务的位置。我有这个问题,因为交付功能不能直接访问产品数据库,因此当交付订单放入交付数据库时,数据库级别没有适用的约束(无法检查产品数据库中是否存在外键匹配或表格)。

【问题讨论】:

    标签: database microservices


    【解决方案1】:

    可以为多个微服务使用共享数据库。您可以在此链接中找到微服务数据管理的模式:http://microservices.io/patterns/data/database-per-service.html。顺便说一下,这是一篇非常有用的微服务架构博客。

    在您的情况下,您更喜欢按服务模式使用数据库。这使微服务更加自主。在这种情况下,您应该在多个微服务之间复制一些数据。您可以通过微服务之间的 api 调用共享数据,也可以通过异步消息传递来共享数据。这取决于您的基础架构和数据更改的频率。如果它不经常更改,您应该使用异步事件复制数据。

    在您的示例中,送货服务可以复制送货地点和产品信息。产品服务管理产品和位置。然后使用异步消息将所需数据复制到交付服务的数据库(例如,您可以使用 rabbit mq 或 apache kafka)。送货服务不会更改产品和位置数据,但会在工作时使用这些数据。如果交付服务使用的部分产品数据经常变化,异步消息传递的数据复制将非常昂贵。在这种情况下,您应该在 Product 和 Delivery 服务之间进行 api 调用。交付服务要求产品服务检查产品是否可以交付到特定位置。送货服务向产品服务询问产品和位置的标识符(名称、ID 等)。这些标识符可以从最终用户那里获取,也可以在微服务之间共享。因为这里微服务的数据库不同,所以我们不能在这些微服务的数据之间定义外键。

    Api 调用可能更容易实现,但此选项的网络成本更高。此外,当您进行 api 调用时,您的服务的自主性也较低。因为,在您的示例中,当产品服务关闭时,交付服务无法完成其工作。如果您使用异步消息复制数据,则进行交付所需的数据位于交付微服务的数据库中。当产品服务不工作时,您将能够交付。

    【讨论】:

    • 很好的答案。我使用 API 调用,但它还需要对来自另一个服务的数据进行排序和分页。你知道这种情况的最佳方法吗?
    • 您应该将与分页和排序相关的参数添加到您的api中。然后,获取具有正确顺序的正确页面的责任将由 api 的消费者承担。有一些技术用于定义像 GraphQL 这样的 api。据我所知,这些技术已经具有排序和分页功能。如果您不使用这种技术,您可以简单地从您的客户端获取参数并使用它们返回您的按页面排序的数据。
    • 确实是个好答案!
    • 但是,你保留外键吗?例如:每篇博文都会有很多 cmets。 Monolith 将有 cmets 表,其中包含博客文章的外键。但是在微服务中,我们将有两个服务。服务 1:使用这些表字段(PostID、名称、内容)发布 Microservie 服务 2:使用这些表字段(CommentID、PostID、Cpmment)评论 Microservie 问题是,我们在服务 2(评论微服务)中是否需要“PostID”?我想答案是肯定的,因为我们需要知道哪个评论属于哪个帖子。我的理解正确吗?
    • 如何将系统划分为微服务完全是另一回事,但如果您决定创建 2 个微服务,如帖子和评论,您需要 cmets 微服务上的帖子标识符,因为每个评论都属于一个帖子。但是,这并不意味着您需要在这些表之间定义 FK。 FK 只是 RDBMS 世界中的一个约束,它有助于确保数据的完整性和一致性。如果您将这些微服务的数据保存在单独的模式中,您将无法定义 FK,甚至您可以将数据保存在 FK 不适用的 nosql 数据库(这对 cme​​ts 微服务有意义)中。
    【解决方案2】:

    在分发代码以减少耦合时,您希望避免资源共享,而数据是您希望避免共享的资源。

    另一点是系统中只有一个组件拥有数据(用于状态更改操作),其他组件可以读取但不能写入,它们可以拥有数据的副本,或者您可以共享它们可以用来获取的视图模型对象的最新状态。

    引入引用完整性将重新引入耦合,而不是您想为主键使用 Guid 之类的东西,它们将由对象的创建者创建,剩下的就是管理最终一致性。

    看看Udi Dahan的talk in NDC Oslo for a more details

    希望对你有帮助

    【讨论】:

    • Udi Dahan 演讲的链接很有意思
    【解决方案3】:

    第一个解决方案:API Composition

     Implement a query by defining an API Composer, which invoking the
     services that own the data and performs an in-memory join of the
     results
    

    第二种解决方案:CQRS

    Define a view database, which is a read-only replica that is designed to support that 
    query. The application keeps the replica up to data by subscribing to Domain events 
    published by the service that own the data.
    

    【讨论】:

    • 这个答案与问题无关。这不是检索数据,而是确保一致性。
    【解决方案4】:

    此答案的 2020 年更新是使用 Debezium 等变更数据捕获工具。 Debezium 将监视您的数据库表的更改并将它们流式传输到 Kafka/Pulsar(其他管道),然后您的订阅者可以捕获更改并同步它们。

    【讨论】:

      【解决方案5】:

      ...我如何确保微服务 1(即交付功能)不会将订单带到未提供服务的位置...

      您不是在线进行,而是以延迟的方式进行。

      您的服务 #1 收到订单,执行它自己可以执行的所有验证,然后保存它。延迟服务,处理订单并稍后验证订单的其他方面。一旦发现该位置无法使用,它可能会以被拒绝的形式返回。您的服务需要优雅地通知用户,甚至可能取消订单。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2017-07-20
        • 2019-12-03
        • 2017-11-09
        • 2022-01-02
        • 1970-01-01
        • 2018-11-28
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多