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开发分布式应用——配置文件篇
我们已经知道可以通过编码的方式配置服务器通道和远程客户机,除此之外,还可以使用配置文件对服务器通道和远程客户机进行配置.使用远程客户机和服务器对象的配置文件的优点在于,用户无需修改任何一行代码,也无需 ...
随机推荐
- Elastic-Job:动态添加任务,支持动态分片
多情只有春庭月,犹为离人照落花. 概述 因项目中使用到定时任务,且服务部署多实例,因此需要解决定时任务重复执行的问题.即在同一时间点,每一个定时任务只在一个节点上执行.常见的开源方案,如 elasti ...
- 宝锋UV-5R说明书下载
宝锋UV-5R说明书 百度网盘下载地址: 链接: https://pan.baidu.com/s/1QJXEJ2YyO7ovMAQG7Uur4A 提取码: j8d2 BI8EJM 73 ...
- AD 域服务简介(三)- Java 对 AD 域用户的增删改查操作
博客地址:http://www.moonxy.com 关于AD 域服务器搭建及其使用,请参阅:AD 域服务简介(一) - 基于 LDAP 的 AD 域服务器搭建及其使用 Java 获取 AD 域用户, ...
- 夯实Java基础系列4:一文了解final关键字的特性、使用方法,以及实现原理
目录 final使用 final变量 final修饰基本数据类型变量和引用 final类 final关键字的知识点 final关键字的最佳实践 final的用法 关于空白final final内存分配 ...
- tomcat下c3p0连接池配置问题
一.首先如果要使用这个连接池,就需要导入c3p0-0.9.2-pre1.jar架包和支持架包mchange-commons-0.2.jar, 我这里测试使用的是msql数据库 当然也需要导入mysql ...
- UploadFile
import org.apache.hadoop.conf.*; import org.apache.hadoop.fs.*; import java.io.IOException; import j ...
- Python学习笔记整理总结【Django】Ajax
一.Ajax(part1) Ajax即“Asynchronous Javascript And XML”(异步JavaScript和XML),是指一种创建交互式网页应用的网页开发技术,AJAX = ...
- Java Map知识点
1.遍历 java遍历Map的方式有多种,一下以代码示例来说明使用: Map<String, String> tmap = new HashMap<String, String> ...
- mybatis批量插入应用
一.foreach简单介绍 foreach的主要用在构建in条件中,它可以在SQL语句中进行迭代一个集合. foreach元素的属性主要有item,index,collection,open,sepa ...
- 正睿国庆DAY2动态规划专题
正睿国庆DAY2动态规划专题 排列-例题 1~n 的排列个数,每个数要么比旁边两个大,要么比旁边两个小 \(f[i][j]\) 填了前i个数,未填的数有\(j\)个比第\(i\)个小,是波峰 \(g[ ...