简单事务:在 .NET Core 微服务应用程序中设计基于队列的消息传递解决方案
在 .NET Core 中设计基于队列的消息传递解决方案以解决复杂问题的简单方法
目录
- 引言
- 问题描述
- 应用程序架构
- 解决方案设计
- 开发环境
- 开源工具/技术
- WebApi 端点
- 解决方案结构
- 组件之间的交互
- 带 Cron 表达式的计划后台任务
- 微服务之间的通信
- 如何运行应用程序
- 控制台应用程序:网关客户端
- 结论
相关文章
引言
这是 Simple Transaction: Microservices Sample Architecture for .NET Core Application 的第二部分,旨在继续演示如何使用 .NET Core 构建命令驱动/消息驱动的解决方案。上一篇文章中使用的示例应用程序已扩展了几个额外的微服务。
上一篇文章中的 示例 使用 ASP.NET Core Web API、C#.NET、Entity Framework 和 SQL Server 实现了一个简单的自动化银行功能,例如余额、存款和取款的微服务。第二部分将介绍使用 RabbitMQ 设计基于队列的消息传递解决方案,通过后台服务插入生成月度账户报表的功能,并将其存储在 No-SQL 数据库 (MongoDB) 中,然后通过单独的微服务访问。
问题描述
动态生成类似账户报表这样的报告,从 SQL Server 中可用的事务数据中提取,对于实时系统来说,由于数据量巨大,会引起性能问题。
解决此问题的更好方法是分离报表生成过程(写入/命令)和数据访问(读取/查询)。本示例将向您展示如何通过后台服务分离命令职责,并通过单独的微服务查询数据,这解决了性能问题,并允许我们根据负载独立扩展服务,从而使系统能够处理大量数据。
应用程序架构
下面是更新后的架构图。突出显示的部分是新添加的用于访问账户报表的微服务。“Statement” 服务使用 MongoDB 作为持久化存储,并使用 Redis 缓存作为临时存储,数据被加载到缓存中一段时间,并在后续访问时从缓存中检索。
解决方案设计
下图显示了后台实现的顶层设计,该设计将命令职责与查询数据分离开来。它有三个核心组件,通过命令/消息处理分离写操作,通过查询数据分离读操作。
- 发布者 API / 调度器服务
- 接收者服务
- 报表 API
发布者 API / 调度器服务
当需要为给定月份生成月度账户报表时,此微服务会将命令发布到消息总线(消息队列)。此服务具有自动调度程序,可在计划时间触发报表生成过程,并提供 WebApi 端点以手动触发该过程。
接收者服务
接收者服务是一个监听器,监听消息队列,消耗传入的消息并进行处理。接收者与依赖服务通信以获取数据,并为给定月份生成用户月度账户报表,然后将文档存储在 MongoDB 中。
报表 API
此微服务公开了一个 WepApi 端点,该端点通过数据存储库与 Redis 缓存和 MongoDB 通信以访问账户报表。
开发环境
- .NET Core 2.2 SDK
- Visual Studio .NET 2017
- Microsoft Azure
- SQL Server Management Studio (SSMS)
- MongoDB Compass
- Redis Desktop Manager (RDM)
开源工具、技术
- Mongo DB 作为持久化存储
- Redis 缓存作为临时存储
- RabbitMQ 作为消息代理
- EasyNetQ 作为 RabbitMQ 客户端
- NCrontab 用于基于时间的调度
- .NET Core
IHostedService
作为后台服务
WebApi 端点
“发布者 API”中实现的用于将命令发布到消息队列的端点
- 路由:“api/publish/statement” [HttpPost] - 将消息发布到队列以触发后台进程。默认为上个月。
- 路由:“api/publish/statement/{month}” [HttpPost] - 将消息发布到队列以触发后台进程。
- 路由:“api/publish/statement/{month}/accountnumbers” [HttpPost] - 将消息发布到队列以触发给定账户号码列表的后台进程。
“报表 API”中实现的用于访问账户报表的端点
- 路由:“api/statement/{month}” [HttpGet] - 访问经过身份验证的用户在给定月份的账户报表
已配置并可通过 API 网关访问以访问账户报表的端点
- 路由:“statement/{month}” [HttpGet] - 访问经过身份验证的用户的账户报表。
解决方案结构
下图中突出显示的部分显示了添加到示例中的新服务/组件。
组件之间的交互
下图显示了后台服务中组件/对象之间的交互。
- 消息队列:后台服务“发布者”和“接收者”使用两个不同的队列。其中一个是“触发”队列。来自调度程序的初始触发/命令被发送到由“接收者”服务处理的“触发”队列。工作流中的第二个队列是“报表”队列,它接收来自“触发”处理程序的输入,并由“报表”处理程序处理。
- 消息类型:后台服务“
Publisher
”和“Receiver
”理解“Publish.Framework
”中定义的ICommand
类型消息。定义了两种消息类型来处理两个不同的队列:“TriggerMessage
”和“StatementMessage
”。 - 发布者:在调度程序计划的时间或通过 API 端点按需调用时,将“
Trigger
”消息发送到“Trigger
”队列。 - 接收者:消耗“
Trigger
”消息,为用户账户列表准备“Statement
”消息,并为每个用户账户将“Statement
”消息发布到下一个队列。“Receiver
”服务然后消耗“Statement
”消息并调用处理器。 - 处理器:包含准备账户报表的逻辑。
- 文档存储库:包含将文档保存到 MongoDB 的数据逻辑。
带 Cron 表达式的计划后台任务
Cron 表达式是一种用于指定执行任务的时间表格式。NCrontab 是包含在“Publisher.Service
”中的 Nuget 包,用于解析表达式并确定下一次运行时间。该服务配置为在每个月开始时触发后台进程,以生成上个月的账户报表。
微服务之间的通信
微服务之间需要进行通信,以便其他服务知道何时发生更改或获取依赖信息。在这里,在我们的示例中,在需要的地方使用了同步和异步机制来建立微服务之间的通信。
基于消息的异步通信用于触发事件。这是一种点对点通信,具有单个接收者。这意味着当消息发布到队列时,有一个接收者会消耗该消息进行处理。下图显示了“发布者/调度程序”和“接收者”之间通过消息总线发生的异步通信。
后台服务“Receiver
”通过 HTTP 请求与依赖服务(Identity
和 Transaction
)通信,以获取后台处理的实际数据。
如何运行应用程序
请遵循上一篇文章中提供的相同说明来运行应用程序。此外,新添加的服务(Publisher
、Receiver
和 Statement
API)也应该启动并运行。
您需要在本地环境或云中设置 MongoDB 和 Redis 缓存。在此之后,您可以根据需要更新“Receiver.Service
”和“Statement.Service
”的 appsettings.json 文件中的 MongoDB 和 Redis 缓存连接设置。
对于数据库更改,请参阅此版本代码中包含的数据库项目,其中包含更新的脚本。
控制台应用程序:网关客户端
结论
为了提高系统性能,一个微服务不一定需要两个 NoSQL 数据库。在这种情况下,只有 MongoDB 就可以满足需求,但我包含了 Redis 缓存来展示一种提高频繁访问数据性能的替代方法。
历史
- 2019 年 11 月 22 日:初始版本