性能计数器 - System.InvalidOperationException:类别不存在 [英] Performance Counter - System.InvalidOperationException: Category does not exist

查看:2347
本文介绍了性能计数器 - System.InvalidOperationException:类别不存在的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有以下类返回每个IIS第二当前请求的数量。我叫RefreshCounters每一分钟,以保持每二刷新请求值(因为它是平均水平,如果我把它太长旧值会影响结果太多)... ...当我需要显示当前RequestsPerSecond我称之为属性。

I have following class that returns number of current Request per Second of IIS. I call RefreshCounters every minute in order to keep Requests per Second value refreshed (because it is average and if I keep it too long old value will influence result too much)... and when I need to display current RequestsPerSecond I call that property.

public class Counters
{
    private static PerformanceCounter pcReqsPerSec;
    private const string counterKey = "Requests_Sec";
    public static object RequestsPerSecond
    {
        get
        {
            lock (counterKey)
            {
                if (pcReqsPerSec != null)
                    return pcReqsPerSec.NextValue().ToString("N2"); // EXCEPTION
                else
                    return "0";
            }
        }
    }

    internal static string RefreshCounters()
    {
        lock (counterKey)
        {
            try
            {
                if (pcReqsPerSec != null)
                {
                    pcReqsPerSec.Dispose();
                    pcReqsPerSec = null;
                }

                pcReqsPerSec = new PerformanceCounter("W3SVC_W3WP", "Requests / Sec", "_Total", true);
                pcReqsPerSec.NextValue();

                PerformanceCounter.CloseSharedResources();

                return null;
            }
            catch (Exception ex)
            {
                return ex.ToString();
            }
        }
    }
}

问题在于以下异常的有时的抛出:

System.InvalidOperationException: Category does not exist.

at System.Diagnostics.PerformanceCounterLib.GetCategorySample(String machine,\ String category)
at System.Diagnostics.PerformanceCounter.NextSample()
at System.Diagnostics.PerformanceCounter.NextValue()
at BidBop.Admin.PerfCounter.Counters.get_RequestsPerSecond() in [[[pcReqsPerSec.NextValue().ToString("N2");]]]

难道我不打烊的PerformanceCounter的previous情况正常吗?我在做什么错了,这样我结束了该异常现象?

Am I not closing previous instances of PerformanceCounter properly? What am I doing wrong so that I end up with that exception sometimes?

编辑:
而刚刚备案,我主持这个类在IIS网站(即,当然,托管应用程序池拥有管理权限),并调用从ASMX服务方法。使用计数器的值(显示出来)网站呼吁RefreshCounters每1分钟和RequestsPerSecond每5秒; RequestPerSecond被调用之间缓存。

And just for the record, I am hosting this class in IIS website (that is, of course, hosted in App Pool which has administrative privileges) and invoking methods from ASMX service. Site that uses Counter values (displays them) calls RefreshCounters every 1 minute and RequestsPerSecond every 5 seconds; RequestPerSecond are cached between calls.

我打电话RefreshCounters每隔1分钟,因为价值观往往成为过时 - 由旧的价值观也影响(即是实际1分钟前,例如)

I am calling RefreshCounters every 1 minute because values tend to become "stale" - too influenced by older values (that were actual 1 minute ago, for example).

推荐答案

Antenka导致你在一个好的方向在这里。你不应该部署和重新建立对价值每次更新/请求的性能计数器。有用于实例的性能计数器和第一读如下引用指示可以是不准确的成本。此外,您的锁(){...} 语句是非常广泛的(它们覆盖大量的报表),将是缓慢的。它最好有你的锁尽可能小。我给Antenka为质量参考和很好的建议一voteup!

Antenka has led you in a good direction here. You should not be disposing and re-creating the performance counter on every update/request for value. There is a cost for instantiating the performance counters and the first read can be inaccurate as indicated in the quote below. Also your lock() { ... } statements are very broad (they cover a lot of statements) and will be slow. Its better to have your locks as small as possible. I'm giving Antenka a voteup for the quality reference and good advice!

不过,我想我可以为你提供一个更好的答案。我有与监控服务器的性能体验公平一点,了解你需要什么。您code没有考虑到的一个问题是,无论code为显示你的性能计数器(的.aspx,的.asmx,控制台应用程序,winform应用程序等),可以在任何速度要求该统计信息;它可能被要求每隔10秒,每秒也许5次,你不知道,应该不会在意。因此,你需要从不会从code,实际上报告当前请求/秒值监测分开的PerformanceCounter收集code。而对于性能方面的原因,我还要向你展示如何设置在第一个请求的性能计数器,然后继续下去,直到没有人提出任何要求,持续5秒,然后关闭/妥善处理的PerformanceCounter。

