1

我有一个工人角色,它处理队列中的项目。它基本上是一个无限循环,将项目从队列中弹出并异步处理它们。

我有两个配置设置 (PollingIntervalMessageGetLimit),我希望工作人员角色在更改时拾取它们(因此不需要重新启动)。

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 次。

这是有害的还是低效的?

4

2 回答 2

3

John 的回答是使用 Environment Changed/Changed 事件来修改您的设置而无需重新启动的好方法,但我认为也许更好的方法是让您使用指数退避策略来提高轮询效率。通过让代码行为更智能,您将减少在那里调整它的频率。请记住,每次更新这些环境设置时,都必须将其推广到所有实例,这可能需要一些时间,具体取决于您运行的实例数量。此外,您在这里迈出了一步,必须让人类参与其中。

您正在使用 Windows Azure 存储队列,这意味着每次执行 GetMessages 时,它都会调用服务并检索 0 条或更多消息(直到您的 MessageGetLimit)。每次它要求时,您都会被收取一笔交易费用。现在,明白交易真的很便宜。即使每天 100,000 笔交易也是 0.01 美元/天。但是,不要低估循环的速度。:) 您可能会获得比这更多的吞吐量,并且如果您有多个工作角色实例,这加起来(尽管与实际运行实例本身相比仍然是一笔非常小的钱)。

更有效的方法是采用指数退避方法从队列中读取消息。查看 Maarten 在一个简单示例中的这篇文章:http: //www.developerfusion.com/article/120619/advanced-scenarios-with-windows-azure-queues/。将退避方法与基于队列深度的工作角色自动缩放相结合,您将获得一个较少依赖人工调整设置的解决方案。输入实例计数的最小值和最大值,根据您下次请求消息时出现的次数调整要提取的消息数量等。这里有很多选项可以减少您的参与并拥有一个高效的系统。

此外,您可能会查看 Windows Azure 服务总线队列,因为它们实现了长轮询,因此在等待工作进入队列时会导致更少的事务。

于 2013-10-28T12:15:43.067 回答
2

预先免责声明,我没有使用 RoleEnvironments。

MDSN 文档GetConfigurationSettingValue说明配置是从磁盘读取的。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"));
}
于 2013-10-28T08:21:44.027 回答