6

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

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

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

如果不使用 WCF,任何人都可以提出任何模式或指向任何实现重试的示例吗?

4

2 回答 2

7

我找不到一个单一的、流行的模式来做到这一点。但是在 System.Messaging 中探索了一下之后,我能够利用 Message 属性和 MSMQ 行为,我认为这是用最少的移动部件完成工作的合适方式。

这是我实施的。结果证明它相当简单和轻量级 - 代码不多且易于维护:

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

int Order, int NumberOfRetries, TimeSpan 延迟

接收应用程序的配置现在有一个 RetryLevel 列表。所以新特性基本支持n级重试。

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

int 尝试,字符串 SourceQueuePath

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

最后,我在接收器的配置中添加了一个等待队列路径。此队列是消息在“超时”时将被丢弃的地方。

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

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

从这里,MSMQ 监视消息的 TTBR。当超时时,MSMQ 将消息放回其管理队列属性中的队列中,该属性是消息最初来自的队列。如果消息继续被处理程序拒绝,它只会向上移动到 RetryLevels。

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

于 2013-04-09T15:28:36.327 回答
0

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

就个人而言,我对位于 MSMQ 之上的NServiceBus获得了积极的体验。

它使配置错误处理变得非常容易。您可以为您的应用程序定义实际的工作队列,并且您可以另外定义一个专用的错误队列。此外,您还可以配置应用程序将尝试多少次(对您的代码自动且完全透明),直到它将有害消息移动到您选择的队列中。

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

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

示例配置(来自官方文档),注意Publisherapp.config部分:

来自官方文档 http://images.nservicebus.com/basic_pubsub.png 的基本发布者/订阅者配置

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

于 2013-03-01T16:23:20.330 回答