NServiceBus+Saga开发分布式应用
前言
当你在处理异步消息时,每个单独的消息处理程序都是一个单独的handler,每个handler之间互不影响。这时如果一个消息依赖另一个消息的状态呢? 这时业务逻辑怎么处理?

借用我们上篇文章的业务场景,如果在Ship项目里需要发送一个ShipOrder Command。这个ShipOrder需要依赖Sales.OrderPlaced和Bill.OrderBilled Command的状态,目前我们的两个单独的Message Handler都没有保持任何的状态字段,所以这时如果我们需要完成这个业务模型,就需要跟踪他们的状态。
什么是Saga
这个就是本篇文章要提的saga,定义在NServiceBus框架里,他的本质是一个消息驱动模型里的状态机,或者也可以理解为一系列消息处理程序用来共享状态的业务模型。我理解在消息队列里如果我们要保证消息一致性通常会自己创建一张Event表,这里saga维持状态的角色有点像我们这里的Event表。

好的,回到正题上,如果我们需要在Shipping Service里发送一个ShipOrder,发送他之前需要确定OrderPlaced和OrderBilled的状态,确保这两个消息都收到以后才能发送ShipOrder。
如何使用Saga
当然,我暂且理解Saga的目的是为了处理在长时间运行的任务里保证数据一致性这样的一个角色。
Saga状态
saga状态主要是告诉NServiceBus在处理数据一致性的判断逻辑,这里需要继承抽象类ContainSagaData,在我们这个业务场景中则主要是判断OrderPlaced和OrderBilled消息是否已经接收到并处理。
public class ShippingPolicyData:ContainSagaData
{
public string OrderId { get; set; }
public bool IsOrderPlaced { get; set; }
public bool IsOrderBilled { get; set; }
}
Saga如何工作
有了状态以后,我们还需要一个“handler”来告诉NServiceBus,在这个handler里主要用来处理消息数据一致性,我看了官方文档后,他们建议我们这里的handler角色使用Policy后缀命名,当然我觉的也可以用Saga后缀命名,比如ShippingPolicy或者ShippingSaga。
同时这里我们这个handler觉色还要继承Saga类,Saga类主要重写方法ConfigureHowToFindSaga,这个方法的作用主要是在接受的消息和我们的Saga实体之间建立映射关系。
public class ShipPolicy:Saga<ShippingPolicyData>,
IAmStartedByMessages<OrderPlaced>,
IAmStartedByMessages<OrderBilled> //都可以创建Saga实例
{
private static ILog log = LogManager.GetLogger<ShipPolicy>();
protected override void ConfigureHowToFindSaga(SagaPropertyMapper<ShippingPolicyData> mapper)
{
mapper.ConfigureMapping<OrderPlaced>(t=>t.OrderId).ToSaga(sagaData=>sagaData.OrderId);
mapper.ConfigureMapping<OrderBilled>(t=>t.OrderId).ToSaga(sagaData=>sagaData.OrderId);
}
public Task Handle(OrderPlaced message, IMessageHandlerContext context)
{
log.Info("OrderPlaced message received ");
this.Data.IsOrderPlaced = true;
return ProcessOrder(context);
}
public Task Handle(OrderBilled message, IMessageHandlerContext context)
{
log.Info("OrderBilled message received");
this.Data.IsOrderBilled = true;
return ProcessOrder(context);
}
private async Task ProcessOrder(IMessageHandlerContext context)
{
if (Data.IsOrderBilled && Data.IsOrderPlaced)
{
await context.SendLocal(new ShipOrder()
{
OrderId = Data.OrderId
});
MarkAsComplete();
}
}
}
这个类里你会发现还实现了接口**IAmStartedByMessages, **这个接口主要是告诉Saga,不论是那种消息类型先进来,都可以创建一个Saga实例,就比如是Event表,不管那个消息进来,都需要先插入一条数据,后续消息再进来时要更新数据状态,当然,这里的Saga实例也好,Event表也好,关键问题就是有效标识,或者叫主键,我们这个业务模型里,OrderPlaced和OrderBilled都包含一个属性OrderId, 这里Saga实例则使用这个OrderId做关键属性。
发送ShipOrder Command
到这里也就是我们的OrderPlaced和OrderBIlled消息都收到了,业务逻辑符合要求,可以发送ShipOrder消息了,也就是用户创建了订单,付了款,可以发货了。

新建ShipOrder类
public class ShipOrder:ICommand
{
public string OrderId { get; set; }
}
新建ShipOrderHandler
public class ShipOrderHandler:IHandleMessages<ShipOrder>
{
private static ILog log = LogManager.GetLogger<ShipOrderHandler>();
public Task Handle(ShipOrder message, IMessageHandlerContext context)
{
log.Info($"Order [{message.OrderId}] - Successfully shipped");
return Task.CompletedTask;
}
}
运行Shipping项目,看到下图,则说明程序运行成功,我们这个业务场景里OrderPlaced消息肯定先接受到,OrderBilled消息后接受到。

