我们要求按照到达MSMQ的顺序处理所有邮件。
我们将向客户端公开WCF服务,此WCF服务将使用NServiceBus(Sendonly Bus)将消息发布到MSMQ。
我们将开发一个Windows服务(MessageHandler),它将使用Nservicebus从MSMQ中读取消息并将其保存到数据库中。我们的数据库每天都有几个小时不可用。
在数据库停机期间,我们希望该进程在MSMQ中重试第一条消息并暂停处理其他消息,直到数据库启动。数据库启动后,我们希望NServicebus按照发送消息的顺序进行处理。
在这种情况下,设置MaximumConcurrencyLevel =“1”MaximumMessageThroughputPerSecond =“1”会有帮助吗?
使用NServiceBus处理这种情况的最佳方法是什么?
答案 0 :(得分:1)
我们要求在中处理所有邮件 抵达MSMQ的顺序。
请参阅此问题的答案How to handle message order in nservicebus?以及此帖here。
我同意,尽管可以按顺序交付,但设计您的系统以使订单无关紧要要好得多。链接的文章概述了以下解决方案:
- 为所有消息添加序列号
- 在接收器中检查序列号是最后看到的数字+ 1如果没有抛出无序异常
- 启用二级重试(因此,如果它们出现故障,他们会在收到正确的消息后再次尝试)
但是,为了解决您的具体问题:
将设置MaximumConcurrencyLevel =“1” MaximumMessageThroughputPerSecond =“1”在这种情况下有帮助吗?
不是。
每当您需要订购交付时,逻辑的基本法则规定,在您的消息处理管道的某处,您必须具有单线程流程,以确保按顺序交付。
发生这种情况取决于你(查看resequencer pattern),但你当然可以将NserviceBus处理程序限制为单个线程(我认为你不需要设置MaximumMessageThroughputPerSecond以使其成为单线程虽然)。
但是,即使你这样做了,即使你使用了事务性队列,你仍然无法保证每个消息都会按顺序出列并处理到数据库,因为如果任何消息都有任何永久性失败它们将从队列中删除并处理下一条消息。
在数据库停机期间,我们希望该进程重试第一个 MSMQ中的消息并暂停处理其他消息,直到数据库 起来了。一旦数据库启动,我们希望NServicebus处理 命令邮件已发送。
不建议这样做。 NServiceBus中的第二级重试功能旨在处理意外和短期中断,而不是计划和长期中断。
对于初学者,当你的NServiceBus消息处理程序端点试图在其输入队列中处理消息并发现数据库不可用时,它将实现它的第二级重试策略,默认情况下将尝试出现频率增加5次,并且然后永久失败,将失败的消息粘贴在它的错误队列中。然后它将移动到输入队列中的下一条消息。
虽然这并不违反您的订单交付要求,但由于两个原因,这会使生活变得非常困难:
如果您事先知道经常计划的中断,那么处理它们的最简单方法是实现一个服务窗口,这是另一个用于计划的术语。
但是,Windows服务管理器不支持服务窗口的概念,因此您必须使用计划任务停止然后启动服务,或查看其他选项,例如hangfire,{{3}或其他一些quartz.net库。
答案 1 :(得分:1)
这取决于您需要消息按顺序到达的原因。如果您首先收到Order
条消息,然后收到所有属于某个订单的OrderLine
条消息,则有多种可能性。
一个是接受没有OrderLine
的{{1}}条消息。无论如何,Order
将在稍后出现。最终的一致性。
另一个是在NServiceBus Saga中收集消息(和可能的状态)。通常情况Order
需要先到达时,只有在稍后接收MessageA
和MessageB
时,才能让所有三条消息都能够启动传奇。所有三条消息都需要将它们联系在一起,就像一个独特的GUID。然后传奇将确保它正确地收集它们并且当所有消息到达时,可能存储其最终状态并将传奇标记为已完成。
另一种选择是将所有消息直接保存到数据库中,并让其他东西找出属于什么的东西。这对于数据仓库非常有用,无论如何都需要收集数据。有些数据可能不是100%准确(或一致),但这没关系。
异步消息传递使得很难按顺序100%处理它们,尤其是当调用WCF的客户端出错和/或无序发送时。这不是我第一次有这样的要求和无序消息。