微服务架构设计模式-第七章

第7章 在微服务架构中实现查询

在微服务架构中实现查询操作有两种不同的模式:

  • API 组合模式:这是最简单的方法,应尽可能使用。它的工作原理是让拥有数据的服务的客户端负责调用服务,并组合服务返回的查询结果
  • 命令查询职责隔离(CQRS)模式:它比 API 组合模式更强大,但也更复杂。它维护活多个视图数据库,其唯一目的是支持查询。

7.1 使用API组合模式进行查询

7.1.1 find0rder)查询操作

31-订单状态视图

7.1.2 什么是API组合模式

有两种类型的参与者:

  • API 组合器:通过查询数据提供方的服务来实现查询操作
  • 数据提供方服务:拥有查询返回的部分数据的服务

API 组合器可能是需要数据呈现网页的客户端,例如 web 应用程序。或者,它可能是一个服务。
32-API组合器
是否可以使用该模式实现特定查询操作取决于几个因素,包括数据的分区方式、拥有数据的服务公开的 API 的功能,以及服务使用数据库的功能,等等。例如,即使提供方服务拥有用于检索所需数据的 API,聚合器也可能需要执行大量数据集的低效内存连接。

7.1.3 使用API组合模式实现findOrder()查询操作

findOrder()查询相当于一个简单的基于主键的 equ-join 查询。可以假设每个提供方服务都有一个 API 接口,用于提供 orderId 检索所需的数据。

7.1.4 API组合模式的设计缺陷

使用此模式,必须解决两个设计问题:

  • 确定架构中的哪个组件是查询操作的 API 组合器
  • 如何编写有效的聚合逻辑

由谁来担任 API 组合器的角色

  • 由服务的客户端扮演 API 组合器的角色
  • 由实现应用程序外部 API 的 API Gateway 来扮演 API 组合器的角色,用来完成查询操作和查询结果的组合
  • 将 API 组合器实现为独立的服务

7.1.5 API组合模式的好处和弊端

好处:

  • 增加了额外的开销
  • 带来可用性降低的风险
    • 解决方法一:在提供方服务不可用时,返回先前缓存的数据
    • 解决方法二:让 API 组合器返回不完成的数据
  • 缺乏事务数据一致性

7.2 使用CQRS模式

CQRS:使用事件来维护从多个服务复制数据的只读视图,借此实现对来自多个服务的数据的查询

7.2.1 为什么要使用CQRS

  • 使用API组合模式检索分散在多个服务中的数据会导致昂贵、低效的内存中连接
  • 拥有数据的服务将数据存储在不能有效支持所需查询的表单或数据库中
  • 隔离问题的考虑意味着,拥有数据的服务不一定是会实现查询操作的服务

7.2.2 什么是CQRS

command query responsibility segregation
33-CQRS
独立的查询模型可以用来处理复杂的查询场景。查询端的代码往往比命令端简单的多,因为它不需要负责实现具体的业务逻辑。查询端可以使用的数据库种类很灵活,只要数据库能够支持需要的查询功能即可。查询端的事件处理程序会订阅领域事件并更新数据库。甚至可能存在多个查询模型,与需要的查询类型一一对应。
34-DDD服务分类
35-查询端服务

7.2.3 CQRS的好处

  • 在微服务架构中高效地实现查询
  • 高效地实现多种不同的查询类型
  • 在基于事件溯源技术的应用程序中实现查询
    • CQRS 还克服了事件溯源的主要限制。事件存储库仅支持基于主键的查询。CQRS 模式订阅由基于事件溯源的聚合发布的事件流,可以保持最新的聚合的一个或多个视图,由此解决此限制。这也是基于事件溯源的应用程序总是使用CQRS的原因
  • 更进一步第实现问题隔离

7.2.4 CQRS的弊端

  • 更加复杂的架构
  • 处理数据复制导致的延迟
    • 解决方法一:采用命令端和查询端API为客户端提供版本信息,使其能够判断查询端是否过时。客户端可以轮询查询端的视图,直到它是最新的

尽可能使用API组合,并在必要时使用CQRS

7.3 设计CQRS视图

