We can use a number of ways to find out which external requests are most frequent and how long the site has to wait for a response:
The remainder of this section focuses on performance counters. Also, performance counters are a convenient way to keep an eye on off-box requests on a day-to-day basis instead of as a one-off.
Windows offers you 28 types of performance counters to choose from. Some of these are esoteric, others extremely useful. For example, you can measure the rate per second that a request is made, and the average time in milliseconds that the site waits for a response. Adding your own custom counters is easy, and you can see their real-time values in perfmon, along with that of the built-in counters.
The runtime overhead of counters is minimal. You have already come across some of the hundreds of counters published by ASP.NET, SQL Server, and Windows itself. Even if you add a lot of counters, CPU overhead would be well under one percent.
This section describes only three commonly used counters: simple number, rate per second, and time. A list of all types of counters with examples of their use is available at http://msdn.microsoft.com/en-us/library/system.diagnostics.performancecountertype.aspx?ppud=4.
To use the counters, you need to follow these three steps:
In this example, we'll put counters on a page that simply waits for one second to simulate waiting for an external resource.
Windows allows you to group counters into categories. We'll create a new category "Test Counters" for the new counters.
Counter NameCounter TypeDescriptionNbr Page HitsNumberOfItems6464 bit counter, counting the total number of hits on the page since the website started.Hits/secondRateOfCountsPerSecond32Hits per secondAverage WaitAverageTimer32Time taken by the resource. Inspite of the name, it is used here to simply measure an interval, not an average.Average Wait Base*AverageBaseUtility counter required by Average Wait.
*The text says there are three counters, but the table lists four. Why? The last counter, Average Wait Base, doesn't provide information on its own, but helps to compute the value of counter Average Wait. Later on, we'll see how this works.
There are two ways to create the "Test Counters" category and the counters themselves:
To create the counters in Visual Studio:
This technique is easy. However, you'll need to remember to add the same counters to the production machine when you release new code with new custom counters. Writing a program to create the counters is more work initially, but gives you easier maintenance in the long run. Let's see how to do this.
From a maintenance point of view, it would be best to create the counters when the web application starts, in the Global.asax file. However, you would then have to make the account under which the application pool runs part of the Performance Monitor Users group.
An alternative is to create the counters in a separate console program. An administrator can then run the program to create the counters on the server. Here is the code.
using System;
using System.Diagnostics;
namespace CreateCounters
{
class Program
{
static void Main(string[] args)
{
To create a group of counters, you create each one in turn, and add them to a CounterCreationDataCollection object:
CounterCreationDataCollection ccdc = new
CounterCreationDataCollection();
Create the first counter, Nbr Page Hits. Give it a short help message and the counter type. Now, add it to the CounterCreationDataCollection object:
CounterCreationData ccd = new CounterCreationData
("Nbr Page Hits", "Total number of page hits",
PerformanceCounterType.NumberOfItems64);
ccdc.Add(ccd);
Add the second, third, and fourth counters along the same lines:
ccd = new CounterCreationData("Hits / second", "Total number of page hits / sec", PerformanceCounterType.RateOfCountsPerSecond32); ccdc.Add(ccd);
ccd = new CounterCreationData("Average Wait",
"Average wait in seconds",
PerformanceCounterType.AverageTimer32);
ccdc.Add(ccd);
ccd = new CounterCreationData("Average Wait Base", "",
PerformanceCounterType.AverageBase);
ccdc.Add(ccd);
Now, it's time to take the CounterCreationDataCollection object and make it into a category. Because you'll get an exception when you try to create a category that already exists if there already is a category with the same name, delete it now. Because you can't add new counters to an existing category, there is no simple work-around for this:
if (PerformanceCounterCategory.Exists("Test Counters"))
{
PerformanceCounterCategory.Delete("Test Counters");
}
Finally, create the Test Counters category. Give it a short help message, and make it a single instance. You can also make a category multi-instance, which allows you to split the category into instances. Also, pass in the CounterCreationDataCollection object with all the counters. This creates the complete category with all your counters in one go, as shown in the following code:
PerformanceCounterCategory.Create("Test Counters",
"Counters for test site",PerformanceCounterCategoryType.
SingleInstance,ccdc);
}
}
}
Now that you know how to create the counters, let's see how to update them in your code
To keep things simple, this example uses the counters in a page that simply waits for a second to simulate waiting for an external resource:
using System;
using System.Diagnostics;
public partial class _Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
First, increment the nbrPageHits counter. To do this, create a PerformanceCounter object, attaching it to the nbrPageHits counter in the Test Counters category. Then, increment the PerformanceCounter object:
PerformanceCounter nbrPageHitsCounter =
new PerformanceCounter("Test Counters", "Nbr Page Hits", false);
nbrPageHitsCounter.Increment();
Now, do the same with the Hits/second counter. Because you set its type to RateOfCountsPerSecond32 when you generated it in the console program, the counter will automatically give you a rate per second when viewed in perfmon:
PerformanceCounter nbrPageHitsPerSecCounter =
new PerformanceCounter("Test Counters", "Hits / second", false);
nbrPageHitsPerSecCounter.Increment();
To measure how long the actual operation takes, create a Stopwatch object, and start it:
Stopwatch sw = new Stopwatch();
sw.Start();
Execute the simulated operation:
// Simulate actual operation System.Threading.Thread.Sleep(1000);
Stop the stopwatch:
sw.Stop();
Update the Average Wait counter and the associated Average Wait Base counter to record the elapsed time in the stopwatch.
PerformanceCounter waitTimeCounter = new
PerformanceCounter("Test Counters", "Average Wait", false);
waitTimeCounter.IncrementBy(sw.ElapsedTicks);
PerformanceCounter waitTimeBaseCounter = new
PerformanceCounter("Test Counters", "Average Wait Base",
false);
waitTimeBaseCounter.Increment();
}
}
Now that we've seen how to create and use the most commonly used counters, it's time to retrieve their values.
Accessing your custom counters goes the following way: