如何从现实世界中的多个队列中读取?

时间:2012-06-09 22:06:53

标签: azure message-queue azure-storage-queues azure-servicebus-queues

这是一个理论问题:

当我使用消息队列构建应用程序时,我将需要多个队列支持不同的数据类型以用于不同的目的。我们假设我有20个队列(例如,一个用于创建新用户,一个用于处理新订单,一个用于编辑用户设置等)。

我将使用“最小”1个Web角色和1个辅助角色将其部署到Windows Azure。

如何以正确的方式从所有这20个队列中读取?这就是我的想法,但我对这方面的实际经验很少或没有:

创建一个在worker角色'main'类中生成20个线程的类。让这些线程中的每一个执行一个方法来轮询不同的队列,让所有这些线程在每个轮询之间休眠(当然还有一个增加休眠时间的退避机制)。

这导致有20个线程(或21?),以及20个正在被主动轮询的队列,导致大量浪费的消息(每次轮询空队列时,它都被记为消息)。

你如何解决这个问题?

3 个答案:

答案 0 :(得分:4)

我读了其他答案(非常好的答案),并希望自己动手。

坚持使用Windows Azure队列,正如@Lucifure所描述的那样:除了两种情况外,我真的不需要多个队列:

  • 您需要不同的优先级。你想要的最后一件事是高优先级的消息被卡在数百个低优先级消息背后。为这些创建hi-pri队列。
  • 消息读取+删除的数量将超过每秒500个事务的目标。在这种情况下,创建多个队列,以跨存储分区分布事务卷(并且存储帐户将每秒处理超过5K的事务)。

如果坚持使用单个队列(基于存储,而不是服务总线),则可以一次读取消息块(最多32个)。您可以轻松编写一种格式,帮助您区分消息类型(可能使用简单的前缀)。然后,只需将消息传递给适当的线程进行处理。 Service Bus队列没有多消息读取,尽管它们允许预取(这会导致缓冲的消息被下载到缓存中)。

一个队列优于多个队列的优点:删除(或大大减少)“许多队列没有消息,导致空读取”的问题。

如果您需要更多的吞吐量,您可以随时增加执行队列读取和分派的线程数。

请记住,每次删除都是原子的;没有批处理。就队列轮询而言:你考虑退避是正确的。成功阅读消息(或消息块)后,无需后退。在尝试阅读后,如果没有得到任何东西,请退回。

与Service Bus队列相比的一个很好的优势:Windows Azure队列为您提供了近似的消息计数(在考虑向多个实例的横向扩展时,这非常有用)。 Service Bus队列不提供此功能。

答案 1 :(得分:2)

另一种策略是使用单个或更少的队列,以便队列可以支持更多这种类型的消息。如果您的系统架构可以支持,这种方法更容易管理,也更便宜。

在现实世界中,我已成功使用多个队列(出于可伸缩性的目的),每个队列在由计时器事件触发的单独线程上读取。根据队列上的负载和应用程序的需要,计时器事件已更改为以动态更改的间隔为队列提供服务。

答案 2 :(得分:1)

如果存储队列上的退避机制不足以满足您的要求,我建议您考虑服务总线队列。使用Service Bus Queues,您无需进行此类激进的轮询。

您仍然需要实现循环来轮询队列,但接收超时使其比使用存储队列时的常规轮询机制更轻。

在以下示例中,我尝试从队列中接收消息。如果没有找到消息,它将保持连接打开30秒以查看是否有新内容。如果30秒后没有消息到达,则Receive方法将返回null(我将尝试再次调用Receive)。请注意,最长超时为24

MessagingFactory factory = MessagingFactory.Create(ServiceBusEnvironment.CreateServiceUri("sb", ServiceNamespace, string.Empty), credentials); 
QueueClient myQueueClient = factory.CreateQueueClient("TestQueue");
myQueueClient.Receive(new TimeSpan(hours: 0, minutes: 0, seconds: 30));

为每个要读取的队列弹出线程是一个好主意,但是看到CLR线程池的容量限制,您还应该考虑异步接收消息(例如,使用 TaskFactory.FromAsync ):http://msdn.microsoft.com/en-us/library/windowsazure/hh851744.aspx