你当前正在访问 Microsoft Azure Global Edition 技术文档网站。 如果需要访问由世纪互联运营的 Microsoft Azure 中国技术文档网站,请访问 https://docs.azure.cn

微服务的数据注意事项

Azure DevOps

本文介绍在微服务体系结构中管理数据的注意事项。 由于每个微服务管理自己的数据,因此数据完整性和数据一致性是关键挑战。

微服务的基本原则是每个服务管理自己的数据。 两个服务不应共享数据存储。 相反,每个服务都负责自己的专用数据存储,其他服务无法访问这些存储。

此规则的原因是避免服务之间的意外耦合,这可能会导致服务共享相同的基础数据架构。 如果数据架构发生更改,则必须跨依赖于该数据库的每个服务协调更改。 通过隔离每个服务的数据存储,我们可以限制更改的范围,并保留真正独立的部署的灵活性。 另一个原因是每个微服务可能都有自己的数据模型、查询或读/写模式。 使用共享数据存储会限制每个团队优化其特定服务的数据存储的能力。

#B0 #A1 CQRS #A2 #C3 错误方法的关系图

此方法自然会导致 polyglot 持久性 - 在单个应用程序中使用多个数据存储技术。 一个服务可能需要文档数据库的架构读取功能。 另一个可能需要 RDBMS 提供的引用完整性。 每个团队都可以自由地为其服务做出最佳选择。

备注

服务可以共享同一物理数据库服务器。 当服务共享同一架构或读取和写入同一组数据库表时,会出现此问题。

挑战

这种分布式管理数据的方法提出了一些挑战。 首先,数据存储之间可能存在冗余,同一项数据出现在多个位置。 例如,数据可能作为事务的一部分存储,然后存储在其他位置进行分析、报告或存档。 重复或分区的数据可能会导致数据完整性和一致性问题。 当数据关系跨越多个服务时,不能使用传统的数据管理技术来强制实施关系。

传统数据建模使用“一个事实在一个地方”的规则。每个实体在架构中完全显示一次。 其他实体可以保留对该实体的引用,但不能重复它。 传统方法的明显优势在于,更新是在单个位置进行的,从而避免了数据一致性问题。 在微服务体系结构中,必须考虑如何在服务之间传播更新,以及如何在多个位置显示数据时管理最终一致性,而不会保持高度一致性。

管理数据的方法

在所有情况下,没有一种正确的方法,但下面是在微服务体系结构中管理数据的一些一般准则。

  • 定义每个组件所需的一致性级别,尽可能首选最终一致性。 了解系统中需要强一致性或 ACID 事务的位置,以及可接受最终一致性的位置。 查看 使用战术 DDD 设计微服务, 以获取进一步的组件指南。

  • 需要强大的一致性保证时,一个服务可能表示给定实体的真相来源,该实体通过 API 公开。 其他服务可能保存其自己的数据副本或数据子集,该副本最终与主数据一致,但未被视为事实来源。 例如,假设一个具有客户订单服务和推荐服务的电子商务系统。 建议服务可能会侦听来自订单服务的事件,但如果客户请求退款,则它是订单服务,而不是建议服务,具有完整的交易历史记录。

  • 对于事务,请使用 计划程序代理主管补偿事务 等模式,使数据在多个服务之间保持一致。 可能需要存储一个额外的数据片段,用于捕获跨多个服务的工作单元的状态,以避免多个服务出现部分故障。 例如,在多步骤事务正在进行时,将工作项保留在持久队列上。

  • 仅存储服务所需的数据。 服务可能只需要有关域实体的信息子集。 例如,在“发货”边界上下文中,我们需要知道哪个客户与特定交付相关联。 但是,我们不需要客户的帐单地址 ,这是由帐户边界上下文管理的。 仔细考虑域并使用 DDD 方法可在此处提供帮助。

  • 考虑你的服务是否一致且松散耦合。 如果两个服务不断相互交换信息,从而导致聊天 API,则可能需要通过合并两个服务或重构其功能来重新绘制服务边界。

  • 使用 事件驱动的体系结构样式。 在此体系结构样式中,服务在其公共模型或实体发生更改时发布事件。 感兴趣的服务可以订阅这些事件。 例如,另一个服务可以使用事件来构造更适合查询的数据的具体化视图。

  • 拥有事件的服务应发布一个架构,该架构可用于自动序列化和反序列化事件,以避免发布服务器和订阅服务器之间的紧密耦合。 考虑 JSON 架构或 框架,例如 Microsoft Bond、Protobuf 或 Avro。

  • 大规模情况下,事件可能会成为系统上的瓶颈,因此请考虑使用聚合或批处理来减少总负载。

