前言

当你在处理异步消息时,每个单独的消息处理程序都是一个单独的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开发分布式应用的更多相关文章

  1. NServiceBus+RabbitMQ开发分布式应用

    前言      NServiceBus提供了8种传输管道组件,分别是Learning.MSMQ.Azure Service Bus.Azure Service Bus (Legacy).Azure S ...

  2. 跟我一起学WCF(3)——利用Web Services开发分布式应用

    一.引言 在前面文章中分别介绍了MSMQ和.NET Remoting技术,今天继续分享.NET 平台下另一种分布式技术——Web Services 二.Web Services 详细介绍 2.1 We ...

  3. 跟我一起学WCF(2)——利用.NET Remoting技术开发分布式应用

    一.引言 上一篇博文分享了消息队列(MSMQ)技术来实现分布式应用,在这篇博文继续分享下.NET平台下另一种分布式技术——.NET Remoting. 二..NET Remoting 介绍 2.1 . ...

  4. 利用Web Services开发分布式应用

    一.引言 在前面文章中分别介绍了MSMQ和.NET Remoting技术,今天继续分享.NET 平台下另一种分布式技术——Web Services 二.Web Services 详细介绍 2.1 We ...

  5. 使用.NET Remoting开发分布式应用——基于租约的生存期

    一.概述 知名类型的SingleCall对象可以在客户程序的方法调用之后被垃圾收集器清理掉,因为它没有保持状态,属于无状态的.而客户激活的类型的对象和知名类型的SingleTon对象都属于生存期长的对 ...

  6. 初识用.NET Remoting来开发分布式应用

    一..NET Remoting简介: .NET Remoting从某种意义上讲是DCOM的替代品.ASP.NET Web服务十分有用,但是这项技术在企业内联网的解决方案中,对于某些业务请求来说并不快, ...

  7. 使用NServiceBus开发分布式应用

    系列主题:基于消息的软件架构模型演变 NServiceBus 是一个.Net平台下开源的消息服务框架,这类产品有时也被称作ESB(Enterprise Service Bus)--企业服务总线.NSe ...

  8. NServiceBus SAGA 消息状态驱动

    https://docs.particular.net/tutorials/nservicebus-sagas/1-getting-started/ 链接:https://pan.baidu.com/ ...

  9. 使用.NET Remoting开发分布式应用——配置文件篇

    我们已经知道可以通过编码的方式配置服务器通道和远程客户机,除此之外,还可以使用配置文件对服务器通道和远程客户机进行配置.使用远程客户机和服务器对象的配置文件的优点在于,用户无需修改任何一行代码,也无需 ...

随机推荐

  1. AirFlow常用命令

    airflow常用命令如下所示: airflow test dag_id task_id execution_date 测试task 示例: airflow test example_hello_wo ...

  2. Metasploit工具----辅助模块

    Metasploit框架(Metasploit Framework,MSF)是一个开源工具,旨在方便渗透测试,他是有Ruby程序语言编写的模板化框架,具有很好的扩展性,便于渗透测试人员开发.使用定制的 ...

  3. (5)Makefile详解

         Makefile是一个自动化的编译工具,关系到整个工程的编译规则,极大的提高了软件开发的效率.     (1)Makefile的编译规则 //Makefile 也可以写作 makefile1 ...

  4. thymeleaf自定义标签方言处理

    项目背景:springboot+thymeleaf thymeleaf两种方式处理自定义标签:AbstractAttributeTagProcessor 和 AbstractElementTagPro ...

  5. 从零开始入门 K8s | 应用编排与管理(酒祝)

    一.需求来源 背景问题 首先来看一下背景问题.如下图所示:如果我们直接管理集群中所有的 Pod,应用 A.B.C 的 Pod,其实是散乱地分布在集群中. 现在有以下的问题: 首先,如何保证集群内可用 ...

  6. JQuery发送ajax请求时中文乱码

    先排除项目故障: 1.web.xml中是否配置了字符拦截器 <filter> <filter-name>encodingFilter</filter-name> & ...

  7. Java连载35-类总结、空指针异常

    一.总结 1.栈内存中主要存储的是方法体中的局部变量 2.对象内部有实例变量,实例变量存储在堆内存中 3.变量分类:局部变量(方法体中声明):成员变量(方法体外声明) 实例变量(前边修饰符没有stat ...

  8. pyenv的安装和简单使用

    centos7.4   python2.7 安装pyenv需要的依赖 yum -y install gcc zlib-devel bzip2-devel openssl-devel ncurses-d ...

  9. 使用Shell脚本编译运行C++源码 输入输出重定向

    在写C++控制台程序的时,如果使用Xcode或者Visual Studio之类的IDE,需要创建许多工程,会造成很多不便.有时,采用Vim或者Sublime text等编辑器编写简单的控制台程序能节省 ...

  10. 多智能体系统(MAS)简介

    1.背景   自然界中大量个体聚集时往往能够形成协调.有序,甚至令人感到震撼的运动场景,比如天空中集体翱翔的庞大的鸟群.海洋中成群游动的鱼群,陆地上合作捕猎的狼群.这些群体现象所表现出的分布.协调.自 ...