However, I think I can provide a better answer for you. I have a fair bit of experience with monitoring server performance and understand exactly what you need. One problem your code doesn't take into account is that whatever code is displaying your performance counter (.aspx, .asmx, console app, winform app, etc) could be requesting this statistic at any rate; it could be requested once every 10 seconds, maybe 5 times per second, you don't know and shouldn't care. So you need to separate the PerformanceCounter collection code from that does the monitoring from the code that actually reports the current Requests / Second value. And for performance reasons, I'm also going to show you how to setup the performance counter on first request and then keep it going until nobody has made any requests for 5 seconds, then close/dispose the PerformanceCounter properly.

public class RequestsPerSecondCollector
{
    #region General Declaration
    //Static Stuff for the polling timer
    private static System.Threading.Timer pollingTimer;
    private static int stateCounter = 0;
    private static int lockTimerCounter = 0;

    //Instance Stuff for our performance counter
    private static System.Diagnostics.PerformanceCounter pcReqsPerSec;
    private readonly static object threadLock = new object();
    private static decimal CurrentRequestsPerSecondValue;
    private static int LastRequestTicks;
    #endregion

    #region Singleton Implementation
    /// <summary>
    /// Static members are 'eagerly initialized', that is, 
    /// immediately when class is loaded for the first time.
    /// .NET guarantees thread safety for static initialization.
    /// </summary>
    private static readonly RequestsPerSecondCollector _instance = new RequestsPerSecondCollector();
    #endregion

    #region Constructor/Finalizer
    /// <summary>
    /// Private constructor for static singleton instance construction, you won't be able to instantiate this class outside of itself.
    /// </summary>
    private RequestsPerSecondCollector()
    {
        LastRequestTicks = System.Environment.TickCount;

        // Start things up by making the first request.
        GetRequestsPerSecond();
    }
    #endregion

    #region Getter for current requests per second measure
    public static decimal GetRequestsPerSecond()
    {
        if (pollingTimer == null)
        {
            Console.WriteLine("Starting Poll Timer");

            // Let's check the performance counter every 1 second, and don't do the first time until after 1 second.
            pollingTimer = new System.Threading.Timer(OnTimerCallback, null, 1000, 1000);

            // The first read from a performance counter is notoriously inaccurate, so 
            OnTimerCallback(null);
        }

        LastRequestTicks = System.Environment.TickCount;
        lock (threadLock)
        {
            return CurrentRequestsPerSecondValue;
        }
    }
    #endregion

    #region Polling Timer
    static void OnTimerCallback(object state)
    {
        if (System.Threading.Interlocked.CompareExchange(ref lockTimerCounter, 1, 0) == 0)
        {
            if (pcReqsPerSec == null)
                pcReqsPerSec = new System.Diagnostics.PerformanceCounter("W3SVC_W3WP", "Requests / Sec", "_Total", true);

            if (pcReqsPerSec != null)
            {
                try
                {
                    lock (threadLock)
                    {
                        CurrentRequestsPerSecondValue = Convert.ToDecimal(pcReqsPerSec.NextValue().ToString("N2"));
                    }
                }
                catch (Exception) {
                    // We had problem, just get rid of the performance counter and we'll rebuild it next revision
                    if (pcReqsPerSec != null)
                    {
                        pcReqsPerSec.Close();
                        pcReqsPerSec.Dispose();
                        pcReqsPerSec = null;
                    }
                }
            }

            stateCounter++;

            //Check every 5 seconds or so if anybody is still monitoring the server PerformanceCounter, if not shut down our PerformanceCounter
            if (stateCounter % 5 == 0)
            {
                if (System.Environment.TickCount - LastRequestTicks > 5000)
                {
                    Console.WriteLine("Stopping Poll Timer");

                    pollingTimer.Dispose();
                    pollingTimer = null;

                    if (pcReqsPerSec != null)
                    {
                        pcReqsPerSec.Close();
                        pcReqsPerSec.Dispose();
                        pcReqsPerSec = null;
                    }
                }                                                      
            }

            System.Threading.Interlocked.Add(ref lockTimerCounter, -1);
        }
    }
    #endregion
}

好了现在一些解释。

Ok now for some explanation.


  1. 首先你会注意到这个类的设计是一个静态的单例。
    不能装载的多个副本,它有一个私有构造
    和热切初始化自身内部实例。这使得
    确保你不小心创建相同的多个副本
    的PerformanceCounter

  2. 接下来,您将在私有构造通知(这将只运行
    一旦当第一次访问类),我们同时创建
    的PerformanceCounter 和将要使用的计时器轮询
    的PerformanceCounter

  3. 计时器的回调方法将创建的PerformanceCounter 如果
    需要并获得它的下一个值是可用的。此外,每5次迭代
    我们要看到它以来对您的最后一个请求有多长
    的PerformanceCounter 的价值。如果已经超过5秒,我们将
    关闭轮询定时器为目前不需要。我们可以
    总是再次启动以后,如果我们再次需要它。

  4. 现在,我们有一个名为 GetRequestsPerSecond()为您的静态方法
    调用将返回RequestsPerSecond的当前值
    的PerformanceCounter

  1. First you'll notice this class is designed to be a static singleton. You can't load multiple copies of it, it has a private constructor and and eagerly initialized internal instance of itself. This makes sure you don't accidentally create multiple copies of the same PerformanceCounter.
  2. Next you'll notice in the private constructor (this will only run once when the class is first accessed) we create both the PerformanceCounter and a timer which will be used to poll the PerformanceCounter.
  3. The Timer's callback method will create the PerformanceCounter if needed and get its next value is available. Also every 5 iterations we're going to see how long its been since your last request for the PerformanceCounter's value. If it's been more than 5 seconds, we'll shutdown the polling timer as its unneeded at the moment. We can always start it up again later if we need it again.
  4. Now we have a static method called GetRequestsPerSecond() for you to call which will return the current value of the RequestsPerSecond PerformanceCounter.

