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简易导入excel
方法-:利用excel本身的命令实现: 1 将excel文件中的数据转换成sql文件 (1)如图所示,我们在excel中执行如下语句 =CONCATENATE(“insert into table_n ...
- 探索form组件和cookie,session组件
一. 实现注册功能 后端代码: from django.shortcuts import render,HttpResponse,redirect from app01 import models C ...
- 38 (OC)* 进程、线程、堆栈
一.进程和线程 1.什么是进程 进程是指在系统中正在运行的一个应用程序 每个进程之间是独立的,每个进程均运行在其专用且受保护的内存空间内 比如同时打开QQ.Xcode,系统就会分别启动2个进程 通过“ ...
- 【Unity与Android】01-Unity与Android交互通信的简易实现
前言 使用Unity也有不短的时间了,安卓包也打过不少,但是对Unity与Android的交互却知之甚少. 因工作需求,需要在Android平台接一些sdk(扩展功能).我就借此机会就了解了下Unit ...
- tcp居然会数据延迟40ms被发送
tcpdump是很好的tcp分析工具,在此配合nc命令来学习tcpdump nc -l 8000 tcpdump -S -n -i lo tcp and host 127.0.0.1 and port ...
- Unity3D-游戏场景优化之遮挡剔除(Occlusion Culling)的使用
在大型3D游戏场景中,如何优化游戏性能是非常重要的一步.一般遮挡剔除是非常常用的.接下来我们看看如何使用遮挡剔除. 假设这是一个游戏场景. 下面这是相机的视口,相机的视觉是看不到很大立方体后面的那些小 ...
- [Vue warn]: Duplicate keys detected: 'area'. This may cause an update error.
运行vue程序,浏览器报错: 原因:检测到重复的密钥:'area',因为在使用v-for循环绑定的时候,key的值是唯一的,不能相同,否则会出现意想不到的bug 解决办法:v-for时绑定的key唯一
- django开发后台接口error 10053/10054
初学Django,开发完接口之后访问post请求的接口遇到error10053和10054,查阅很多资料没有找到具体的原因. 在这里记录下我遇到这两个报错的原因和解决方案: get请求取请求参数:su ...
- 单例模式-全局可用的 context 对象,这一篇就够了
单例模式在各个方面都有着极为广泛的使用,所谓单例,顾名思义就是整个程序中只有一个该类的实例,所以它成功保证了整个程序的生命周期内该类的对象只能创建一次,并且提供全局唯一访问该类的方法:getInsta ...
- 使用springboot最新版本mysql-Connector连接数据库时报错解决
在连接数据库时,使用了最新版本的mysql-Connector,即6.0以上版本 1.报错如下: Loading class `com.mysql.jdbc.Driver'. This is depr ...