构建Azure工作者角色以处理来自~10个队列的数据的最佳方法

时间:2010-11-03 05:48:26

标签: c# azure architecture azure-storage

我有一个辅助角色,可以将数据抛入大约需要处理的10个队列中。有很多数据 - 可能每秒大约10-100条消息在各种队列中排队。

队列包含不同的数据并分别处理它们。特别是有一个非常活跃的队列。

我现在设置它的方式,我是一个单独的工作者角色,产生10个不同的线程,每个线程执行一个方法,有一个while(true){从队列中获取消息并处理它}。每当备份队列中的数据时,我们只需启动更多这些进程,以帮助加快队列中数据的处理速度。此外,由于一个队列更活跃,我实际上启动了许多指向同一方法的线程来处理来自该队列的数据。

但是,我发现部署的CPU利用率很高。几乎达到或接近100%。

我想知道这是不是因为线程饥饿?或者因为访问队列是RESTful并且线程最终通过连接和减慢速度来阻止彼此?或者,是因为我使用:

while(true)
{
   var message = get message from queue;
   if(message != null)
   {
       //process message
   }
}

执行得太快了?

邮件的每次处理也会将其保存到Azure表存储或数据库中 - 因此可能是保存占用CPU的数据的过程。

实际上,调试高CPU负载非常困难。所以,我的问题是:我是否可以进行一般性的架构更改,以帮助缓解+防止可能出现的任何问题? (例如,而不是使用while(true)使用不同类型的轮询 - 尽管我认为它最终对于该示例是相同的)。

也许简单地使用新的Thread()生成新线程并不是最好的方法。

5 个答案:

答案 0 :(得分:9)

我建议在你的循环中放入一个sleep语句......不仅是紧密的循环可能会占用CPU资源,而且你还需要为存储事务付费。你每隔万次检查队列,就要花一分钱。这是一个很小的成本,但随着时间的推移它可能会增加很多。

我也经常使用这样的代码:

while(true) { var msg = q1.GetMessage(); if (msg != null) { ... } msg = q2.GetMessage(); if (msg != null) { ... } }

换句话说,连续轮询队列而不是并行(这应该完全是一个单词)。这样你实际上只是一次一件事(如果你的任务是CPU密集的话很有用),但是你仍然在检查每个循环中的所有队列。

答案 1 :(得分:2)

与CPU有同样的问题。这可能是由Azure队列的非高效本地实现引起的。

最后,我添加了指数性睡眠策略(用于实现 - 在Lokad.CQRS for Azure项目中检出),其中队列被频繁轮询,但如果任何一个都没有消息,我们逐渐开始增加睡眠间隔直到达到某个上限。如果发现了消息 - 我们立即删除间隔。

这样整个系统不会浪费存储事务(和本地开发CPU),但如果连续出现多条消息,则会保持极其快速响应。

答案 2 :(得分:2)

查看Brian Hitney的Scaling Down Azure Roles视频。基本方法是生成一些线程,每个线程都有一个“worker”,而不是监视给定的队列并采取适当的行动。特别是这会阻止一个队列阻塞其他队列......

答案 3 :(得分:1)

我认为你的问题来自循环实现。必须通过类似sleep()的方式减慢轮询速度。否则,什么都不会阻止循环消耗100%CPU核心(这实际上是正常行为)。

答案 4 :(得分:1)

有一篇很棒的MSDN文章涵盖了所有这些

MSDN - Best Practices for Maximizing Scalability and Cost Effectiveness of Queue-Based Messaging Solutions on Windows Azure

它讨论了在有工作要做的时候添加线程和实例 - 并且在没有工作时退出,这样你就不会连续不必要地从多线程和实例中轮询队列,从而增加了交易成本和将CPU转换为具有100%CPU利用率的加热器。