有条件地处理和删除多个消费者从MSMQ队列中发送的消息

时间:2014-05-15 09:50:02

标签: c# asp.net .net message-queue msmq

我发现了这个related question,但我的情况有点不同。

我有一个ASP.NET应用程序,它可以生成长时间运行的任务,这些任务应该由许多后台进程(Windows服务)处理。大多数任务都是类似的,大多数任务运行者都可以处理。由于客户端应用程序的不同版本(用户生成任务),某些任务只能由特定版本的任务运行程序处理。 Web服务器不了解任务的类型。它只是使用MSMQ将所有任务发送到同一队列。

如果任务进入队列,下一个免费任务运行器应该接收任务,决定他是否可以处理这类任务,从队列中删除任务并运行任务。

如果收到该消息的跑步者无法处理此类任务,则应将该消息放回队列,以便其他跑步者可以查看该消息。

我尝试使用事务实现条件接收,如果任务格式错误,我可以中止:

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

它似乎有用,但我不知道这是否是最好的方法。

此解决方案的另一个问题是,如果没有其他可以处理此消息的免费跑步者,我会一次又一次地收到它。

有没有办法只使用MSMQ解决这个问题?整个任务数据已存储在SQL数据库中。任务运行器通过HTTP API访问任务数据(这就是为什么我排除像SQLServer Service Broker这样的解决方案)。发送到消息队列的数据只是用于标识作业的元数据。

如果普通MSMQ不是正确的工具,我可以使用MassTransit解决问题(我不喜欢我必须安装并运行额外的MassTransit RuntimeServices + SQL数据库的事实)吗?其他建议?

2 个答案:

答案 0 :(得分:2)

你能创建另一个队列吗?如果是,那么我会创建多个队列。就像GenericTaskQ一样,它将包含xTaskQ和yTaskQ中的所有任务。现在你的xTaskRunner将从通用队列中选择任务,如果无法处理它,那么将它放在yTaskQ(或任何适当的q)中。同样适用于yTaskRunner,如果它无法处理将其放入xTaskQueue中的消息。 x和y任务管理员应该首先查找各自的队列,如果没有,那么请查看genericq。

如果您无法创建多个q,请使用消息(任务)标签(应该是唯一的,我们通常使用GUID)来记住任务运行器已经看到的任务并且无法处理。在实际收到消息之前,还要使用Peek检查此消息是否已被解决。

答案 1 :(得分:2)

您使用MSMQ的方式实际上是在绕开该技术的一些基本功能。如果读取器无法普遍处理队列消息,则会导致相当大的系统性能损失,许多任务处理服务在请求任务时可能会空手而归。在极端情况下,想象如果只有一个服务可以执行任务类型“A”会发生什么。如果该服务停止运行,并且从队列中取出的第一个任务是“A”类型,那么整个系统将关闭。

我建议采用以下两种方法之一:

  1. 利用多个队列,如每个任务版本一个。隐藏API或其他服务背后的任务检索。您的服务可以从一个或多个任务类型请求任务,或者您甚至可以允许任何任务。然后,API将负责确定要从哪个队列中提取(即映射到特定任务类型,随机选择一个,进行某种循环处理等)。
  2. 选择不同的存储技术进行排队。如果你编写了足够好的SQL,那么关系数据库就会胜任这项任务。你必须表现出很多谨慎,不要发生死锁。