简体   繁体   English

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

[英]Creating a retry mechanism in MSMQ without WCF

I have multiple existing applications that send and receive messages using MSMQ via the System.Messaging API. 我有多个现有的应用程序,通过System.Messaging API使用MSMQ发送和接收消息。 The queues are generally non-transactional and are a mixture of MSMQ 3 and 4. 队列通常是非事务性的,是MSMQ 3和4的混合体。

The receiving applications handle poison messages now to the extent that on the first occurrence of any exception the messages are put on an error queue for manual intervention. 接收应用程序现在处理有害消息,以便在第一次出现任何异常时将消息放在错误队列上以进行手动干预。 But it turns out that the vast majority of manual intervention consists of simply moving the message back to the main queue for another try at which point it succeeds. 但事实证明,绝大多数人工干预都只需将消息移回主队列进行另一次尝试即可成功。 So to automate that process, I want to add a retry feature to the receiver such that messages are moved back to the main queue a given number of times with a given delay between each. 因此,为了使该过程自动化,我想向接收器添加重试功能,使得消息被移回给主队列一定次数,每个消息之间有给定的延迟。

Rather than reinventing the wheel, I want to leverage anything I can that MSMQ provides out of the box as well as any popular or best practice patterns around this. 我想利用MSMQ开箱即用的任何东西,以及围绕此问题的任何流行或最佳实践模式,而不是重新发明轮子。 To that end, there is a lot out there about the additional support for poison messages in MSMQ 4. But they don't appear to be easily accessible via .Net. 为此,有很多关于MSMQ 4中对有害消息的额外支持。但它们似乎不容易通过.Net访问。 Furthermore, the only references I can find to using them is via WCF with an MSMQ binding. 此外,我可以找到使用它们的唯一引用是通过带有MSMQ绑定的WCF。

Can anyone suggest any patterns or point to any examples that implement retry if one is not using WCF? 如果没有使用WCF,任何人都可以建议任何模式或指向实现重试的任何示例吗?

I wasn't able to find a single, popular pattern for doing this. 我无法找到一个单一,流行的模式来做到这一点。 But after poking around a bit in System.Messaging, I was able to leverage Message properties and MSMQ behavior in what I think was an appropriate way to get the job done with a minimum of moving parts. 但是在对System.Messaging进行了一些讨论之后,我能够利用Message属性和MSMQ行为,我认为这是一种以最少的移动部件完成工作的合适方式。

Here is what I implemented. 这是我实施的内容。 It turned out to be fairly simple and lightweight - not much code and easy to maintain: 事实证明它相当简单和轻量级 - 代码不多且易于维护:

I created an object called RetryLevel that has three properties: 我创建了一个名为RetryLevel的对象,它有三个属性:

int Order, int NumberOfRetries, TimeSpan Delay int Order,int NumberOfRetries,TimeSpan Delay

The configuration of the receiver application now has a list of RetryLevel. 接收器应用程序的配置现在有一个RetryLevel列表。 So the new feature basically supports n-level retries. 因此新功能基本上支持n级重试。

Then I created an object called RetryInfo. 然后我创建了一个名为RetryInfo的对象。 This object has two properties: 该对象有两个属性:

int Attempts, string SourceQueuePath int Attempts,string SourceQueuePath

An instance of the RetryInfo object is serialized and stored in the Extension property of each Message that ends up being retried. RetryInfo对象的一个​​实例被序列化并存储在每个最终被重试的Message的Extension属性中。 This allows me to track the current retry state on the message itself, thus eliminating the need to maintain a separate retry metadata store and all the overhead of reconciling message Ids, keeping the data in sync, etc. 这允许我跟踪消息本身的当前重试状态,从而消除了维护单独的重试元数据存储的需要以及协调消息ID,保持数据同步等所有开销。

Finally, I added a wait queue path to the receiver's configuration. 最后,我在接收器的配置中添加了一个等待队列路径。 This queue is where messages will be dropped while they are in "timeout". 此队列是消息在“超时”时丢弃的位置。

So now, when a message handler rejects a message, the receiver deserializes it's RetryInfo, if there is one, and looks at the number of (previous) Attempts to determine which of the configured RetryLevels it has reached. 所以现在,当消息处理程序拒绝一条消息时,接收器会反序列化它的RetryInfo(如果有的话),并查看(之前的)尝试次数,以确定它已经到达了哪个已配置的RetryLevels。

