我有一个处理队列中的项目的辅助角色。 它基本上是一个无限循环,它将项目从队列中弹出并异步处理它们。
我有两个配置设置(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 次配置项。
这是有害的还是低效的?
John 的答案很好,使用环境更改/更改事件在不重新启动的情况下修改您的设置,但我认为也许更好的方法是让您使用指数退避策略来提高轮询效率。 通过让代码行为本身更智能,您将减少调整它的频率。 请记住,每次更新这些环境设置时,都必须将其推广到所有实例,这可能需要一些时间,具体取决于您运行的实例数。 此外,你在这里迈出了必须参与的一步。
您正在使用 Windows Azure 存储队列,这意味着每次执行 GetMessages 时,它都会调用服务并检索 0 条或更多条消息(最多为 MessageGetLimit)。 每次它要求时,您都会被收取一笔交易费用。 现在,了解交易真的很便宜。 即使每天 100,000 笔交易也是 0.01 美元/天。 但是,不要低估循环的速度。:) 你可能会获得比这更多的吞吐量,如果你有多个辅助角色实例,这会增加(尽管与实际运行实例本身相比,这仍然是一笔非常小的钱)。
更有效的途径是采用指数退避方法来从队列中读取消息。 看看马丁的这篇文章,举一个简单的例子:http://www.developerfusion.com/article/120619/advanced-scenarios-with-windows-azure-queues/。 将退避方法与基于队列深度的辅助角色的自动缩放相结合,你将拥有一个较少依赖人工调整设置的解决方案。 输入实例计数的最小值和最大值,根据下次请求消息时出现的次数调整要拉取的消息数,等等。 这里有很多选择可以减少您的参与并拥有一个高效的系统。
此外,您可以查看Windows Azure 服务总线队列,因为它们实现了长轮询,因此在等待工作进入队列时,它会导致更少的事务。
预先免责声明,我没有使用过 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"));
}