微服务架构设计模式笔记--第七章 在微服务架构中实现查询
- 本章导读
- 1. 使用API组合模式进行查询
- 1.1 什么是API组合模式
- 1.2 API组合模式的设计缺陷
- 由谁来担任API组合器的角色
- 1.3 弊端
- 2. 使用CQRS模式
- 2.1 什么是CQRS
- 2.2 CQRS的好处
- 2.3 CQRS 的弊端
- 3. 设计CQRS视图
- 3.1 可供选择的查询端视图存储库
- 3.2 设计数据访问模块
- 3.3 添加和更新CQRS视图
本章导读
- 在微服务架构中查询数据的挑战
- 何时以及如何使用APl组合模式实现查询
- 何时以及如何使用CQRS模式实现查询
在微服务架构中编写查询非常具有挑战性。查询通常需要检索分散在多个服务所拥有的数据库中的数据。但是,你不能使用传统的分布式查询处理机制,因为即使技术上可行,它也会打破服务之间的隔离和封装。
在微服务架构中实现查询操作有两种不同的模式:
- API组合模式:这是最简单的方法,应尽可能使用。它的工作原理是让拥有数据的服务的客户端负责调用服务,并组合服务返回的查询结果。
- 命令查询职责隔离(CQRS)模式:它比API组合模式更强大,但也更复杂。它维护一个或多个视图数据库,其唯一目的是支持查询。
1. 使用API组合模式进行查询
1.1 什么是API组合模式
API组合模式由API组合器和两个或多个提供方服务组成。API组合器通过查询提供方服务并组合结果来实现查询。图中显示了三个提供方服务。API组合器通过从提供方服务检索数据并组合结果来实现查询。API组合器可能是需要数据呈现网页的客户端,例如:Web应用程序。
1.2 API组合模式的设计缺陷
使用此模式时,你必须解决两个设计问题:
- 确定架构中的哪个组件是查询操作的API组合器。
- 如何编写有效的聚合逻辑。
由谁来担任API组合器的角色
- 由服务的客户端扮演API组合器的角色
对于防火墙外的客户以及通过较慢网络访问的服务,此选择可能不实用。 - 由实现应用程序外部API的API Gateway来扮演API组合
器的角色,用来完成查询操作和查询结果的组合。 - 将API组合器实现为独立的服务
此选择可以用于由多个服务在内部使用的查询操作。此操作还可用于外部可访问的查询操作,由于它们的聚合逻辑过于复杂,因此无法在API Gateway中完成查询,必须使用单独的服务。API组合器应该使用响应式编程模型。
1.3 弊端
- 增加了额外的开销
这种模式的一个缺点是它需要调用多个服务和查询多个数据库,这带来了额外的开销。 - 带来可用性降低的风险
操作的可用性随着所涉及的服务数量而下降。API组合器和至少两个提供方服务,其可用性将显著小于单个服务的可用性。一种策略是API组合器在提供方服务不可用时,返回先前缓存的数据。另一种提高可用性的策略是让API组合器返回不完整的数据。 - 缺乏事务数据一致性
API组合模式则是针对多个数据库执行查询。这种方式存在一种风险,即查询操作将返回不一致的数据。
2. 使用CQRS模式
许多企业级应用程序使用关系型数据库作为数据记录的事务系统,并使用文本搜索数据库(如Elasticsearch或Solr)进行文本搜索查询。某些应用程序可以通过同时写入来保持数据库同步。然后再定期将数据从关系型数据库复制到文本搜索引擎中。具有此架构的应用程序利用了多个数据库的优势:关系型数据库的事务属性和文本数据库的查询功能。
2.1 什么是CQRS
在微服务架构中实现查询时经常会遇到的三个问题:
- 使用API组合模式检索分散在多个服务中的数据会导致昂贵、低效的内存中连接。
- 拥拥有数据的服务将数据存储在不能有效支持所需查询的表单或数据库中。
- 隔离问题的考虑意味着,拥有数据的服务不一定是会实现查询操作的服务。
如图所示,CQRS将持久化数据模型和使用数据的模块分为两部分:命令端和查询端。命令端模块和数据模型实现创建、更新和删除操作。查询端模块和数据模型实现查询。查询端通过订阅命令端发布的事件,使其数据模型与命令端数据模型保持同步。
2.2 CQRS的好处
- 在微服务架构中高效地实现查询。
- 高效地实现多种不同的查询类型。
- 在基于事件溯源技术的应用程序中实现查询。
- 更进一步地实现问题隔离。
2.3 CQRS 的弊端
- 更加复杂的架构。
- 处理数据复制导致的延迟。
3. 设计CQRS视图
CQRS视图模块包括由一个或多个查询操作组成的API。它通过订阅由一个或多个服务发布的事件来更新它的数据库视图,从而实现这些查询操作。如图所示,视图模块包含视图数据库和三个子模块。
数据访问模块实现数据库访问逻辑。事件处理程序和查询API模块使用数据访问模块来更新和查询数据库。事件处理程序模块订阅事件并更新数据库。查询API模块负责实现查询API。
在开发视图模块时,你必须做出一些重要的设计决策:
- 你必须选择合适的底层数据库,并设计数据库结构。
- 在设计数据访问模块时,你必须解决各种问题,包括确保更新是幂等的,并且能够处理并发更新。
- 在现有应用程序中实现新视图或更改现有应用程序的模式时,必须实现一种机制,以便有效地构建或重建视图。
- 你必须决定如何设计视图的客户端,以应对前面描述的复制延迟。
3.1 可供选择的查询端视图存储库
3.2 设计数据访问模块
事件处理程序和查询API模块不直接访问数据存储区。相反,它们使用数据访问模块,该模块由数据访问对象(DAO)及其辅助类组成。DAO有几项职责。它实现由事件处理程序调用的更新操作,以及查询模块调用的查询操作。DAO把上层代码映射到数据库API使用的数据类型。它还必须处理并发更新并确保更新是幂等的。
- 并发处理
DAO必须以确保正确处理这种情况的方式编写。它不能允许一次更新覆盖另一次更新。如果DAO通过读取记录进行更新,然后再写入已更新记录的做法,则必须使用悲观锁或乐观锁。 - 幂等事件处理程序
如果重复事件导致不正确的结果,则事件处理程序不是幂等的。为了确保可靠,事件处理程序必须记录事件ID并以原子化的方式更新数据存储区。如何执行此操作取决于数据库的类型。如果视图的底层数据库是SQL数据库,则事件处理程序可以将已处理的事件作为更新视图事务的一部分插入PROCESSED_EVENTS表中。
3.3 添加和更新CQRS视图
CQRS视图将在应用程序的整个生命周期内被不断地添加和更新。有时你需要添加新视图以支持新查询。在其他时候,你可能需要重新创建视图,因为架构已更改,或者你需要修复更新视图代码中的错误。
- 使用归档事件构建CQRS视图
可以通过使用可扩展的大数据技术(如Apache Spark)来实现此目的。 - 增量式构建CQRS视图
使用两步增量算法。第一步基于其先前的快照和自创建快照以来发生的事件,定期计算每个聚合实例的快照。第二步使用快照和任何后续事件创建视图。