为什么NServiceBus Bus.Publish()不是事务性的?

时间:2014-08-11 16:00:17

标签: nservicebus

设置: 我有几个订阅者通过MSMQ订阅发布者在同一台机器上制作的活动。订户使用两个不同的端点名称,并在其各自的过程中运行。 (这是NSB 4.6.3)

方案: 现在,如果我做了什么"坏"对于其中一个订阅者(比如删除MSMQ中的适当权限以接收消息,或者直接删除MSMQ中的队列......),并调用Bus.Publish(),我仍然会有一个事件成功发布到"好"订户(如果好订单在订阅存储中的订户列表上的坏订单之前),或者没有成功(如果坏订单在好订单之前)。

结论: 这里的结果是Bus.Publish()似乎不是事务性的,因为向订阅者发布所有成功或全部失败。根据列表中订户的顺序,最终结果可能不同。

问题: 这种行为是设计的吗? 这背后的想法是什么? 如果我想让这个电话交易,推荐的方式是什么? (一个选项似乎在我的代码中的TransactionScope中包含Bus.Publish()...)

1 个答案:

答案 0 :(得分:2)

发布是事务性的,或者至少是存在环境事务的情况。假设您尚未采取措施禁用事务,则当您输入Handle方法时,所有消息处理程序都会运行环境事务。 (检查Transaction.Current.TransactionInformation以查看第一手资料。)但是,如果您在IWantToRunWhenBusStartsAndStops之外操作,则不会有环境事务处理,因此您需要使用自己的{{1 }}

如何处理传递(特定于MSMQ传输),具体取决于目标是本地队列还是远程队列。

远程队列

对于远程队列,发布者根本不直接处理传递。它只是将两个消息丢弃在“发件箱”中,可以这么说。 MSMQ使用存储转发来确保这些消息最终传递到其预期目标,无论是在同一台计算机还是远程计算机上。在这些情况下,您可以查看您的传出队列,并查看由于您对目的地所做的任何操作而无法传递的邮件。

存储转发所提供的安全性意味着一个错误的订阅者无法取消发布者,因此减少了整体耦合。这是件好事!但这也意味着在部署NServiceBus系统时,监控传出队列是DevOps故事中非常重要的一部分。

本地队列

对于本地队列,MSMQ在技术上仍然可以在自己的管道中使用outoging队列的概念 - 我不确定它并不重要。但是,MSMQ能够执行(并且确实)的另一个步骤是在尝试发送之前检查本地队列的存在,并且如果它不存在或者出现问题则会抛出异常。这确实会影响出版商。

所以,是的,如果您从非事务状态(如TransactionScope内部)发布消息,并且已关闭的队列恰好是订阅存储列表中的#2,您可能会看到一条消息到达在SubscriberA但不在Subscriber B。如果它在禁用事务的消息处理程序中,由于消息重试逻辑,您可以看到多个副本到达SubscriberA!

<强>结果

IWantToRunWhenBusStartsAndStops非常适合快速演示和证明事情,但尝试尽可能少地使用真实逻辑,而选择环境事务适用的消息处理程序的安全性。还记得内部的异常可能会导致您的主机进程失效。当然,如果没有用自己的交易包装它,就不要在其中发布。