1.1.1 Summary
Recently, our system is facing a severe performance bottleneck. This is due to the increase in access traffic and the increase in client requests at the same time, which forces us to solve this problem from two aspects, increase hardware and improve system performance.
You can use various methods to optimize our system. This article will introduce how to use the Cache method to optimize the system performance and reduce the burden on the system.
1.1.2 text
Cache in different locations
The cache used in Web applications mainly includes client browser cache, client, server, and server cache. Therefore, the cache can be divided into the following types:
Client Caching)
Proxy Caching)
Reverse Proxy Caching)
Web Server Caching)
Cache in ASP. NET
ASP. NET has two types of cache: Output cache and data cache.
Output cache: This is the simplest cache type. It stores copies of pages sent to the client. When the next client sends the same page request, this page will not be regenerated (within the cache period), but will be retrieved from the cache. Of course, because the cache expires or is recycled, the page will be regenerated.
Data Cache
In addition, there are two special caches: fragment cache and Data Source cache.
Fragment cache: this is a special output cache. It does not cache the entire page but some pages. Because it is not feasible to cache the entire page, because some parts of the page are customized for users (such as user login information), but we can cache the shared parts of the application, in this case, we can consider using fragment caching and user control caching.
Data Source cache: it is the cache built on the data source control. It includes the SqlDataSource, ObjectDataSource, and XmlDataSource controls. The data source cache uses the data cache method. The difference is that we do not need to process the cache through the display method. We only need to set the corresponding attributes, and then the data source control can store and retrieve data.
Output Cache
The output cache caches the final displayed page. When the client requests the same page again, the control object will not be re-created, and the page lifecycle will not be started, and no code needs to be executed again, obtain the cached page in the cache.
Now we design a page. Every time a user sends a page request, the current code execution time is obtained and displayed on the page.
Figure 1 Output Cache
This is a simple example. Every time a user sends a page request, the time displayed on the page is updated. This is because each request obtains a new page, we do not need to respond to each page request in real time. We can cache the page through the output cache whenever the user sends the same page request, and during the cache validity period, you can return the cached page to the user through the output cache.
To implement the output cache, you only need to add the following code to the page:Copy codeThe Code is as follows: <! -- Adds OutputCache directive -->
<% @ OutputCache Duration = "23" VaryByParam = "None" %>
It supports five attributes. The Duration and VaryByParam attributes are mandatory.
Duration |
Required attribute. The time when the page should be cached, in seconds. Must be a positive integer. |
Location |
Specify the location where the output should be cached. To specify this parameter, it must be Any, Client, Downstream, None, Server, or ServerAndClient. |
VaryByParam |
Required attribute. The name of the variable in the Request. These variable names should generate separate cache entries. "None" indicates no change. "*" Can be used to create a cache entry for each variable group. Variables are separated. |
VaryByHeader |
Changes cache entries based on changes in the specified header. |
VaryByCustom |
Allow you to specify custom changes (for example, "Browser") in global. asax "). |
Table 1 Output cache attributes
Here, we set the validity period of the output cache to 23 seconds. That is to say, when the cache expires, it will be recycled. When the user requests the page again, the page will be re-created.
Client Cache
Another option is Client Cache. If you click the back button in the browser or re-enter the URL in the address bar, in this case, the browser will obtain the page from the cache; however, if you click "refresh", the cache in the browser will expire and the browser will send a page request.
To use the Client cache, you only need to specify the attribute Location = "Client" in OutputCache. The Code is as follows:
Copy codeThe Code is as follows: <! -- Sets client OutputCache -->
<% @ OutputCache Duration = "23" VaryByParam = "None" Location = "Client" %>
By adding the Location attribute to OutputCache, we implement the Client Cache. Some people may ask: "each user's first page request must be completed by the server, which cannot effectively reduce the service pressure ". Indeed, compared with the server cache, the client cache does not reduce code execution and database operations, but when we cache pages that contain personalized data on the server and the client requests the page, because personalized data varies with users, this will lead to request errors, so we can use the fragment cache to cache public parts or the client cache to cache user information.
Query String Cache
In the previous example, we set the VaryByParam attribute in OutputCache to None, so ASP.. NET Program caches only one copy of the page. If the page request contains query parameters, we can only view only the cache results within the cache validity period. Suppose we have a report program, it allows you to query related product information based on the product name.
First, we create two pages: Query and result pages. Because of the time relationship, we have designed the page as follows:
Figure 2 Report Program
First, we provide a query page for users to query the product information based on the product name (ProductName). The specific code is as follows:Copy codeThe Code is as follows: protected void Page_Load (object sender, EventArgs e)
{
If (! Page. IsPostBack)
{
// Gets product id from table Production. Product.
// Then binding data to drop down list control.
InitControl (GetProductId ());
}
}
/// <Summary>
/// Handles the Click event of the btnSubmit control.
/// Redirects to relative product information page.
/// </Summary>
Protected void btnSubmit_Click (object sender, EventArgs e)
{
Response. Redirect (string. Format ("Product. aspx? Productname = {0} ", ddlProductName. SelectedValue ));
}
After clicking the Submit button, the user jumps to the Product page and transmits the query parameter-Product Name (ProducName) in the Url ).
Next, we continue to complete the query page. Because the query parameter ProductName is passed in the previous page, we will obtain the corresponding product information based on the ProductName query database. The specific code is as follows:Copy codeThe Code is as follows: protected void Page_Load (object sender, EventArgs e)
{
// Get product name.
String productName = Request. QueryString ["productname"];
// Binding data to data grid view control.
InitControl (this. GetData (productName ));
}
/// <Summary>
/// Inits the control.
/// </Summary>
/// <Param name = "ds"> The dataset. </param>
Private void InitControl (DataSet ds)
{
DgvProduct. DataSource = ds;
DgvProduct. DataBind ();
}
/// <Summary>
/// Gets the data.
/// </Summary>
/// <Param name = "productName"> Name of the product. </param>
/// <Returns> Returns dataset </returns>
Private DataSet GetData (string productName)
{
// The query SQL base on product name.
String SQL =
String. Format (
"SELECT Name, ProductNumber, SafetyStockLevel, ReorderPoint, StandardCost, DaysToManufacture"
+ "FROM Production. Product WHERE ProductNumber = '{0 }'",
ProductName );
// Get data from table Production. Product.
Using (var con = new SqlConnection (ConfigurationManager. ConnectionStrings ["SQLCONN"]. ToString ()))
Using (var com = new SqlCommand (SQL, con ))
{
Com. Connection. Open ();
/// GdvData. DataSource = com. ExecuteReader ();
/// GdvData. DataBind ();
Var ada = new SqlDataAdapter (com );
Var ds = new DataSet ();
Ada. Fill (ds );
Return ds;
}
}
In the previous example, we obtained the ProductName value through the Request property QueryString, then queried the Database Based on ProductName, and finally bound the obtained data to the Datagridview control (note: SQL Injection is not taken into account in the previous instance ).
Figure 3 query results
Now we can add the output cache on the page, as shown in the following code:Copy codeThe Code is as follows: <! -- Adds OutputCache directive -->
<% @ OutputCache Duration = "30" VaryByParam = "None" %>
As mentioned above, when the output cache attribute VaryByParam = "None", ASP. the. NET program caches only one copy of the page within the cache validity period. Now we will send the request again within the cache validity period (30 s.
Figure 4 query results
We found that the query parameter ProductName = BK-M18B-40 is now available, but the query result is still data for ProductName = BB-9108, because the ASP. NET Program caches only one page copy within the cache validity period.
Through the above example, we found that only one page is cached and the page output cache containing query parameters is not applicable. In fact, in the previous example, we only need to make slight changes to meet the query parameters, as you know, you only need to set the VaryByParam attribute.
Figure 5 query results
Now the query can obtain the corresponding results. If the query parameters are the same as the previous request and the page cache is valid, the cache will be reused. Otherwise, a new page cache will be created.
Because ASP. NET adds an output cache for each query parameter, but we must note whether it is really necessary to cache a page copy for each query parameter. If a parameter ProductId is added to the query Url, now there are two query parameters in the Url (ProductName and ProductId ).
Previously, we set VaryByParam to "*" for ASP. the NET program creates a page cache for both ProductName and ProductId. If we only create a page cache for ProductName, We can modify VaryByParam as follows:Copy codeThe Code is as follows: <! -- Sets VaryByParam property -->
<% @ OutputCache Duration = "30" VaryByParam = "productname" %>
Custom Cache Control
We have introduced how to cache one or more pages through query parameters. In fact, ASP. NET also allows us to customize the cache method to determine whether to cache pages or reuse existing ones. In this case, we can set the VaryByCustom attribute.
Suppose we want to design a cache based on different userhostnames. Because the program first calls the global method GetVaryByCustomString () during execution to determine whether to cache the page or reuse the existing one, therefore, we can rewrite the GetVaryByCustomString () method to implement the cache based on UserHostName. First, we create a Global. the asax file then re-implements the global method GetVaryByCustomString () as follows:Copy codeThe Code is as follows: // <summary>
/// Gets vary cache based on custom string value.
/// </Summary>
/// <Param name = "context"> Http context. </param>
/// <Param name = "custom"> custom string </param>
/// <Returns> </returns>
Public override string GetVaryByCustomString (HttpContext context, string custom)
{
If (string. Equals (custom, "UserHostName", StringComparison. OrdinalIgnoreCase ))
{
// Indicates that the cache shocould be vary on user host name.
Return Context. Request. UserHostName;
}
Return base. GetVaryByCustomString (context, custom );
}
We have previously rewritten the GetVaryByCustomString () method to get the corresponding cache value when the UserHostName value is different.
Then let the program create a cache based on UserHostName, so we need to add the following code on the page:Copy codeThe Code is as follows: <! -- Set vary cache based on custom string value -->
<% @ OutputCache Duration = "30" VaryByParam = "None" VaryByCustom = "UserHostName" %>
We customize the current GetVaryByCustomString () method to implement different caching methods for Web programs based on UserHostName. In fact, we can implement more types of caching solutions, such: based on user roles, time, and Url.
Fragment Cache
In some cases, we cannot cache the whole page, but we still want to cache some pages to reduce the burden on the system. In fact, we can achieve this in two ways: fragment cache and data cache.
To implement fragment caching, we need to create a custom control to cache some pages, and then we add the OutputCache command to the custom control so that the entire page will not be cached, except for custom cache controls.
We have introduced the use of the output cache. You only need to add the OutputCache command to the page. It may be easier to add the output cache to several pages, however, we need to add the output cache function to dozens of pages, and in the previous example, the Duration Attribute values are directly Hard code to each page. If we need to modify the Duration Attribute value, you must modify each page, ASP. NET also needs to re-compile these pages, which is not conducive to our maintenance, the most important thing is to increase our workload.
In fact, we can define an outputCacheProfile (ProductCacheProfile) in the web. config file, add the CacheProfile attribute to the page, and assign it to ProductCacheProfile. The web. config file is set as follows:Copy codeThe Code is as follows: <caching>
<! -- Sets out put cache profile -->
<OutputCacheSettings>
<OutputCacheProfiles>
<Add name = "ProductCacheProfile" duration = "30"/>
</OutputCacheProfiles>
</OutputCacheSettings>
</Caching> now, we add the CacheProfile attribute to the page and set it to ProductCacheProfile, as shown below:
Copy codeThe Code is as follows: <! -- Set CacheProfile property -->
<% @ OutputCache CacheProfile = "ProductCacheProfile" VaryByParam = "None" %>
Data Cache
The Cache object is thread-safe. This means that no explicit locking or unlocking is required. The elements in the Cache object must be thread-safe. For example, if we create an object Product and multiple clients may simultaneously operate on this object, at this time, we must implement lock and unlock operations for the entity Product (for synchronization operations, see Singleton 6 implementations).
Automatic removal of Cache items in the Cache object: When the Cache expires, the dependent items are modified or the Cache is out of memory. ASP. NET will automatically remove this Cache item.
Cache items support dependency: You can add dependencies for files, database tables, or other resource types to the cache items.
SqlDataSource Cache
When cache is enabled in the SqlDataSource control, it caches the results in SelectCommand. If a parameter is included in an SQL query statement, the SqlDataSource control caches the results corresponding to each parameter value.
This is the same as the query page cache effect of the report program through the output cache, so we will use the SqlDataSource cache to achieve this effect.
Suppose we want to provide a report program that allows users to obtain product information by selecting the product name (ProductName.
First, we create two data source controls on the page: sourceProductName and sourceProduct, and then bind the data source to Dropdownlist and Gridview respectively. The specific implementation is as follows:Copy codeThe Code is as follows: <! -- The product number datasource START -->
<Asp: SqlDataSource ID = "sourceProductName" runat = "server" ProviderName = "System. Data. SqlClient"
EnableCaching = "True" CacheDuration = "3600" ConnectionString = "<% $ ConnectionStrings: SQLCONN %>"
SelectCommand = "SELECT ProductNumber FROM Production. Product"> </asp: SqlDataSource>
<! -- The product number datasource END -->
<! -- The product datasource START -->
<Asp: SqlDataSource ID = "sourceProduct" runat = "server" ProviderName = "System. Data. SqlClient"
EnableCaching = "True" CacheDuration = "3600" ConnectionString = "<% $ ConnectionStrings: SQLCONN %>"
SelectCommand = "SELECT Name, ProductNumber, SafetyStockLevel, ReorderPoint, StandardCost, DaysToManufacture
FROM Production. Product WHERE ProductNumber = @ ProductNumber ">
<SelectParameters>
<Asp: ControlParameter ControlID = "ddlProductNumber" Name = "ProductNumber" PropertyName = "SelectedValue"/>
</SelectParameters>
</Asp: SqlDataSource>
<! -- The product number datasource END -->
<! -- Binding the product number to gridview control -->
<! -- NOTE: Due to search and result in the same page, so need to set AutoPostBack is True -->
<Asp: DropDownList ID = "ddlProductNumber" AutoPostBack = "True" performanceid = "sourceProductName"
DataTextField = "ProductNumber" runat = "server">
</Asp: DropDownList>
<! -- Binding the product datasource to gridview control -->
<Asp: GridView ID = "gvProduct" runat = "server" performanceid = "sourceProduct" CssClass = "Product">
</Asp: GridView>
Now we can query the report program. If the ProudctName is not cached before, the corresponding cache will be created, and the cached one will be reused. The query result is as follows:
Figure 6 query results
Cache dependency
Dependencies between cache items
ASP. NET Cache allows us to establish dependencies between caches, that is, one Cache item depends on another Cache item. The following sample code creates two Cache items and establishes dependencies between them. The specific implementation is as follows:Copy codeThe Code is as follows: // Creates cache object Key1.
Cache ["Key1"] = "Cache Item 1 ";
// Makes Cache ["Key2"] dependent on Cache ["Key1"].
String [] dependencyKey = new string [1];
DependencyKey [0] = "Key1 ";
// Creates a CacheDependency object.
CacheDependency dependency = new CacheDependency (null, dependencyKey );
// Establishs dependency between cache Key1 and Key2.
Cache. Insert ("Key2", "Cache Item 2", dependency );
Now, when the Key1 cache item is updated or deleted from the cache, The Key2 cache item is automatically deleted from the cache.
File dependency
We have introduced the dependency between Cache items. ASP. NET Cache also provides the dependency between Cache items and files. When files are updated or deleted, the corresponding Cache items will also become invalid.
In the DEMO -- Weibo Feed introduced at the end of the previous blog "Ajax and JSON summary", we send a request to Sina Weibo API in real time to obtain the corresponding data, however, the number of requests within a certain period of time is limited. Once the limit is exceeded, the request is no longer accepted (For details, refer to Rate-limiting ). Therefore, you can Cache the data. When the client requests, if the cached data already exists, the data is directly returned. Otherwise, you can retry Weibo API request data.
First, we create an HttpHandler that sends a request to the Weibo API, stores the data in the file, and finally returns the data to the client.
Figure 7 Request Process
Next, we define the CacheData () method to save Weibo data to a text file and establish the dependency between the cache and the data file.Copy codeThe Code is as follows: // <summary>
/// Caches the data into text file.
/// </Summary>
/// <Param name = "context"> The http context </param>
Private void CacheData (HttpContext context)
{
// Weibo API.
String uri = context. Request. QueryString ["api"] + "? "+
"Source =" + context. Request. QueryString ["source"] + "&" +
"Count =" + context. Request. QueryString ["count"];
HttpWebResponse response = this. GetWeibos (uri );
If (null = response)
{
Throw new ArgumentNullException ("Response is null ");
}
String jsonData;
// Writes the reponse data into text file.
Using (var reader = new StreamReader (response. GetResponseStream (), Encoding. GetEncoding (response. CharacterSet )))
{
JsonData = reader. ReadToEnd ();
}
String dataPath = context. Server. MapPath ("weibo. json ");
Using (var writer = new StreamWriter (dataPath, false, Encoding. GetEncoding (response. CharacterSet )))
{
Writer. Write (jsonData );
}
// Establishs dependency between cache weibo and text file.
// Sets cache expires after 2 minuntes.
HttpRuntime. Cache. Insert ("weibo", jsonData, Dep, Cache. NoAbsoluteExpiration, TimeSpan. FromMinutes (2 ));
}
Now we save the data to a text file and establish the dependency between the cached weibo and the data file. Next we will return the JSON format data to the client.Copy codeThe Code is as follows: // <summary>
/// Responses the weibo data.
/// </Summary>
/// <Param name = "context"> The http contex. </param>
Private void ResponseWeibo O (HttpContext context)
{
// Gets the weibo cache data.
Byte [] buf = Encoding. UTF8.GetBytes (HttpRuntime. Cache ["weibo"]. ToString ());
// Writes the data into output stream.
Context. Response. OutputStream. Write (buf, 0, buf. Length );
Context. Response. OutputStream. Flush ();
/// Context. Response. Close ();
}
In the preceding example, we convert a JSON string to a Byte value, write it to OutputStream, and return the data to the client.Copy codeThe Code is as follows: // The function to get weibo data.
LoadWeibo: function (){
$. Ajax ({
// Weibo API.
Url: "WeiboHandler. ashx ",
Type: "GET ",
// NOTE: We get the data from same domain,
// DataType is json.
DataType: "json ",
Data :{
Source: JQWeibo O. appKey,
Count: JQWeibo O. numWeibo o
},
// When the requet completed, then invokes success function.
Success: function (data, textStatus, xhr ){
// Sets html structure.
Var html =
'<Div class = "weibo">' +
'<A href = "http://weibo.com/DOMAIN" target = "_ blank"> USER </a>' +
': WEIBO_TEXT <div class = "time"> AGO </div> ';
// Appends weibos into html page.
For (var I = 0; I <data. length; I ++ ){
$ (JQWeibo O. appendTo). append (
Html. replace ('weibo _ text', JQWeibo. ify. clean (data [I]. TEXT ))
// Uses regex and declare DOMAIN as global, if found replace all.
. Replace (/DOMAIN/g, data [I]. user. domain)
. Replace (/USER/g, data [I]. user. screen_name)
. Replace ('ago ', JQWeibo. timeAgo (data [I]. created_at ))
);
}
}
})
}
Figure 8 request results
1.1.3 Summary
Caching can greatly improve the performance of applications. Therefore, you should consider designing applications. This article mainly introduces ASP. application scenarios and differences between the output cache and data cache in. NET.
The page cache is applicable to the situations where the generated pages are usually the same or the change time is relatively fixed. For example, if the data is updated every hour, we can set the duration to 3600 s.
The pages generated by the data cache are always changing.
Http://www.codeproject.com/Articles/29899/Exploring-Caching-in-ASP-NET
Http://msdn.microsoft.com/zh-cn/library/aa478965.aspx#XSLTsection129121120120
Http://www.amazon.com/Beginning-ASP-NET-3-5-2008-Professional/dp/1590598911