Performance Counter - System.InvalidOperationException: Category does not exist

前端 未结 5 1452
花落未央
花落未央 2020-12-08 17:19

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 (be

5条回答
  •  爱一瞬间的悲伤
    2020-12-08 17:32

    I had an issue retrieving requests per second on IIS using code similar to the following

    var pc = new PerformanceCounter();
    pc.CategoryName = @"W3SVC_W3WP";
    pc.InstanceName = @"_Total";
    pc.CounterName = @"Requests / Sec";
    Console.WriteLine(pc.NextValue());
    

    This would sometimes throw InvalidOperationException and I was able to reproduce the exception by restarting IIS. If I run with a non warmed up IIS, e.g. after a laptop reboot or IIS restart, then I get this exception. Hit the website first, make any http request beforehand, and wait a second or two and I don't get the exception. This smells like the performance counters are cached,and when Idle they get dumped, and take a while to re-cache? (or similar).

    Update1: Initially when I manually browse to the website and warm it up, it solves the problem. I've tried programmatically warming up the server with new WebClient().DownloadString(); Thread.Sleep() up to 3000ms and this has not worked? So my results of manually warming up server, might somehow be a false positive. I'm leaving my answer here, because it might be the cause, (i.e. manual warming up), and maybe someone else can elaborate further?

    Update2: Ah, ok, here are some unit tests that summarises some learning from further experimenting I did yesterday. (There's not a lot on google on this subject btw.)

    As far as I can reason, the following statements might be true; (and I submit the unit tests underneath as evidence.) I may have misinterpreted the results, so please double check ;-D

    1. Create a performance counter and calling getValue before the category exists, e.g. querying an IIS counter, while IIS is cold and no process running, will throw InvalidOperation exception "category does not exist". (I assume this is true for all counters, and not just IIS.)

    2. From within a Visual Studio unit test, once your counter throws an exception, if you subsequently warm up the server after the first exception, and create a new PerformanceCounter and query again, it will still throw an exception! (this one was a surprise, I assume this is because of some singleton action. My apologies I have not had enough time to decompile the sources to investigate further before posting this reply.)

    3. In 2 above, if you mark the unit test with [STAThread] then I was able to create a new PerformanceCounter after one has failed. (This might have something to do with Performance counter possibly being singletons? Needs further testing.)

    4. No pause was required for me before creating counter and using it, despite some warnings in MSDN same code documentation, other than the time it takes to create a performance counter itself before calling NextValue().In my case, to warm up the counter and bring the "category" into existance, was for me to fire one shot across the bow of IIS, i.e. make a single GET request, and viola, no longer get "InvalidOperationException", and this seems to be a reliable fix for me, for now. At least when querying IIS performance counters.

    CreatingPerformanceCounterBeforeWarmingUpServerThrowsException

    [Test, Ignore("Run manually AFTER restarting IIS with 'iisreset' at cmd prompt.")]
    public void CreatingPerformanceCounterBeforeWarmingUpServerThrowsException()
    {
        Console.WriteLine("Given a webserver that is cold");
        Console.WriteLine("When I create a performance counter and read next value");
        using (var pc1 = new PerformanceCounter())
        {
            pc1.CategoryName = @"W3SVC_W3WP";
            pc1.InstanceName = @"_Total";
            pc1.CounterName = @"Requests / Sec";
            Action action1 = () => pc1.NextValue();
            Console.WriteLine("Then InvalidOperationException will be thrown");
            action1.ShouldThrow();                
        }
    }
    
    
    [Test, Ignore("Run manually AFTER restarting IIS with 'iisreset' at cmd prompt.")]
    public void CreatingPerformanceCounterAfterWarmingUpServerDoesNotThrowException()
    {
        Console.WriteLine("Given a webserver that has been Warmed up");
        using (var client = new WebClient())
        {
            client.DownloadString("http://localhost:8082/small1.json");
        }
        Console.WriteLine("When I create a performance counter and read next value");
        using (var pc2 = new PerformanceCounter())
        {
            pc2.CategoryName = @"W3SVC_W3WP";
            pc2.InstanceName = @"_Total";
            pc2.CounterName = @"Requests / Sec";
            float? result = null;
            Action action2 = () => result = pc2.NextValue();
            Console.WriteLine("Then InvalidOperationException will not be thrown");
            action2.ShouldNotThrow();
            Console.WriteLine("And the counter value will be returned");
            result.HasValue.Should().BeTrue();
        }
    }
    

提交回复
热议问题