如何并行化azure worker角色?

如何并行化azure worker角色?,azure,azure-worker-roles,parallel.foreach,Azure,Azure Worker Roles,Parallel.foreach,我在azure中运行了一个工作者角色 此工作进程处理包含大量整数的队列。对于每个整数,我必须进行相当长的处理(根据整数,从1秒到10分钟) 由于这相当耗时,我希望并行处理这些过程。不幸的是,当我使用400个整数的队列进行测试时,我的并行化似乎没有效率 以下是我的实现: public class WorkerRole : RoleEntryPoint { private readonly CancellationTokenSource cancellationTokenSour

我在azure中运行了一个工作者角色

此工作进程处理包含大量整数的队列。对于每个整数,我必须进行相当长的处理(根据整数,从1秒到10分钟)

由于这相当耗时,我希望并行处理这些过程。不幸的是,当我使用400个整数的队列进行测试时,我的并行化似乎没有效率

以下是我的实现:

  public class WorkerRole : RoleEntryPoint {
        private readonly CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
        private readonly ManualResetEvent runCompleteEvent = new ManualResetEvent(false);
        private readonly Manager _manager = Manager.Instance;
        private static readonly LogManager logger = LogManager.Instance;

        public override void Run() {
            logger.Info("Worker is running");

            try {
                this.RunAsync(this.cancellationTokenSource.Token).Wait();
            }
            catch (Exception e) {
                logger.Error(e, 0, "Error Run Worker: " + e);
            }
            finally {
                this.runCompleteEvent.Set();
            }
        }

        public override bool OnStart() {
            bool result = base.OnStart();

            logger.Info("Worker has been started");

            return result;
        }

        public override void OnStop() {
            logger.Info("Worker is stopping");

            this.cancellationTokenSource.Cancel();
            this.runCompleteEvent.WaitOne();

            base.OnStop();

            logger.Info("Worker has stopped");
        }

        private async Task RunAsync(CancellationToken cancellationToken) {
            while (!cancellationToken.IsCancellationRequested) {
                try {
                    _manager.ProcessQueue();
                }
                catch (Exception e) {
                    logger.Error(e, 0, "Error RunAsync Worker: " + e);
                }
            }
            await Task.Delay(1000, cancellationToken);

        }
    }
}
以及ProcessQueue的实现:

  public void ProcessQueue() {
            try {

                _queue.FetchAttributes();

                int? cachedMessageCount = _queue.ApproximateMessageCount;

                if (cachedMessageCount != null && cachedMessageCount > 0) {

                    var listEntries = new List<CloudQueueMessage>();

                    listEntries.AddRange(_queue.GetMessages(MAX_ENTRIES));

                    Parallel.ForEach(listEntries, ProcessEntry);
                }
            }
            catch (Exception e) {
                logger.Error(e, 0, "Error ProcessQueue: " + e);
            }
}
在ProcessQueue函数中,我尝试使用不同的MAX_条目值:首先=20,然后=2。 当MAX_ENTRIES=20时,速度似乎较慢,但无论MAX_ENTRIES的值是多少,速度似乎相当缓慢

我的虚拟机是A2媒体


我真的不知道我是否正确地进行了并行化;也许问题来自于工人本身(这可能是很难并行的)。