CQRS 视图模块包括由一个或多个查询操作组成的API。它通过订阅由一个或多个服务发布的事件来更新它的数据库视图,从而实现这些查询操作。
36-CQRS视图模块的设计
在开发视图模块时,你必须做出一些重要的设计决策:

  • 你必须选择合适的底层数据库,并设计数据库结构
  • 在设计数据访问模块时,必须解决各种问题,包括确保更新是幂等的,并且能够处理并发更新
  • 在现有应用程序中实现新视图或更改现有应用程序的模式时,必须实现一种机制,以便有效地构建或重建视图
  • 必须决定如何设计视图的客户端,以应对前面描述的复制延迟

7.3.1 选择视图存储库

SQL还是NoSQL数据库

nosql数据库通常是cqrs视图的一个很好的选择,cqrs可以利用它们的优势并忽略其弱点。cqrs视图受益于nosql数据库更丰富的数据模型和性能。它不受nosql数据库事务处理能力的限制,因为cqrs只需要使用简单的事务并执行一组固定的查询即可。
37-查询端视图存储库
支持更新操作

除了有效地实现查询之外,视图数据模型还必须有效地实现事件处理程序执行的数据更新操作。事件处理程序通常使用其主键更新或删除视图数据库中的记录。

在CQRS架构中,查询侧的数据库(View Database)绝不接受外部的直接更新请求(比如用户直接发个PUT请求来改表)。它只接受领域事件(Domain Events)的驱动。

写操作(Command):用户下单,触发OrderCreatedEvent。

更新操作(Event Handler):系统后台有一个专门监听该事件的处理器(Handler),它收到事件后,主动去更新查询侧的订单视图表(比如增加一条记录,或修改状态)。

读操作(Query):用户刷新页面,API去查询这张已经更新好的视图表,返回最新数据。

所以,查询侧的更新是“被动的、内部的后台行为”,而不是“对外的业务行为”。

7.3.2 设计数据访问模块

事件处理程序和查询API模块不直接访问数据存储区。相反,它们使用数据访问模块,该模块由数据访问对象(DAO)及其辅助类组成。DAO有几项职责。它实现由事件处理程序调用的更新操作,以及查询模块调用的查询操作。DAO把生成代码映射到数据库API使用的数据类型。它还必须处理并发更新并确保更新是幂等的。

并发处理

使用悲观锁或乐观锁

幂等事件处理程序

为了确保可靠,事件处理程序必须记录事件ID并以原子化的方式更新数据存储区。如何执行此操作取决于数据库的类型。

  • SQL数据库,事件处理程序可以将已处理的事件作为更新视图事务的一部分插入 processed_events 表中。
  • nosql 数据库,事件处理程序必须将事件保存在它更新的数据存储区 "记录" (例如,mongodb 文档或dynamoDb 表项)中

延迟

命令端操作将包含已发布事件的ID标记返回给客户端。然后,客户端把这个事件有关的ID传递给查询操作,如果这事件尚未更新视图,则返回查询错误。视图模块可以使用重复事件检测机制来实现这样的功能。

7.3.3 添加和更新CQRS视图

使用归档事件构建CQRS视图

通过从消息代理读取所有需要的事件来构建视图。还必须读取已存档的旧事件,这些旧事件可能都已经保存到了 aws s3(对象存储)上。通过使用可扩展的大数据技术(apache spark)来实现此目的

增量式构建CQRS视图

视图创建的另一个问题是处理所有事件所需的时间和资源随着时间的推移而不断增长。最终,视图创建将变得缓慢而昂贵。解决方案是使用两步增量算法。

  • 第一步基于其先前的快照和自创建快照以来发生的事件,定期计算每个聚合实例的快照
  • 第二步使用快照和任何后续事件创建视图

7.4 实现基于AWS DynamoDB的CQRS视图

38-CQRS实现

7.4.1 OrderHistoryEventHandlers模块

7.4.2 DynamoDB中的数据建模和查询设计

7.4.3 OrderHistoryDaoDynamoDb类

posted @ 2026-08-28 15:48  LHX2018  阅读(3)  评论(0)    收藏  举报