简体   繁体   English

有条件的处理和删除多个使用者从MSMQ队列中的消息

[英]Conditionally handle and remove messages by multiple consumers from a MSMQ queue

I found this related question but my situation is a little bit different. 我找到了这个相关问题,但是我的情况有些不同。

I have a ASP.NET application that produces long running tasks that should be processed by a number of background processes (Windows Services). 我有一个ASP.NET应用程序,该应用程序会生成长时间运行的任务,这些任务应由许多后台进程(Windows Services)处理。 Most of the tasks are similar and can be handled by most task runners. 大多数任务是相似的,大多数任务执行者都可以处理。 Due to different versions of the client application (where the tasks are generated by users) some tasks can only be processed by task runners of specific version. 由于客户端应用程序的版本不同(任务由用户生成),某些任务只能由特定版本的任务运行者处理。 The web server has no knowledge about the kind of task. Web服务器不了解任务的种类。 It just sends all the tasks to the same queue using MSMQ. 它只是使用MSMQ将所有任务发送到同一队列。

If a task enters the queue, the next free task runner should receive the task, decide if he can handle this kind of task, remove the task from the queue and run the task. 如果任务进入队列,则下一个空闲任务运行者应接收该任务,确定他是否可以处理此类任务,将其从队列中删除并运行该任务。

If the runner that received the message is not able to process this kind of task, it should put back the message to the queue, so that another runner can have a look on the message. 如果收到消息的运行程序无法处理此类任务,则应将消息放回队列,以便其他运行程序可以查看该消息。

I tried to implement a conditional receive using a transaction, that I can abort if the task has the wrong format: 我尝试使用事务来实现条件接收,如果任务格式错误,我可以中止接收:

transaction.Begin();
var msg = queue.Receive(TimeSpan.FromSeconds(1000), transaction);
if (CanHandle(msg))
{
    transaction.Commit();
    // handle
}
else
{
    transaction.Abort();
}

It seems to work, but I don't know if this is the preferable way do go. 似乎可行,但我不知道这是否是首选的方法。

Another problem with this solution is, if there is no other free runner that can handle this message I will receive it again and again. 此解决方案的另一个问题是,如果没有其他免费的运行器可以处理此消息,我将一次又一次收到它。

Is there a way I can solve this problem only using MSMQ? 有没有一种方法可以仅使用MSMQ解决此问题? The whole task data is already stored in a SQL database. 整个任务数据已经存储在SQL数据库中。 The task runner accesses the task data over a HTTP API (Thats why I rule out solution like SQLServer Service Broker). 任务运行程序通过HTTP API访问任务数据(这就是为什么我排除SQLServer Service Broker之类的解决方案的原因)。 The data sent to the message queue is only meta data used to identify the job. 发送到消息队列的数据仅是用于标识作业的元数据。

If plain MSMQ is not the right tool, can I solve the problem using MassTransit (I didn't like the fact that I have to install and run the additional MassTransit RuntimeServices + SQL db for it) for example? 如果纯MSMQ不是正确的工具,例如,我可以使用MassTransit解决问题吗(我不喜欢必须为此安装并运行其他MassTransit RuntimeServices + SQL db的事实)? Other suggestions? 还有其他建议吗?

Can you create another queue? 您可以创建另一个队列吗? if Yes then I would create multiple queues. 如果是,那么我将创建多个队列。 Like GenericTaskQ, which will have all the tasks in it then xTaskQ and yTaskQ. 像GenericTaskQ一样,它将具有所有任务,然后是xTaskQ和yTaskQ。 Now your xTaskRunner will pick the tasks from Generic queue and if can not process it then put it in yTaskQ(or whatever q is appropriate). 现在,您的xTaskRunner将从通用队列中选择任务,如果无法处理,则将其放入yTaskQ(或合适的q)。 same is for yTaskRunner, if it cant handle the message put it in xTaskQueue. yTaskRunner也是如此,如果它无法处理消息,则将其放入xTaskQueue。 And x and y taskrunners should always look for their respective queues first, if nothing there then go look into genericq. x和y任务运行者应该始终先查找各自的队列,如果那里什么也没有,那就去研究genericq。

If you can not create multiple qs, use message(task) labels (which should be unique, we normaly use GUID) to remember what tasks have already been seen by a task runner and can not be processed. 如果您不能创建多个q,请使用message(task)标签(应该是唯一的,我们通常使用GUID)来记住任务运行者已经看到并且无法处理的任务。 also use Peek, to check if this message is already been addressed, before actually receiving the message. 还可以使用Peek在实际收到消息之前检查此消息是否已得到解决。

The way you are utilizing MSMQ is really circumventing some of the fundamental features of the technology. 您使用MSMQ的方式实际上是在规避该技术的某些基本功能。 If queue message cannot be universally handled by the reader, you are incurring a pretty sizable system performance penalty, where many of your task processing services can get sent back empty-handed when they ask for tasks. 如果阅读器无法普遍处理队列消息,则将导致相当大的系统性能损失,许多任务处理服务在请求任务时会空手而退。 In extreme scenario, imagine what would happen if there were only one service that could perform task type "A." 在极端情况下,想象一下,如果只有一项服务可以执行任务类型“ A”,将会发生什么。 If that service were to go down, and the first task to be pulled out of the queue is of type "A," then your entire system will shut down. 如果该服务将关闭,并且要从队列中拉出的第一个任务是“ A”类型,则整个系统将关闭。

I would suggest one of two approaches: 我建议以下两种方法之一:

  1. Utilize multiple queues, as in one per task version. 利用多个队列,每个任务版本使用一个队列。 Hide task retrieval behind an API or some other service. 将任务检索隐藏在API或其他服务的后面。 Your service can request a task from one or more task types, or you can even allow for anything. 您的服务可以从一种或多种任务类型中请求一项任务,甚至可以允许任何事情。 The API would then be charged with figuring out which queue to pull from (ie map to a specific task type, pick one at random, do some sort of round robining, etc.) 然后,API将负责确定要从哪个队列中拉出(例如,映射到特定任务类型,随机选择一个,进行某种循环等等)。
  2. Opt for a different storage technology over queueing. 在排队时选择其他存储技术。 If you write good enough SQL, a relational database would be more than up for the task. 如果您编写了足够好的SQL,那么关系数据库将不仅仅可以完成任务。 You just must exhibit a lot of care to not incur deadlocks. 您只是必须格外小心,以免发生僵局。

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

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