参考链接
https://docs.particular.net/tutorials/nservicebus-sagas/1-getting-started/
https://docs.particular.net/nservicebus/sagas/
NServiceBus+Saga开发分布式应用的更多相关文章
- NServiceBus+RabbitMQ开发分布式应用
前言 NServiceBus提供了8种传输管道组件,分别是Learning.MSMQ.Azure Service Bus.Azure Service Bus (Legacy).Azure S ...
- 跟我一起学WCF(3)——利用Web Services开发分布式应用
一.引言 在前面文章中分别介绍了MSMQ和.NET Remoting技术,今天继续分享.NET 平台下另一种分布式技术——Web Services 二.Web Services 详细介绍 2.1 We ...
- 跟我一起学WCF(2)——利用.NET Remoting技术开发分布式应用
一.引言 上一篇博文分享了消息队列(MSMQ)技术来实现分布式应用,在这篇博文继续分享下.NET平台下另一种分布式技术——.NET Remoting. 二..NET Remoting 介绍 2.1 . ...
- 利用Web Services开发分布式应用
一.引言 在前面文章中分别介绍了MSMQ和.NET Remoting技术,今天继续分享.NET 平台下另一种分布式技术——Web Services 二.Web Services 详细介绍 2.1 We ...
- 使用.NET Remoting开发分布式应用——基于租约的生存期
一.概述 知名类型的SingleCall对象可以在客户程序的方法调用之后被垃圾收集器清理掉,因为它没有保持状态,属于无状态的.而客户激活的类型的对象和知名类型的SingleTon对象都属于生存期长的对 ...
- 初识用.NET Remoting来开发分布式应用
一..NET Remoting简介: .NET Remoting从某种意义上讲是DCOM的替代品.ASP.NET Web服务十分有用,但是这项技术在企业内联网的解决方案中,对于某些业务请求来说并不快, ...
- 使用NServiceBus开发分布式应用
系列主题:基于消息的软件架构模型演变 NServiceBus 是一个.Net平台下开源的消息服务框架,这类产品有时也被称作ESB(Enterprise Service Bus)--企业服务总线.NSe ...
- NServiceBus SAGA 消息状态驱动
https://docs.particular.net/tutorials/nservicebus-sagas/1-getting-started/ 链接:https://pan.baidu.com/ ...
- 使用.NET Remoting开发分布式应用——配置文件篇
我们已经知道可以通过编码的方式配置服务器通道和远程客户机,除此之外,还可以使用配置文件对服务器通道和远程客户机进行配置.使用远程客户机和服务器对象的配置文件的优点在于,用户无需修改任何一行代码,也无需 ...
随机推荐
- mysql 函数 存取过程
1.打开数据库 2.选择函数,点击新建函数,选择过程,点击完成 4.写入自己要添加的sql语句 5.点击CTAL + S 保存,如若报错则语法或函数错误
- 跟我学SpringCloud | 第十八篇:微服务 Docker 化之基础环境
1. 容器化 Docker 的横空出世,给了容器技术带来了质的飞跃,Docker 标准化了服务的基础设施,统一了应用的打包分发,部署以及操作系统相关类库等,解决了测试生产部署时环境差异的问题.对于运维 ...
- Android远程服务AIDL开发过程中容易遇见的两个问题
问题 一 JavaBinder: Uncaught remote exception! (Exceptions are not yet supported across processes.) jav ...
- ActiveMQ消息选择器Selector
一.前言 消息发送到Broker,消费者通过Destination可以订阅消费某个特定的通道内的消息.一些特殊情况下,需要消费者对消息过滤下再进行消费,也就是筛选出某些特定消息.ActiveMQ提供了 ...
- 使用gdb调试c++程序
上篇(使用c++开发跨平台程序)说到,我不怕造东西,我怕的是造出来的东西,如果出了问题,我却不知道原因.所以调试分析是一个重要的手段. C++调试是一个复杂的活.虽然大部分调试可以通过IDE在开发期间 ...
- 从零开始使用 Webpack 搭建 Vue 开发环境
创建项目 先创建一个空目录,在该目录打开命令行,执行 npm init 命令创建一个项目(无法执行 npm 命令?需要先安装 Node),这个过程会提示输入一些内容,随意输入就行,完成后会自动生成一个 ...
- postgresql从库搭建
1 复制类型 PostgreSQL支持物理复制(流复制)及逻辑复制2种.通过流复制技术,可以从实例级复制出一个与主库一模一样的实例级的从库.流复制同步方式有同步.异步两种. 另一种复制方式为逻辑复制, ...
- 格子游戏Grid game CodeForce#1104C 模拟
题目链接:Grid game 题目原文 You are given a 4x4 grid. You play a game — there is a sequence of tiles, each o ...
- java自学小段 产生随机数
public class Suijishu { public static void main(String[] args) { double i=Math.random();//产生一个0-0.5的 ...
- Oozie、Flume、Mahout配置与应用
-------------------------Oozie-------------------- [一.部署] 1)部署Oozie服务端 [root@cMaster~]#sudo yum inst ...