示例:为无人机交付应用程序选择数据存储

本系列前面的文章将无人机交付服务作为正在运行的示例进行讨论。 可以 在此处阅读有关方案的详细信息和相应的参考实现。 此示例非常适合飞机和航空航天行业。

为了回顾,此应用程序定义了多个微服务,用于计划无人机交付。 当用户计划新的交付时,客户端请求包括有关交付的信息,例如取件和下车位置,以及包的大小和重量。 此信息定义一个工作单元。

各种后端服务关心请求中信息的不同部分,并且还具有不同的读取和写入配置文件。

数据注意事项示意图

交付服务

传递服务存储有关当前计划或正在进行的每个传递的信息。 它侦听无人机的事件,并跟踪正在进行的交付状态。 它还发送具有传递状态更新的域事件。

预计用户在等待包裹时会经常检查送达状态。 因此,传递服务需要一个数据存储,该数据存储强调长期存储的吞吐量(读取和写入)。 此外,传递服务不执行任何复杂的查询或分析,它只是提取给定传递的最新状态。 交付服务团队选择了 Azure Cache for Redis,以获得较高的读写性能。 存储在 Redis 中的信息相对较短。 交付完成后,传递历史记录服务就是记录系统。

交付历史记录服务

传递历史记录服务侦听传递服务中的传递状态事件。 它将此数据存储在长期存储中。 此历史数据有两个不同的用例,这些用例具有不同的数据存储要求。

第一种方案是聚合数据以进行数据分析,以便优化业务或提高服务质量。 请注意,传递历史记录服务不会对数据执行实际分析。 它只负责引入和存储。 对于此方案,必须使用基于架构的读取方法来适应各种数据源,针对大量数据进行数据分析。 Azure Data Lake Store 非常适合此方案。 Data Lake Store 是与 Hadoop 分布式文件系统(HDFS)兼容的 Apache Hadoop 文件系统,针对数据分析方案的性能进行了优化。

另一种方案是允许用户在交付完成后查找交付历史记录。 Azure Data Lake 未针对此方案进行优化。 为了获得最佳性能,Microsoft建议在 Data Lake 中存储按日期分区的文件夹中的时序数据。 (请参阅 优化 Azure Data Lake Store 以获取性能)。 但是,该结构并不是按 ID 查找单个记录的最佳结构。 除非还知道时间戳,否则按 ID 查找需要扫描整个集合。 因此,传递历史记录服务还会将历史数据的子集存储在 Azure Cosmos DB 中,以便更快地查找。 记录不需要无限期地保留在 Azure Cosmos DB 中。 旧交付可以存档 - 例如,在一个月后。 这可以通过运行偶尔的批处理来完成。 存档较旧的数据可以减少 Cosmos DB 的成本,同时仍使数据可供 Data Lake 的历史报告使用。

包服务

包服务存储有关所有包的信息。 包的存储要求如下:

  • 长期存储。
  • 能够处理大量包,需要高写入吞吐量。
  • 按包 ID 支持简单的查询。 没有复杂联接或引用完整性要求。

由于包数据不是关系型数据库,因此适合使用面向文档的数据库,Azure Cosmos DB 可以使用分片集合实现高吞吐量。 在包服务上运行的团队熟悉 MEAN 堆栈(MongoDB、Express.js、AngularJS 和 Node.js),因此他们选择 Azure Cosmos DB 的 MongoDB API 。 这样,他们就可以利用其与 MongoDB 的现有体验,同时获得 Azure Cosmos DB 的优势,这是一项托管的 Azure 服务。

后续步骤

了解有助于缓解微服务体系结构中一些常见挑战的设计模式。

微服务 设计模式