前言

当你在处理异步消息时,每个单独的消息处理程序都是一个单独的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. Linux 笔记 - 第十二章 Shell 脚本

    博客地址:http://www.moonxy.com 一.前言 常见的编程语言分为两类:一类是编译型语言,如:C.C++ 和 Java等,它们远行前要经过编译器的编译.另一类是解释型语言,不需要编译, ...

  2. 公用的update

    包结构: ===================================== jdbc.properties路径:/jdbc-1/src/jdbc.properties 内容: #连接MySQ ...

  3. 32 (OC)* keyChain的本质

    1:它是一个sqlite数据库,其保存的所有数据都是加密过的. 2:Keychain是加密规则(key)的集合.每个规则必须含有以下三个要素:认证算法.认证密钥(加密字符串).规则的时间. 3:key ...

  4. mysql集群基于docker 在centos上

    新博客https://blog.koreyoshi.work/ mysql集群(PXC)基于docker 在centos上 常用设计方案 Replication(复制) 速度快 弱一致性 低价值 场景 ...

  5. [AWS] Serverless & Lambda

    因为Lambda 所以Serverless 进化过程 课程章节:https://edu.51cto.com//center/course/lesson/index?id=199646 作用和优势 ev ...

  6. Spring boot缓存初体验

    spring boot缓存初体验 1.项目搭建 使用MySQL作为数据库,spring boot集成mybatis来操作数据库,所以在使用springboot的cache组件时,需要先搭建一个简单的s ...

  7. FP-Tree算法详细过程(Java实现)

    我就不说FP-Tree的作用.优点什么的了,直接用例子来解释构建FP-Tree和找出所有频繁项集,第一次写博客,不对之处还请指出. 输入文件: testInput.txt T1 T2 T3 T4 T5 ...

  8. Gradle 梳理 - 插件

    Gradle 教程:第二部分,JAVA PROJECTS[翻译]   原文地址:http://rominirani.com/2014/07/28/gradle-tutorial-part-2-java ...

  9. sort,uniq,wc,tr

    sort (选项)(参数)      sort是用来排序数据的. 以下面这个文本为例 [root@bogon ~]# cat a 123 4567 88 abc aaa AAA jk 777 777 ...

  10. Scala 学习笔记之函数(3)

    class student{ def sayHello(name: => String){ println(s"Hello, $name, welcome $name") } ...