这个实现的好处是,你只有一次创建性能计数器,然后直到你完成它继续使用。它易于使用,因为你简单的通话 RequestsPerSecondCollector.GetRequestsPerSecond()无论你需要它(的.aspx,的.asmx,控制台应用程序,WinForms应用程序等)。总会有只有一个的PerformanceCounter ,它总是会以每秒正好1次,无论你如何快速调用 RequestsPerSecondCollector.GetRequestsPerSecond()查询。它也将自动关闭和处置的的PerformanceCounter 如果你还没有要求在5秒以上,它的价值。当然,你可以调整计时器的时间间隔和超时毫秒,以满足您的需求。你可以更快地轮询和超时在说60秒5,而不是我选择5秒,因为它证明了它的工作原理非常快,而在Visual Studio调试。一旦你测试它,知道它的工作原理,你可能要更长的超时。

The benefits of this implementation are that you only create the performance counter once and then keep using until you are finished with it. Its easy to use because you simple call RequestsPerSecondCollector.GetRequestsPerSecond() from wherever you need it (.aspx, .asmx, console app, winforms app, etc). There will always be only one PerformanceCounter and it will always be polled at exactly 1 times per second regardless of how quickly you call RequestsPerSecondCollector.GetRequestsPerSecond(). It will also automatically close and dispose of the PerformanceCounter if you haven't requested its value in more than 5 seconds. Of course you can adjust both the timer interval and the timeout milliseconds to suit your needs. You could poll faster and timeout in say 60 seconds instead of 5. I chose 5 seconds as it proves that it works very quickly while debugging in visual studio. Once you test it and know it works, you might want a longer timeout.

希望这有助于你不仅更好地利用PerformanceCounters,也觉得安全重用这个类,这是独立于任何你想要显示的统计数据。可重复使用code始终是一个加分!

Hopefully this helps you not only better use PerformanceCounters, but also feel safe to reuse this class which is separate from whatever you want to display the statistics in. Reusable code is always a plus!

编辑:作为后续行动的问题,如果你同时这种性能计数器运行要表现一些清理工作或照顾任务每60秒是什么?嗯,我们已经有了运行每隔1秒和可变跟踪呼吁我们循环迭代定时器 stateCounter 这是每个定时器的回调递增。所以,你可以在某些code这样的补充:

As a follow up question, what if you want to performance some cleanup or babysitting task every 60 seconds while this performance counter is running? Well we already have the timer running every 1 second and a variable tracking our loop iterations called stateCounter which is incremented on each timer callback. So you could add in some code like this:

// Every 60 seconds I want to close/dispose my PerformanceCounter
if (stateCounter % 60 == 0)
{
    if (pcReqsPerSec != null)
    {
        pcReqsPerSec.Close();
        pcReqsPerSec.Dispose();
        pcReqsPerSec = null;
    }
}

我要指出的是,在这个例子性能计数器不应该变坏。我相信,请求/秒应该是的平均值的,而不是一个的均线的统计数据,但本示例只是说明你的可以 做定期的时间间隔任何类型的清理磁盘或保姆你的的PerformanceCounter 。在这种情况下,我们正在关闭和处理性能计数器,这将导致它要对下一个计时器回调重建。你可以修改此为您的使用情况,并根据具体的PerformanceCounter您使用的是大多数人读这问题/答案应该不需要做检查的文档所需的PerformanceCounter,看看这是连续数,平均,移动平均值,等等,并适当调整您的实现。

I should point out that this performance counter in the example should not "go stale". I believe 'Request / Sec" should be an average and not a moving average statistic. But this sample just illustrates a way you could do any type of cleanup or "babysitting" of your PerformanceCounter on a regular time interval. In this case we are closing and disposing the performance counter which will cause it to be recreated on next timer callback. You could modify this for your use case and according the specific PerformanceCounter you are using. Most people reading this question/answer should not need to do this. Check the documentation for your desired PerformanceCounter to see if it is a continuous count, an average, a moving average, etc... and adjust your implementation appropriately.

这篇关于性能计数器 - System.InvalidOperationException:类别不存在的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