我找不到一个单一的、流行的模式来做到这一点。但是在 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),在那里可以手动处理它。