这里要考虑的一些事情:

  • 您的个人任务是否需要CPU?如果是这样,并行性可能没有帮助。然而,如果他们大部分时间都在等待其他资源处理数据处理任务,那么并行化是一个好主意

  • >P>如果并行化是一个好主意,考虑不要使用Realth.FoeCH来进行队列处理。并行。Foreach有两个问题妨碍您实现最佳状态:

    • 代码将等待所有已启动的线程完成处理后再继续。因此,如果您有5个线程,每个线程需要10秒,1个线程需要10分钟,那么Parallel.Foreach的总处理时间将是10分钟

    • 即使假设所有线程都将同时开始处理,Parallel.Foreach也不会以这种方式工作。它查看服务器上的内核数和其他参数,通常只启动它认为可以处理的线程数,而不知道这些线程中有什么。因此,如果您有很多非CPU绑定的线程,它们/可以/可以/同时启动,而不会导致CPU过度利用,那么默认行为可能不会以最佳方式运行它们

    如何以最佳方式做到这一点: 我确信有很多解决方案,但作为参考,我们构建它的方式(必须启动数百个独立线程并尽快完成它们)是使用ThreadPool.QueueUserWorkItem并手动跟踪正在运行的线程数

    基本上,我们使用线程安全集合来跟踪由ThreadPool.QueueUserWorkItem启动的正在运行的线程。线程完成后,将其从该集合中删除。队列监视循环与在该集合中执行逻辑无关。如果处理集合未达到您认为最理想的限制,队列监视逻辑将从队列获取消息。如果集合中有空间,它会尝试从队列中拾取更多消息,将它们添加到集合中,并通过ThreadPool.QueueUserWorkItem启动它们。处理完成后,它将启动一个委托,该委托从集合中清理线程


    希望这有帮助并且有意义

    您没有提到您正在使用哪种Azure消息队列技术,但是对于我希望并行处理多条消息的任务,我倾向于在服务总线队列和订阅上使用消息泵模式,利用OnMessage()方法在服务总线队列和订阅客户端上都可用:

    • QueueClient OnMessage()
    • SubscriptionClient OnMessage()-
    • 这些东西的工作原理概述:-)-
    从MSDN:

    调用OnMessage()时,客户端启动一个内部消息泵 不断轮询队列或订阅的。这个信息泵 由发出Receive()调用的无限循环组成。如果电话 超时时,它发出下一个Receive()调用

    此模式允许您使用委托(或者在我的首选情况下使用匿名函数),该委托在WaWorkerHost进程的单独线程上处理代理消息实例的接收。事实上,为了提高吞吐量水平,可以指定消息泵应该提供的线程数,从而允许并行地接收和处理队列中的2、4、8条消息。您还可以告诉消息泵在代理成功完成消息处理后自动将消息标记为完成。线程计数和自动完成指令都在重载方法的OnMessageOptions参数中传递

    public override void Run()
    {
        var onMessageOptions = new OnMessageOptions()
        {
            AutoComplete = true, // Message-Pump will call Complete on messages after the callback has completed processing.
            MaxConcurrentCalls = 2 // Max number of threads the Message-Pump can spawn to process messages.
        };
    
        sbQueueClient.OnMessage((brokeredMessage) =>
        {
    
            // Process the Brokered Message Instance here
    
        }, onMessageOptions);
    
        RunAsync(_cancellationTokenSource.Token).Wait();
    }
    
    如果需要,您仍然可以利用RunAsync()方法在主辅助角色线程上执行其他任务


    最后,我还建议您考虑将Worker角色实例扩展到至少2个(用于容错和冗余),以提高总体吞吐量。从我在该模式的多个生产部署中看到的情况来看,当多个工作者角色实例运行时,OnMessage()的性能非常好。

    谢谢您的回答。我的工人使用了99.9%的CPU;所以我可能还有另一个问题?为什么不使用多个A1实例呢?谢谢你的回答。我不明白。有两个A1而不是一个A2更好吗?无论如何,SLA至少要覆盖两个实例。每个可以是A1,一次只处理一个项目。这可以简化很多事情。
    public override void Run()
    {
        var onMessageOptions = new OnMessageOptions()
        {
            AutoComplete = true, // Message-Pump will call Complete on messages after the callback has completed processing.
            MaxConcurrentCalls = 2 // Max number of threads the Message-Pump can spawn to process messages.
        };
    
        sbQueueClient.OnMessage((brokeredMessage) =>
        {
    
            // Process the Brokered Message Instance here
    
        }, onMessageOptions);
    
        RunAsync(_cancellationTokenSource.Token).Wait();
    }