高 CPU Azure Worker 角色

Posted

技术标签:

【中文标题】高 CPU Azure Worker 角色【英文标题】:High CPU Azure Worker Role 【发布时间】:2018-03-03 18:36:58 【问题描述】:

所以这是一个相当广泛的问题,但已经没有想法了。 我们目前正在运行 2 个工作角色实例,它们执行以下操作:

通过为每个批次生成 N 个线程来监控和处理 IoT 中心事件。 监控和处理来自 IoT 中心的连接/断开(操作监控)消息 某些服务总线是否工作(主题和队列) 写入 SQL、DocDB (Mongo API) 和 Azure 表存储以通过 NLOG 进行日志记录 通过 IoT 中心向设备发送云消息

我们面临的问题是,在高峰期,我们的 CPU 明显增加,但遗憾的是永远不会回落,而且经常会飙升至 100% 并坐在那里,直到我重新启动实例以使其回落。我一直在研究线程,因为我仍然觉得它可能与“while(1)”类型的场景有关,即使看不到原因。现在让我们进入代码...

WorkerRole.cs中:

    class WorkerRole : RoleEntryPoint
    
        private readonly ManualResetEvent runCompleteEvent = new ManualResetEvent(false);

        public override void Run()
        
            _eventprocessor.Start(instanceId, instanceIndex);//.Wait(-1);

            //Wait for shutdown to be called, else the role will recycle
            this.runCompleteEvent.WaitOne();
        
    

EventProcessor.cs 中: 我会尽量省略很多果汁,但会添加我认为值得的东西。将尽可能添加“伪代码”。

public class EventProcessor : IEventProcessor

  private readonly ManualResetEvent runCompleteEvent = new ManualResetEvent(false);

  public async Task Start(string serviceId, int InstanceIndex)
  

    //Setup Topic

    //Setup Queue

    //Setup EventProcessorHost for receiving events and operations monitoring and start listening

    //Receiving cloud to device feedback from service
    ReceiveFeedbackAsync();

    runCompleteEvent.WaitOne();
  

  async Task IEventProcessor.ProcessEventsAsync(PartitionContext context, IEnumerable<EventData> messages)
  
        if (messages.Count() > 0)
        
            if (!_cancellationSource.IsCancellationRequested)
            
                await ProcessEventsBulk(context, messages);
            
        

        if (messages.Count() > 0)
        
            await context.CheckpointAsync();               
        
   

  async Task ProcessEventsBulk(PartitionContext context, IEnumerable<EventData> messages)
        
            List<Task> TaskList = new List<Task>();
            foreach (EventData message in messages)
            
                var LastTask = Task.Run(() => GoBoy(context, message));
                TaskList.Add(LastTask);
            
            await Task.WhenAll(TaskList);
        

    async Task GoBoy(PartitionContext context, EventData message)
    
        try
        
            using (var db = new AppDbContext(_dbContextConnectionString))
            
                await ProcessEvent(message, context.Lease.PartitionId, new CoreManagerContainer(db), db);
                await db.SaveChangesAsync();
            
        
        catch (Exception e)
        
           //Do Some stuff...
        
    

  private async void ReceiveFeedbackAsync()
    
        var feedbackReceiver = serviceClientReceiver.GetFeedbackReceiver();
        while (true)
        
            try
            
              var feedbackBatch = await feedbackReceiver.ReceiveAsync();
              if (feedbackBatch == null) continue;
              foreach (var records in feedbackBatch.Records)
              

              
              await feedbackReceiver.CompleteAsync(feedbackBatch);
            
            catch (Exception)
            
              Thread.Sleep(30000);                    
            
         

    


如果有任何人需要任何额外的东西,请不要犹豫。我真的非常感谢任何帮助。

这里显示了我重新启动工作程序后的 CPU 下降

Microsoft 支持协助要求我执行一些 PerfViews 和一些 ProcDumps。结果是我们应该查看调用我们的集线器“https://abcxyz.azure-devices.net:443/$iothub/websocket”的线程。这就是为什么我决定添加 ReceiveFeedbackAsync() 方法的原因,因为我知道它依赖于永久连接到我们的集线器来收集反馈。

据我所知,我们正在正确地注册到我们的 EVPH,但如果有人也想查看该代码,请告诉我。

【问题讨论】:

如果您的 feedbackReceiver 由于某些情况而不断返回 null,那么您就有了一个完美的 'while(true);'循环。 @TonPlooij 感谢您的回复,我确实考虑过这个,除了它是推荐的解决方案。 docs.microsoft.com/en-us/azure/iot-hub/…。查看接收交付反馈部分。 一些想法:检查点更少,比如每1000条消息左右,或者基于一个计时器间隔,比如一分钟。摆脱 ORM 并使用普通的 ado.net。根据收到的消息数量,这可能会有所帮助。尤其是 ORM 会影响其转换为对象的性能。 @PeterBons 感谢您的建议!我现在在每个 partitionId 上一分钟后将其更改为检查点,让我们看看今晚高峰时段的情况。更新:忘记点击添加评论,但哇 CPU 现在在非高峰期要低得多,所以让我们看看。到目前为止,对结果非常满意。将让每个人都了解最新信息。 @David 在彼得提到的同一个循环中,您正在捕获异常,休眠然后重试。这也可能导致 while(true) 循环,因为每条错误消息都会引发异常,然后线程休眠 30 秒,然后重试。我认为您应该设置一个重试计数器,如果超过它,请在 catch 中跳出循环。我认为随着彼得的建议,您的问题应该得到解决。 【参考方案1】:

您是否单步执行了代码并确保您没有创建一个不会引发任何异常的无限循环条件,以便执行 Thead.Sleep。由于您希望在代码中休眠,因此最好避免使用异常来触发它。也许在处理每批反馈后将其编码为睡眠。 Exception 用于错误处理和异常情况,不帮助控制逻辑流。

【讨论】:

以上是关于高 CPU Azure Worker 角色的主要内容,如果未能解决你的问题,请参考以下文章

在 web/worker 角色的 windows azure 实例中安装虚拟驱动器 (vhd)

Azure 云服务工作者角色崩溃和不健康

nginx 常用模块整理

826. Most Profit Assigning Work

nginx一篇全搞定

为啥在 Worker 角色中托管 WCF 服务