Tip: 此篇已加入.NET Core微服务基础系列文章索引
一、案例结构与说明在上一篇中,我们了解了MassTransit这个开源组件的基本用法,这一篇我们结合一个小案例来了解在ASP.NET Core中如何借助MassTransit+Quartz.Net来实现数据的最终一致性。当然,实现数据的最终一致性有很多方案,这里只是举一种我所学到的比较简单易于学习的实现方式而已。
假设我们有一个XX保险微信商城(WechatShop,简称WS)系统,根据服务的划分,针对下订单的这个场景,我们划分了四个微服务,他们分别是订单服务(OrderService),库存服务(StorageService)、配送服务(DeliveryService)以及一个只用于后台监控事件消息的事件后台服务(EventService)。
我们来看看这个场景下的一些基本业务逻辑流程(可能并不一定正确或者覆盖全面):
(1)当终端User在微信商城中看中一个保单产品,并且下单之后,订单服务会接收订单信息并更新订单数据库(这里假设只更新一张订单表)。
(2)然后事件后台服务会定期(比如每隔30秒)检查订单数据库的事件状态表(比如一张单独的Events表,里面有某个订单的2行记录,每行记录分别代表与订单相关的服务(这里就是库存和配送服务)的处理状态),如果发现相关服务的事件状态为未处理,则会向事件总线(假设这里基于RabbitMQ)发送消息告知对应订阅者要处理这个订单。
(3)这里对应的订阅者就是库存服务和配送服务,他们收到消息之后,会进行各自的业务逻辑处理。比如,库存服务会根据订单信息去更新库存数据库并做一些逻辑处理比如更新保单促销活动的记录,配送服务会根据订单信息更新配送数据库并做一些逻辑处理比如打印纸质保单并进行物流预约登记,当他们各自处理完成之后便会向事件总线发送一个处理完毕的消息。
(4)事件后台服务也会作为订阅者,接收库存和配送服务发送过来的消息,如果接收到某个服务的处理完毕消息,便会根据接收到的消息去更新前面事件状态表中的对应的事件记录记录行。比如:接收到库存服务发送的消息,会更新时间状态表中这个OrderID相关的库存事件状态的那一行记录的状态为已处理。
(5)时间后台服务的定时任务中(这里假设每隔30秒一次),会Check事件是否还有未处理完毕的事件消息,如果没有则休眠,否则会检查其创建记录的时间与现在的系统时间的间隔是否超过了最大容忍值(这里假设1小时),如果没有超过则继续向事件总线发送消息,如果超过了则进行一些事务回滚逆操作和向管理员发送一些警告信息以便进行人工干预等操作。
Talk is cheap, Show me the code。下面我们来看看如何实现,由于篇幅原因可能只会列出关键性代码,详细代码请自行去GitHub上下载或Clone。
下面是这次实验的项目结构,需要准备如下五个项目(四个ASP.NET Core WebAPI和一个.NET Core类库)
数据库这里实验采用的是MSSQL,只创建了一个Order数据库,两张表的数据如下:
可以看到,在Events表的设计中,通过EventType来区分事件类型,这里是订单创建(CreateOrder)这个事件的两个具体消息(StorageService和DeliveryService),状态(StatusValue)为1代表未处理,为2则代表已处理。
二、OrderService的实现 2.1 准备工作
其中,Controllers中主要用于与终端用户(比如WebForm、MVC、SPA等)交互,Models下主要用于定义DTO、EF DbContext与Entity,Repositories目录下主要用于定义Repository与数据库进行交互。
2.2 具体实现(1)OrderController
[Route("api/Order")] public class OrderController : Controller { public IOrderRepository OrderRepository { get; } public OrderController(IOrderRepository OrderRepository) { this.OrderRepository = OrderRepository; } [HttpPost] public string Post([FromBody]OrderDTO orderDTO) { var result = OrderRepository.CreateOrder(orderDTO).GetAwaiter().GetResult(); return result ? "Post Success" : "Post Failed"; } }