我有一个Worker角色来处理队列中的项目。它基本上是一个无限循环,它将项目从队列中弹出并异步处理它们。
我有两个配置设置(PollingInterval
和MessageGetLimit
),我希望更改后可以获取辅助角色(因此不需要重新启动)。
private TimeSpan PollingInterval
{
get
{
return TimeSpan.FromSeconds(Convert.ToInt32(RoleEnvironment.GetConfigurationSettingValue("PollingIntervalSeconds")));
}
}
private int MessageGetLimit
{
get
{
return Convert.ToInt32(RoleEnvironment.GetConfigurationSettingValue("MessageGetLimit"));
}
}
public override void Run()
{
while (true)
{
var messages = queue.GetMessages(MessageGetLimit);
if (messages.Count() > 0)
{
ProcessQueueMessages(messages);
}
else
{
Task.Delay(PollingInterval);
}
}
}
问题:
在高峰时段,while循环可能每秒运行几次。这意味着它每天最多可查询配置项100,000次。
这有害还是低效?
答案 0 :(得分:3)
John的答案很好,使用环境更改/更改事件来修改您的设置而不重启,但我想也许更好的方法是使用指数退避策略使您的轮询更多高效。通过使代码行为更加智能化,您将减少调整它的频率。请记住,每次更新这些环境设置时,都必须将其推广到所有实例,这可能需要一些时间,具体取决于您运行的实例数。此外,你在这里迈出了一步,人类必须参与其中。
您正在使用Windows Azure存储队列,这意味着每次GetMessages执行它时都会调用该服务并检索0个或更多消息(直到您的MessageGetLimit)。每当它要求您收取交易费用时。现在,了解交易真的很便宜。每天甚至100,000笔交易是0.01美元/天。但是,不要低估循环的速度。 :)你可能获得更多的吞吐量,如果你有多个工作者角色实例,这会增加(虽然与实际运行实例本身相比仍然是一个非常小的金额)。
更有效的途径是采用指数退避方法从队列中读取消息。看看Maarten的这篇文章就一个简单的例子:http://www.developerfusion.com/article/120619/advanced-scenarios-with-windows-azure-queues/。结合退出方法,根据队列深度自动调整工作者角色,您将拥有一个更少依赖于人工调整设置的解决方案。输入实例计数的最小值和最大值,根据消息在下次请求消息时出现的次数来调整要提取的消息数量等。这里有很多选项可以减少您的参与并拥有一个高效的系统。
此外,您可能会查看Windows Azure Service Bus Queues,因为它们实现了长轮询,因此在等待工作到达队列时会导致更少的事务。
答案 1 :(得分:2)
Upfront disclaimer,我没有使用RoleEnvironments。
GetConfigurationSettingValue
的MDSN文档指出从磁盘读取配置。 http://msdn.microsoft.com/en-us/library/microsoft.windowsazure.serviceruntime.roleenvironment.getconfigurationsettingvalue.aspx。所以经常打电话肯定会很慢。
MSDN文档还显示设置更改时触发了一个事件。 http://msdn.microsoft.com/en-us/library/microsoft.windowsazure.serviceruntime.roleenvironment.changed.aspx。您可以使用此事件仅在实际更改时重新加载设置。
这是一种(未经测试,未编译)的方法。
private TimeSpan mPollingInterval;
private int mMessageGetLimit;
public override void Run()
{
// Refresh the configuration members only when they change.
RoleEnvironment.Changed += RoleEnvironmentChanged;
// Initialize them for the first time
RefreshRoleEnvironmentSettings();
while (true)
{
var messages = queue.GetMessages(mMessageGetLimit);
if (messages.Count() > 0)
{
ProcessQueueMessages(messages);
}
else
{
Task.Delay(mPollingInterval);
}
}
}
private void RoleEnvironmentChanged(object sender, RoleEnvironmentChangedEventArgs e)
{
RefreshRoleEnvironmentSettings();
}
private void RefreshRoleEnvironmentSettings()
{
mPollingInterval = TimeSpan.FromSeconds(Convert.ToInt32(RoleEnvironment.GetConfigurationSettingValue("PollingIntervalSeconds")));
mMessageGetLimit = Convert.ToInt32(RoleEnvironment.GetConfigurationSettingValue("MessageGetLimit"));
}