在没有WCF的情况下在MSMQ中创建重试机制

时间:2013-02-27 14:51:48

标签: wcf msmq message-queue messaging

我有多个现有的应用程序通过System.Messaging API使用MSMQ发送和接收消息。队列通常是非事务性的,是MSMQ 3和4的混合。

接收应用程序现在处理有害消息,以便在第一次出现任何异常时将消息放入错误队列以进行手动干预。但事实证明,绝大多数人工干预都只需将消息移回主队列进行另一次尝试即可成功。因此,为了使该过程自动化,我想向接收器添加一个重试功能,以便消息被移回给主队列一定次数,每个消息之间有给定的延迟。

我不想重新发明轮子,而是想利用MSMQ开箱即用的任何东西,以及围绕此问题的任何流行或最佳实践模式。为此,有很多关于MSMQ 4中对有害消息的额外支持。但它们似乎不容易通过.Net访问。此外,我可以找到使用它们的唯一参考是通过带有MSMQ绑定的WCF。

任何人都可以建议任何模式或指向任何实现重试的示例,如果没有使用WCF吗?

2 个答案:

答案 0 :(得分:7)

我无法找到这样做的单一流行模式。但是在对System.Messaging进行了一些讨论后,我能够利用Message属性和MSMQ行为,我认为这是一种以最少的移动部件完成工作的合适方式。

这是我实施的内容。结果是相当简单和轻量级 - 代码不多,易于维护:

我创建了一个名为RetryLevel的对象,它有三个属性:

int订单, int NumberOfRetries, TimeSpan延迟

接收器应用程序的配置现在有一个RetryLevel列表。因此新功能基本上支持n级重试。

然后我创建了一个名为RetryInfo的对象。该对象有两个属性:

int尝试, string SourceQueuePath

RetryInfo对象的一个​​实例被序列化并存储在每个最终被重试的Message的Extension属性中。这允许我跟踪消息本身的当前重试状态,从而消除了维护单独的重试元数据存储的需要以及协调消息ID,保持数据同步等所有开销。

最后,我在接收者的配置中添加了一个等待队列路径。这个队列是消息在“超时”时被删除的地方。

现在,当一个消息处理程序拒绝一条消息时,接收器会反序列化它的RetryInfo(如果有的话),并查看(之前的)尝试次数,以确定它已经到达了哪个已配置的RetryLevels。​​

然后,接收方将Message的TimeToBeRecieved(TTBR)属性设置为DateTime.Now加上相应RetryLevel的Delay值。然后,它将AdministrativeQueue属性设置为从RetryInfo的SourceQueuePath属性创建的Queue,并将Message的AcknowledgeType设置为AcknowledgeTypes.NegativeReceive。最后,它将Message放在等待队列中。

从这里开始,MSMQ会观看Message的TTBR。当它超时时,MSMQ将消息放回其AdministrativeQueue属性中的队列,该属性是消息最初来自的队列。如果处理程序继续拒绝该消息,它只会向上移动RetryLevels。​​

如果消息的尝试超出了已配置的RetryLevels上的所有NumberOfRetries,则消息的TTBR属性设置为TimeSpan.Zero,UseDeadLetterQueue属性设置为true,并且消息将像其他任何内容一样放在等待队列中重试。但是,这一次,它立即超时,MSMQ将其发送到等待队列的主机系统死信队列(DLQ),在那里可以手动处理。

答案 1 :(得分:0)

正如你所说,你不想重新发明轮子,我建议使用许多可用的框架,如MassTransit或其他框架。

就个人而言,我对NServiceBus取得了积极的经验,Basic Publisher/Subscriber configuration from official documentation http://images.nservicebus.com/basic_pubsub.png位于MSMQ之上。

这使得配置错误处理非常容易。您可以为应用程序定义实际的工作队列,并且可以添加定义专用的错误队列。此外,您可以配置应用程序将尝试的次数 - 自动且完全透明的代码 - 尝试将毒性消息移动到您选择的队列中。

这使您可以轻松配置如下内容:“如果我无法在5次重试中正确处理此消息,请将其移至我的错误队列中。

这是一种开箱即用的功能。

示例配置(来自官方文档),请注意发布商部分的app.config

{{3}}

对于非WCF部分,NServiceBus适用于任何.NET应用程序。您可以选择简单地引用DLL,也可以使用NServiceBus作为容器将您的应用程序作为本地服务托管,或者您可以将其永久注册为Windows服务。最后,如果您有一天会改变主意,并切换到支持的WCF。 : - )