65.9K
CodeProject 正在变化。 阅读更多。
Home

性能计数器 - 多实例/多类别

starIconstarIconstarIconstarIconstarIcon

5.00/5 (1投票)

2013 年 9 月 17 日

CPOL

4分钟阅读

viewsIcon

14810

性能计数器:多实例/多类别。

引言

关于 Azure 中的性能计数器,网上已经有很多讨论。但是,大多数文章都给出了非常基本的示例,并没有过多地谈论人们面临的问题。在下面的文章中,我尝试突出我遇到的一些问题并提供解决方法。

以下是我的应用程序的样子

创建了一个包含 Web(1 个实例)和 Worker(2 个实例)角色的 Azure 应用程序,并使用 Windows Azure 存储。 为了检查性能,在 Worker 角色中启用了诊断功能,并使用 Powershell 脚本创建了作为“多实例”类别一部分的自定义计数器,该脚本通过具有提升权限的启动任务运行。 然后在应用程序代码中递增这些计数器,应用程序代码也以提升的权限运行。

Powershell 脚本创建了一个 AverageTimer32 和 AverageBase 计数器对。 这些计数器(例如,‘ResponseProcessTime’和‘ResponseProcessTimeBase’分别是自定义性能计数器名称)可帮助我们找出完成方法执行所需的时间。

遇到的问题

  1. 尽管自定义性能计数器在 Windows 机器上可见(可以使用 perfmon 和服务器资源管理器'machinename'性能计数器看到),但它们并没有传输到 WADPerformanceCounters 表,无论是在本地模拟器上还是在云存储上。 只有偶尔我们才能看到计数器保存在 WADPerformaceCountersTable 中。 有时,计数器仅出现在来自单个实例的 WADPerformanceCountersTable 中。 如果这些计数器不可见,我们将无法计算性能。 非自定义计数器 Committed Bytes 和 % Processor Time 始终在 WADPerformanceCountersTable 中可见。
  2. 在模拟器中遇到并在 WADDiagnosticInfrastructureLogTable 中看到的错误。
  3. 错误详细信息:/p>

    PdhGetFormattedCounterArray(\MSBAdapter(deployment18(283).
       MyAdapter.MyAdapter.Worker_IN_0)\ResponseProcessTime) failed pdhStatus=c0000bc6; retry 2
    PdhGetFormattedCounterArray(\MSBAdapter(deployment18(283).MyAdapter.
       MyAdapter.Worker_IN_0)\ResponseProcessTimeBase) failed pdhStatus=800007d5; retry 2
  4. AverageTimer32 计数器并不总是按预期运行。 有时,当计数器值成功传输到 WADPerformanceCounterTable 中时,计数器的值为零。

本文的剩余部分将讨论如何解决这些问题

背景

本文将重点介绍问题及其解决方案,而不是深入介绍性能计数器原理及其类型。 有关性能计数器的学习,请参考 –

使用代码

上述问题被发现是 Windows 的一个已知限制,即一个进程在第一个进程启动后检索由另一个进程创建的实例计数器的能力。 这表明它正在改变 MonAgent (MA) 启动和计数器实例创建之间的竞争条件,因为我们正在使用多个 worker 实例。

"最佳的解决方法可能是在启动任务中将实例计数器创建为“Singleinstance”,但是,由于需要在角色的生命周期内的任意时间创建实例计数器,因此需要以编程方式重置 WAD 配置,如下所示,同时创建新的计数器实例”,因此,我们没有将性能计数器类别从 Multiisatnce 更改为单实例,而是继续通过启动中的 powershell 将 perf 计数器类别创建为“multinsatnce”,但是,我们不会在不同级别任意时间创建计数器,而是创建一个具有公共属性的类,该类的每个属性都用于我们要维护的计数器,并在构造函数中创建它们。 然后通过实际需要的那些属性访问所有计数器。 可以使用 Ninject 注入单个实例。

代码如下

  1. 创建计数器的接口/类
  2. public interface IDiagnosticHelper 
    { 
        PerformanceCounter ResponseProcessTime { get; } 
        PerformanceCounter ResponseProcessTimeBase { get; }     
    } 
    
    public class DiagnosticHelper : IDiagnosticHelper 
    { 
        public static string defaultPerformanceCounterCategory = "MyAdapter"; 
        public static string defaultPerformanceCounterInstance = RoleEnvironment.CurrentRoleInstance.Id; 
        
        public PerformanceCounter ResponseProcessTime { get; private set; } 
    
        public PerformanceCounter ResponseProcessTimeBase { get; private set; } 
       
        public DiagnosticHelper() 
        { 
           ResponseProcessTime = new PerformanceCounter(defaultPerformanceCounterCategory,  
              "ResponseProcessTime ", defaultPerformanceCounterInstance, false); 
           ResponseProcessTimeBase = new PerformanceCounter(defaultPerformanceCounterCategory, 
              "ResponseProcessTimeBase", defaultPerformanceCounterInstance, false); 
                       
            Trace.TraceWarning("DiagnosticHelper Created performance counters."); 
        } 
    }
  3. 我们使用 Ninject 进行依赖注入。 在我们的 Ninject Module 中,我们在绑定时指定单例范围(为了避免由于运行我们的 worker 角色的 2 个实例而产生的竞争条件),如下所示
  4. Bind<IDiagnosticHelper>().To<DiagnosticHelper>().InSingletonScope();
  5. 在 workerrole 中,我们在 Onstart 和 Run 中 ninject 这个接口,以便按照以下方式创建这些计数器
  6. ninjectKernel.Get<IDiagnosticHelper>();
  7. 现在,在我们要递增计数器的任何类/处理程序中,我们都不会再次显式创建计数器,而只是调用诊断帮助程序
  8. public class MyHandler 
    { 
        private readonly IDiagnosticHelper diagnosticHelper; 
        public MyHandler(IDiagnosticHelper diagnosticHelper) 
        { 
            this.diagnosticHelper = diagnosticHelper; 
        } 
    
        void CallMe() 
        { 
            var watch = new Stopwatch(); 
            watch.Start(); 
            int i = 3; 
            int j = 7; 
            int h = i + j; 
            Thread.Sleep(5000); 
            watch.Stop(); 
    
            if (PerformanceCounterCategory.Exists("MyAdapter")) 
            { 
                if (null != diagnosticHelper.GetFacilityTime) 
                { 
                    diagnosticHelper.GetFacilityTime.IncrementBy(watch.ElapsedTicks); 
                    diagnosticHelper.GetFacilityTimeBase.Increment(); 
                } 
            } 
        } 
    }

这段代码帮助我解决了 pdhStatus=c0000bc6 错误(这意味着数据无效),因为现在竞争条件不再是问题。

此外,出现错误 pdhStatus=800007d5(没有数据返回)是因为最初我将基本计数器“ResponseProcessTimeBase”添加到 PerformanceCounters.DataSources,这是不需要的,因为它是一个平均基本计数器,仅用于补充 AverageTimer32 计数器,并且本身不需要添加到数据源。

关注点

  1. 如果您不需要显式需要多实例,最好选择单实例性能类别
  2. Cerebretta 的工具 Azure Diagnotic Manager 可用于以图形格式查看 PerfCounters

历史

  • 更新了代码格式。
© . All rights reserved.