The receiver then sets the the Message's TimeToBeRecieved (TTBR) property to DateTime.Now plus the Delay value of the appropriate RetryLevel. 接收器然后将Message的TimeToBeRecieved(TTBR)属性设置为DateTime.Now加上相应RetryLevel的Delay值。 It then sets the AdministrativeQueue property to a Queue created from the RetryInfo's SourceQueuePath property and sets the Message's AcknowledgeType to AcknowledgeTypes.NegativeReceive. 然后,它将AdministrativeQueue属性设置为从RetryInfo的SourceQueuePath属性创建的Queue,并将Message的AcknowledgeType设置为AcknowledgeTypes.NegativeReceive。 Finally, it puts the Message on the wait queue. 最后,它将Message放在等待队列中。

From here, MSMQ watches the Message's TTBR. 从这里开始,MSMQ会观看Message的TTBR。 When it times out, MSMQ puts the message back on the queue in its AdministrativeQueue property which is the queue the message originally came from. 当它超时时,MSMQ将消息放回其AdministrativeQueue属性中的队列,该属性是消息最初来自的队列。 Should the message continue to be rejected by handlers, it just moves its way up the RetryLevels. 如果处理程序继续拒绝该消息,它只会向上移动RetryLevels。

If a Message's Attempts is beyond that of all the NumberOfRetries on the configured RetryLevels, the message's TTBR property is set to TimeSpan.Zero, the UseDeadLetterQueue property is set to true and the message is put on the wait queue just like any other retry. 如果消息的尝试超出了配置的RetryLevels上的所有NumberOfRetries,则消息的TTBR属性设置为TimeSpan.Zero,UseDeadLetterQueue属性设置为true,并且消息将像其他任何重试一样放在等待队列中。 This time, however, it times out immediately and MSMQ ships it to the wait queue's host's system dead letter queue (DLQ) where it can be dealt with manually. 但是,这一次,它立即超时,MSMQ将其发送到等待队列的主机系统死信队列(DLQ),在那里可以手动处理。

As you say you do not want to re-invent the wheel, I suggest using one the many available frameworks like MassTransit or others. 正如你所说,你不想重新发明轮子,我建议使用许多可用的框架,如MassTransit或其他。

Personally, I made positive experience with NServiceBus , which sits on top of the MSMQ. 就个人而言,我在NServiceBus上取得了积极的经验, NServiceBus位于MSMQ之上。

It makes configuring the error handling pretty easy. 它使配置错误处理变得非常容易。 You can define the actual working queue for your application and you can additinally define a dedicated error queue. 您可以为应用程序定义实际的工作队列,并且可以添加定义专用的错误队列。 Also, you configure how many tries the application will - automatically and completely transparent to your code - attempt until it moves the poison message into the queue of your choice. 此外,您还可以配置应用程序尝试的次数 - 自动且完全透明的代码 - 尝试将毒性消息移动到您选择的队列中。

This allows you to easily configure something like: " If I cannot process this message correctly within 5 retries, then move it into my error queue. " 这使您可以轻松配置以下内容:“ 如果我无法在5次重试中正确处理此消息,请将其移至我的错误队列中。

This is an out-of-the-box functionality. 这是一个开箱即用的功能。

Example configuration (from official documentation), note the app.config of the Publisher part: 示例配置(来自官方文档),请注意Publisher部分的app.config

Basic Publisher/Subscriber configuration from official documentation http://images.nservicebus.com/basic_pubsub.png 官方文档http://images.nservicebus.com/basic_pubsub.png中的基本发布者/订阅者配置

As for the non-WCF part, NServiceBus works just as well for any .NET application. 至于非WCF部分,NServiceBus适用于任何.NET应用程序。 You have the choice to simply reference the DLLs, or you can use NServiceBus as a container to host your application as a local service, or you can register it permanently as a windows service. 您可以选择简单地引用DLL,也可以使用NServiceBus作为容器将您的应用程序作为本地服务托管,或者您可以将其永久注册为Windows服务。 And finally, if you would one day change your mind, however, and switch to WCF that would be supported, too. 最后,如果您有一天会改变主意,并切换到支持的WCF。 :-) :-)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM