Training
Module
Improve performance with a cache in a .NET Aspire project - Training
In this module, you'll learn about caches in a .NET Aspire cloud-native app and how to use them to optimize the performance of your microservices.
This browser is no longer supported.
Upgrade to Microsoft Edge to take advantage of the latest features, security updates, and technical support.
by Saad Ladki
Web content can be divided into two categories: static content and dynamic content. Static content does not change from request to request. The content that gets returned to the web browser is always the same. Examples of static content include HTML files, JPG or GIF files.
The other is dynamic content. This is the response that gets generated for dynamic content changes with every request. Examples include ASP.NET or PHP content.
There is a big range between these two categories which includes semi-dynamic content. Imagine a dynamic ASP.NET page that executes a database query. There is no reason to execute this query on every request if the underlying database tables change infrequently.
The IIS Output Caching feature targets semi-dynamic content. It allows you to cache static responses for dynamic requests and to gain tremendous scalability.
For this walkthrough, you need IIS 7.0 or above on Windows® Vista SP1 or on Windows Server® 2008 Beta 3 or later. This walkthrough also uses ASP.NET 2.0 which must be installed as an optional component in the IIS Setup.
In the following walkthrough, we use the IIS extensibility interfaces to add language-specific copyright messages to JPG files.
First, we do this by adding a managed handler. Dynamically inserting a copyright message into every JPG file comes with a cost however, because now managed code must be executed for every JPG file.
Next, we install WCAT, an IIS performance analysis tool, to measure the throughput of our JPG copyright handler.
Then we add Output Caching to regain performance degradation incurred by adding our copyright handler.
Create a directory called 'pictures' under the %systemroot%\inetpub\wwwroot
directory. Execute the following command in an elevated command shell:
md %systemdrive%\inetpub\wwwroot\pictures
Copy some digital pictures - this walkthrough assumes them to be JPG files - to the new pictures directory.
Note
Due to the high Internet Explorer security settings on Windows Server 2008, you might get a security dialog box telling you that the web site is blocked. To download the IIS wallpaper, add wallpaper.iis7.org to the list of trusted sites.
Create an application with the appcmd command-line tool.
%windir%\system32\inetsrv\appcmd add app -site.name:"Default Web Site"
-path:/pictures -physicalPath:%systemdrive%\inetpub\wwwroot\pictures
Create the directory App_Code underneath the pictures directory:
md %systemdrive%\inetpub\wwwroot\pictures\App_Code
Open Notepad and paste the following code into it.
using System;
using System.Web;
using System.Drawing;
using System.Drawing.Imaging;
namespace IIS7Demos
{
public class imageCopyrightHandler : IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
string message = "Copyright © IIS 7.0 Team";
try {
string languageHeader;
languageHeader = context.Request.Headers["Accept-Language"].Substring(0,2).ToUpper();
switch (languageHeader)
{
case ("DE"):
message = "IIS 7.0 Team - Alle Rechte vorbehalten";
break;
case ("ES"):
message = "Marca Registrada IIS 7.0 Team";
break;
default:
break;
}
}
catch
{
// if something fails, e.g. no Language-Accept header, we go with the english message
}
InsertCopyrightMessage ( context,
message,
"yellow"
);
}
void InsertCopyrightMessage(
HttpContext context,
string message,
string color
)
{
try
{
// get physical path of request
string strPath = context.Request.PhysicalPath;
// load as bitmap
Bitmap jpgFile = new Bitmap(strPath);
// add copyright message
Graphics g = Graphics.FromImage(jpgFile);
Font f = new Font("Arial", 20, GraphicsUnit.Pixel);
SolidBrush sb = new SolidBrush(Color.FromName(color));
// write copyright message to bitmap
g.DrawString( message,
f,
sb,
5,
jpgFile.Height - f.Height - 5
);
f.Dispose();
g.Dispose();
// save it to response stream
jpgFile.Save( context.Response.OutputStream,
System.Drawing.Imaging.ImageFormat.Jpeg
);
jpgFile.Dispose();
}
catch (Exception e)
{
context.Response.Write(e.Message);
}
}
public bool IsReusable
{
get { return true; }
}
}
}
Save the file as %systemdrive%\inetpub\wwwroot\pictures\App\_Code\imageCopyrightHandler.cs
.
Create the handler that executes this code when a JPG file is requested:
%windir%\system32\inetsrv\appcmd set config /section:system.webServer/handlers
/+[name='imageCopyrightHandler-Integrated',path='*.jpg',
verb='GET,HEAD',type='IIS7Demos.imageCopyrightHandler',preCondition='integratedMode']
We must also enable directory browsing because there is not yet a default document:
%windir%\system32\inetsrv\appcmd set config "Default Web Site/pictures"
-section:directoryBrowse -enabled:true
Browse to the pictures application by typing in the Internet Explorer address bar: http://localhost/pictures
. Click the link to your JPG file in the IIS directory listing. You should see the JPG image with the inserted Copyright message.
Look at the code. You see that the Copyright Message depends on the "Accept-Language" header that the browser sends. If you have a German version of Microsoft Server 2008 installed, you see the copyright message "IIS 7.0 Team - Alle Rechte vorbehalten"; if you have a Spanish language version, you see "Marca Registrada IIS 7.0 Team". In all other cases the copyright message will be "Copyright © IIS 7.0 Team". A way to test this code is to change the "Accept-Language" header Internet Explorer sends:
http://localhost/pictures/<your_jpg_file>.jpg
. The Copyright message has changed to the language you configured.Once the JPG Copyright Handler works, we must determine how fast our code is. Install an IIS 6.0 Resource Kit Tool to run performance tests:
Download the IIS 6.0 Resource Kit Tools and install them. Do a custom install and install only the Web Capacity Analysis Tool (WCAT). WCAT is the only feature of the IIS 6.0 Resource Kit Tools we need to do our performance tests.
Note
Due to the high Internet Explorer security settings on Windows Server 2008, you might get a security dialog box telling you that the web-site is blocked. To download the IIS 6.0 Resource Kit, add *.microsoft.com to the list of trusted sites.
Create a directory called PERFTEST, for example:
md %systemdrive%\perftest
The WCAT controller requires three input files:
The Script File
Create a new file called script.cfg in the perftest directory and paste the following content into it:
NEW TRANSACTION
classId = 1
NEW REQUEST HTTP
Verb = "GET"
URL = "http://localhost/pictures/<your image name>.JPG"
NEW TRANSACTION
classId = 2
NEW REQUEST HTTP
Verb = "GET"
URL = "http://localhost/pictures/<your image name>.JPG"
Note
Replace the <your image name> entry with the names of your JPG files. If you have more JPG files, you can add a new transaction. Make sure you give each transaction a new ClassID.
The Distribution File
The distribution file tells WCAT how it should weigh requests. With the two URLs above, we do an even 50/50 distribution. Each ClassID gets requested 50% of the time.
Create a file called %systemdrive%\perftest\distribution.cfg
in the perftest directory and paste the following content into it:
1 50
2 50
The Configuration File
Here are recommended parameters for the test:
Create a file called config.cfg in the perftest directory and paste the following content into it:
Warmuptime 5s
Duration 30s
CooldownTime 5s
NumClientMachines 1
NumClientThreads 20
Start the controller by executing the following commands:
Cd \perftest
"%programfiles%\IIS Resources\WCAT Controller\wcctl"
-c config.cfg -s script.cfg -d distribution.cfg -a localhost
As soon as all clients are connected, the perf test will start.
Because we only have one client, open another elevated command-shell and run the following:
"%programfiles%\IIS Resources\WCAT Client\wcclient.exe" localhost
To do this with more clients, set NumClientMachines in config.cfg to a higher number, and connect clients to the controller via the wcclient command by specifying the name of the controller machine.
Example: wcclient MyPerfTestControllerMachine
Note
If you do this on a 64-Bit version of Windows, WCAT is installed in the "program files (x86)" directory and you must use %programfiles(x86)%
to start WCAT.
Here are the results from the first run:
########################################################################
WCAT Performance Statistics_________________________________
Server : localhost ()
#Transactions : 3 (HTTP/1.1)
Total Async Sockets : 20 (5 WCAT Pool Threads)
Total Elapsed Time : 30 Secs (0 Hrs,0 Mins,30 Secs)
Current Connections : 20
Total Connection Attempts : 436 ( 14/Sec)
Total Connect Errors : 0 ( 0/Sec)
Total Success Connections : 436 ( 14/Sec)
Total Consec. Connect Errors: 0 ( 0/Sec)
Total Bytes : 32301100 ( 1051 KB/Sec)
Total Bytes Written : 32264 ( 1 KB/Sec)
Total Bytes Read : 32268836 ( 1050 KB/Sec)
Total Requests : 436 ( 14/Sec)
Total Responses : 436 ( 14/Sec)
Total Socket Reads : 6976 ( 232/Sec)
Total Socket Writes : 436 ( 14/Sec)
Total Parse Errors : 0 ( 0/Sec)
Total Socket Errors : 0 ( 0/Sec)
Total I/O Errors : 0 ( 0/Sec)
Total Internal Errors : 0 ( 0/Sec)
Total Time Outs : 0 ( 0/Sec)
Total 200 OK : 436 ( 14/Sec)
Total 30X Redirect : 0 ( 0/Sec)
Total 304 Not Modified : 0 ( 0/Sec)
Total 404 Not Found : 0 ( 0/Sec)
Total 500 Server Error : 0 ( 0/Sec)
Total Bad Status : 0 ( 0/Sec)
Min. Connect Time : 0 MS
Avg. Connect Time : 0 MS
Max. Connect Time : 16 MS
Min. Resp Time (1st Byte) : 1281 MS
Avg. Resp Time (1st Byte) : 1371 MS
Max. Resp Time (1st Byte) : 1578 MS
Min. Response Time (Last) : 1281 MS
Avg. Response Time (Last) : 1373 MS
Max. Response Time (Last) : 1578 MS
Current Outstanding Connects: 0 ( 20 Max)
Current Waitable Connects : 0 ( 20 Max)
Total Asynchronous Connects : 531 ( 1/Sec)
Total Discarded Connects : 0 ( 0/Sec)
########################################################################
The important number to look at is the requests per second. In this case, we get 14 requests per second.
A word of caution - the bigger your JPG files, the fewer requests you will see. It is likely that your machine is network-bound: IIS will not be able to handle more requests because the network is saturated with the data you are sending. You see the best results with JPG files in the 200-300 KB range.
The code to dynamically insert the copyright message is fairly slow. Fourteen requests per second is not fast for a web server. IIS performs much better. All you must do is create a caching policy that puts URLs with the JPG extension into the kernel Mode cache. Here is how you add the cache policy:
Note
The Output Cache User Interface is not available in versions before Windows Vista Service Pack 1.
To do the same procedure with the appcmd tool, enter the following command:
%windir%\system32\inetsrv\appcmd set config "Default Web Site/pictures"
-section:caching /+profiles.[extension='.jpg',duration='00:00:10',
policy='CacheForTimePeriod',varyByHeaders='Accept-Language']
Repeat the performance run to see what and how the configuration settings change.
Start the controller by executing the following commands:
Cd \perftest
"%programfiles%\IIS Resources\WCAT Controller\wcctl"
-c config.cfg -s script.cfg -d distribution.cfg -a localhost
Start the client with:
"%programfiles%\IIS Resources\WCAT Client\wcclient.exe" localhost
Note
If you do this on a 64-Bit version of Windows, WCAT is installed in the program files (x86)
directory and you must use %programfiles(x86)%
to start WCAT.
########################################################################
WCAT Performance Statistics_________________________________
Server : localhost ()
#Transactions : 3 (HTTP/1.1)
Total Async Sockets : 20 (5 WCAT Pool Threads)
Total Elapsed Time : 30 Secs (0 Hrs,0 Mins,30 Secs)
Current Connections : 19
Total Connection Attempts : 13020 ( 434/Sec)
Total Connect Errors : 0 ( 0/Sec)
Total Success Connections : 13019 ( 433/Sec)
Total Consec. Connect Errors: 0 ( 0/Sec)
Total Bytes : 958045737 (31186 KB/Sec)
Total Bytes Written : 963406 ( 31 KB/Sec)
Total Bytes Read : 957082331 (31155 KB/Sec)
Total Requests : 13019 ( 433/Sec)
Total Responses : 13019 ( 433/Sec)
Total Socket Reads : 258283 ( 8609/Sec)
Total Socket Writes : 13019 ( 433/Sec)
Total Parse Errors : 0 ( 0/Sec)
Total Socket Errors : 0 ( 0/Sec)
Total I/O Errors : 0 ( 0/Sec)
Total Internal Errors : 0 ( 0/Sec)
Total Time Outs : 0 ( 0/Sec)
Total 200 OK : 13019 ( 433/Sec)
Total 30X Redirect : 0 ( 0/Sec)
Total 304 Not Modified : 0 ( 0/Sec)
Total 404 Not Found : 0 ( 0/Sec)
Total 500 Server Error : 0 ( 0/Sec)
Total Bad Status : 0 ( 0/Sec)
Min. Connect Time : 0 MS
Avg. Connect Time : 0 MS
Max. Connect Time : 63 MS
Min. Resp Time (1st Byte) : 0 MS
Avg. Resp Time (1st Byte) : 33 MS
Max. Resp Time (1st Byte) : 125 MS
Min. Response Time (Last) : 0 MS
Avg. Response Time (Last) : 45 MS
Max. Response Time (Last) : 141 MS
Current Outstanding Connects: 0 ( 20 Max)
Current Waitable Connects : 0 ( 20 Max)
Total Asynchronous Connects : 14093 ( 147/Sec)
Total Discarded Connects : 0 ( 0/Sec)
########################################################################
To ascertain the performance in the output cache, look at output cache counters in the "Reliability and Performance Monitor". There are many interesting counters. Here is one example of how to use the "Reliability and Performance Monitor" together with the output cache.
You see that the number of cached URIs increases depending on how many items you request during the performance test.
The IIS output cache supports two cache policies. The regular output cache policy takes advantage of a cache that resides in an IIS worker process. The other cache policy is a kernel mode cache policy, in which case the cache resides in HTTP.SYS, a kernel-mode driver.
Caching your content in kernel-mode allows your web site to go faster. Modify the configuration of the pictures application to use the kernel-mode cache. Here is how the current configuration looks (%systemdrive%\inetpub\wwwroot\pictures\web.config
):
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<system.webServer>
<directoryBrowse enabled="true" />
<caching>
<profiles>
<add extension=".jpg" policy="CacheForTimePeriod"
duration="00:00:10" varyByHeaders="Accept-Language" />
</profiles>
</caching>
</system.webServer>
</configuration>
Now change it to use kernel-mode caching:
Open %systemdrive%\inetpub\wwwroot\pictures\web.config
.
Change the setting.
<caching>
<profiles>
<add extension=".jpg" policy="CacheForTimePeriod"
duration="00:00:10" varyByHeaders="Accept-Language" />
</profiles>
</caching>
to the following:
<caching>
<profiles>
<add extension=".jpg" kernelCachePolicy="CacheForTimePeriod"
duration="00:00:10" />
</profiles>
</caching>
You see that we do not use the varyByHeaders attribute anymore. This is because the kernelModeCache does not support some of the features that the user mode output cache supports.
There are two significant differences between user mode and kernel mode output cache.
Failed Request Event Buffering (FREB) is the best way to find out whether or not your request gets cached. FREB tells you why something does not get cached. Here is a sample of a FREB log. In this case, the HTTPSYS_CACHEABLE event tells you that the request does not get cached because the kernel-mode cache is not enabled.
For more detailed information on how to work with FREB, see Troubleshooting Failed Requests Using Tracing in IIS 7.
Use the following command to find out which content is cached in kernel mode:
netsh http show cachestate
Even if you enable Output Caching, IIS does not immediately cache a request. It must be requested a few times before IIS considers a request to be "cache worthy". Cache worthiness can be configured via the ServerRuntime section described in this MSDN article.
The two properties that determine cache-worthiness are frequentHitTimePeriod and frequentHitThreshold. A request is only cached if more than <frequentHitThreshold>
requests for a cacheable URL arrive within the <frequentHitTimePeriod>.
The default setting for frequentHitTimePeriod is 10 seconds.
The default setting for frequentHitThreshold is 2.
In the example above, we put all files with the extension JPG into the output cache. This does not always work because sometimes you want to be more selective and only put a particular document into the output cache. Here is how you do this with your most frequently requested page, your default document:
Create a file called default.aspx in the %systemdrive%\inetpub\wwwroot\pictures
directory and add the following code:
<%=DateTime.Now%>
Navigate to "Administrative Tools" and select "Internet Information Services (IIS) Manager".
Use the tree view on the left side to navigate to the "pictures" application.
Click "Content View" at the bottom of the page.
Select your default document, for example, default.aspx page.
Click "Switch to feature view" in the "Actions" menu on the right. Every setting that you configure will now only be applied to the default document.
Open the "Output Caching Rules" setting.
Add ".aspx" as a file extension.
Select "kernel-mode caching" then we can select "At time intervals" and enable "Monitor cached files" and enter 00:00:30 as the time interval.
Browse to http://localhost/pictures
with "Internet Explorer". By constantly refreshing the page (press Ctrl+F5 to make sure it does not come from the Browser cache), you see that the time will not change for 30 seconds.
Using the IIS Output Cache feature for semi-dynamic content can improve your web site. You see a substantial improvement in performance and throughput capacity. A simple configuration change is enough to take advantage of this feature.
Training
Module
Improve performance with a cache in a .NET Aspire project - Training
In this module, you'll learn about caches in a .NET Aspire cloud-native app and how to use them to optimize the performance of your microservices.