Today's article I'll talk about caching in the MVC 3 project, and some design considerations for the cache, for reference
Objective
Why do I need to discuss caching? Caching is a problem that must be considered in a medium-sized system. In order to avoid each request to access the background resources (such as the database), we generally consider some of the updates are not very frequent, can be reused data, in a certain way to temporarily save, the subsequent requests can be directly accessed by the situation of these saved data. This mechanism is called a caching mechanism.
Depending on the location of the cache, the zone can be divided into:
- Client-side caching (cached in the user's client, for example in a browser)
- Server cache (cached in the server, can be in memory, can be slow to exist in the file, and can be further divided into local cache and distributed cache two)
It should be said that the design of the cache is a more complex knowledge, the main issues to consider include
- Do you want to cache?
- What data do you want to cache?
- How much data do you want to cache?
- How long do you want to cache?
- How to update the cache (manual or automatic)
- Where do you put the cache?
In a more understandable way, this article will look at how caching is used in MVC3 projects. For some of the specific business issues mentioned above, I will not go into too much in-depth discussion here.
Caching features in the MVC3
The ASP. NET MVC3 inherits the good tradition of ASP. The built-in cache feature is supported. The main performance is the following aspects
Output caching can be defined directly on Controller,action or childaction (this is equivalent to the original page cache and the control cache function)
Support for flexible definition of cache settings (new features) via CacheProfile
Supports cache dependencies so that they are notified when external resources change, and the cache is updated
Support for caching APIs and some third-party caching scenarios (such as distributed caching)
So, let's get to the bottom of this.
0. Sample Preparation
I prepared a blank MVC 3 project, which created a model type: Employee
Using System; using System.Collections.Generic; using System.Linq; using System.Web; namespace mvcapplicationcachesample.models{ string Gender {get; set; }}}
Then I prepared a homecontroller
Using System; using System.Collections.Generic; using System.Linq; using System.Web; using SYSTEM.WEB.MVC; using Mvcapplicationcachesample.models; New Employee () {id=1,name="Ares", gender=return View (employees); }}
At the same time, a view is generated for this action
@model ienumerable<mvcapplicationcachesample.models.employee>@{Viewbag.title ="Index";} "Create New", "Create") </p><table> <tr> <th> Name </th> <th> Gender </th> <th></th> </tr>@foreach (Var item in Model) {<tr> <td> @Html. displayfor (ModelItem = Item. Name) </td> <td> @Html. displayfor (ModelItem = Item. Gender) </td> <td> @Html. ActionLink ( "edit", "edit ", new {id=item.id}) | @Html. ActionLink ( "details", "details", new {id=item.id}) | Html.ActionLink ( "delete", "delete", new {id=item.id}) </ Td> </tr>}</table>
So, the current application runs up to see the effect that's roughly the following
This example is very simple and there is not much to explain.
1. Using Output caching
So, now that we're assuming that the data is very frequent, but the data is not being updated very often, we think we can cache this part of the data in order to reduce the time of each execution.
Yes, we can do that, and it's easy to do that. MVC has a built-in outputcache actionfilter that we can apply to an action or childaction.
Note Childaction is a new concept for MVC3, essentially an action, but usually returns a Partialview. Typically, this action can be combined with a childactiononly actionfilter to identify that it can only be requested as a child and not directly through the address.
"Remarks" We can definitely define output caching at the controller level, but I don't recommend it. The cache is to be considered, not all cached, regardless of 3,721. Improper caching can cause problems that are larger than without caching.
The following code enables the cache function of the index action, and we let him cache for 10 seconds.
Using System; using System.Collections.Generic; using System.Linq; using System.Web; using SYSTEM.WEB.MVC; using Mvcapplicationcachesample.models; [OutputCache (duration=10)]new Employee () {id=1,name="Ares", gender=return View ( Employees); } }}
So, that is, the first time the index is requested, the code inside will be executed, and the result will be cached, and then within 10 seconds, the second or subsequent requests, it is not necessary to execute again, but directly to the user to return the results.
This outputcache attribute, is actually a actionfilter, it has a lot of parameters, specific please refer to
Http://msdn.microsoft.com/zh-cn/library/system.web.mvc.outputcacheattribute.aspx
Of these parameters, duration is a must, which is set an expiration time, in seconds, which I think we all understand well. I'd like to focus on the next few
- Varybycontentencoding
VaryByCustom
VaryByHeader
VaryByParam
These four parameters mean determining how different requests are differentiated in the cache, that is, which factors will determine whether or not to use the cache. By default, if you do not make any settings, all users, regardless of how they are accessed, are directly reading the cache within the specified time period (which we call the cache).
VaryByParam, you can decide whether to read the cache based on the parameters requested by the user. This parameter mainly refers to the querystring. For example
If we cache the Http://localhost/Home/Index, then it is accessed by this address, and the time is read in the cache. But if we visit with an address like Http://localhost/Home/Index?name=chenxizhang, obviously we want to not read the cache because the parameters are different. To implement such a requirement, that is, you want to cache different data depending on the name parameter. You can set the varybyparam= "name".
If you have more than one parameter, you can separate them with commas. For example varybyparam= "Name,id"
"Note" There is actually a potential risk, due to different parameters (and their combination) need to cache different versions of the data, if there is a malicious program, with different parameters to initiate a large number of requests, it will cause the cache explosion, in extreme cases, will cause problems on the server. (Of course, in IIS, if you find that the content of the cache is not enough, it will automatically clean up some of the data, but this also leads to the instability of the program, because some of the normal needs of the cache may be destroyed). That's why I'm stressing that caching design is a more complicated thing.
VaryByHeader, you can decide whether to read the cache depending on the header information provided in the user request. We can see that the header information is included in each request, as shown in
This is also useful, for example, according to different languages, we obviously have different versions. or different versions can be cached depending on the user's browser. You can set this by
Varybyheader= "Accept-language,user-agent"
The above two are more commonly used. Of course, there are two other properties that can also be set
Varybycontentencoding, generally set to accept-encoding inside the possible encoding name, it can be seen that the request contains this header.
VaryByCustom is a fully customizable setting, for example we may need to determine different cached versions based on user roles, or to differentiate between cached versions based on some minor version numbers of the browser, we can set this: varybycustom= "role, Browserversion ", these names are defined by you, and it is of course useless to write this, and we also need to add a special method in the Global.asax file to deal with this particular requirement.
Using System;Using System.Collections.Generic;Using System.Linq;Using System.Web;Using SYSTEM.WEB.MVC;Using System.Web.Routing;using System.Web.Security;Namespace mvcapplicationcachesample{Note:for instructions on enabling IIS6 or IIS7 Classic mode,Visit http://go.microsoft.com/?LinkId=9394801PublicClass MvcApplication:System.Web.HttpApplication {PublicStaticvoid Registerglobalfilters (globalfiltercollection filters) {filters. ADD (New Handleerrorattribute ()); }PublicStaticvoid RegisterRoutes (RouteCollection routes) {routes. Ignoreroute ("{resource}.axd/{*pathinfo}"); Routes. MapRoute ("Default",Route Name"{Controller}/{action}/{id}",URL with parametersNew {controller ="Home", action ="Index", id = urlparameter.optional}Parameter defaults); }protected void Application_Start () {arearegistration.registerallareas (); Registerglobalfilters (globalfilters.filters); RegisterRoutes (routetable.routes); public override String GetVaryByCustomString (HttpContext context, string Custom) {switch ( Custom) {case "Role": {return string. Join (case "Browserversion": {return context. Request.Browser.Type; } default: break;} return string. Empty; } }}
The above four properties can change the behavior of cache usage. There is also an important attribute that will affect where the cache is saved, which is the Location property, which has several options that I can extract from the document.
Member name |
Description |
Any |
The output cache can be located on the browser client that generated the request, the proxy server that participates in the request (or any other server), or the server that is processing the request. (This is the default value) |
Client |
The output cache is located on the browser client that generated the request. |
Downstream |
The output cache can be stored in any HTTP 1.1 cacheable device, except the source server. This includes the proxy server and the client that made the request. |
Server |
The output cache is located on the WEB server that is processing the request. |
None |
For the requested page, disable output caching. |
ServerAndClient |
The output cache can only be stored on the source server or the client that made the request. The proxy server cannot cache the response. |
Here's a question to consider, set to client and what's the behavior of setting the server to be different
If set to client, then the first request, the resulting response header, will record that the page should be cached, and expire after 10 seconds. As shown
If it is set to server, you will see that the client is not cached.
It looks good, doesn't it? If you agree without hesitation, I will tell you that you are wrong. So, don't worry about it, please try again set as client, you will find that if you refresh the page, then the request will still be issued, and result is also returned 200, which means that this is a new request, and indeed returned the result. This is obviously not the same as we expected.
In order to do the test, I deliberately added a time output, if only set to the client, each time the refresh is not the same. This means that the server-side code is executed.
The same problem is now, if we set the location as ServerAndClient, you will find that the client's cache does not seem to be in effect, each time is still a request server, but in this case, the server has been cached, so in the specified time, The server code is not executed, so the results will not change. But the problem is that since the client cache is set up, it is reasonable to use the cached version of the client directly and not to request the server.
This problem, in fact, is a problem of the ASP. Here is an article about http://blog.miniasp.com/post/2010/03/30/ Outputcachelocation-serverandclient-problem-fixed.aspx
We can look at it, set the location to ServerAndClient, and modify the code a little bit.
Using System;Using System.Collections.Generic;Using System.Linq;Using System.Web;using SYSTEM.WEB.MVC; using Mvcapplicationcachesample.models; using System.Web.UI; namespace mvcapplicationcachesample.controllers{ public class Homecontroller:controller { / /// GET:/home/[OutputCache (duration=10,location=outputcachelocation.serverandclient)] public ActionResult Index () { Response.Cache.SetOmitVaryStar (true); Viewbag.currenttime = DateTime.Now.ToString (); //Here currently as a demo, is the direct hard-coded, which may actually read the data of the database var employees = new[]{ new Employee () {id=1,name="Ares", gender= "Male"}; return View (employees);} }}
We see that, starting with the second request, the status code is 304, which means that the page is cached, so the browser does not need to request the server's data. And you can see that the received byte is 221B instead of the original 1.25KB.
However, if you just set it to client, you still won't be able to actually implement the clients cache (this behavior is a bit odd). This problem I did not find a way, if we do need to use client-side caching, simply let us set to serverandclient it.
Using client-side caching can significantly reduce the number of requests made to the server, which is more desirable in some sense.
2. Using the cache configuration file
In the first section, we have a detailed understanding of how to set up caching by outputcache this actionfilter in MVC. However, because these settings are defined directly above the action through C # code, it is not very flexible, for example, we may need to adjust these settings frequently, how to do?
ASP. NET 4.0 provides a new mechanism, that is, the function of CacheProfile, we can define the so-called profile in the configuration file, and then in OutputCache this attribute can be used directly.
The benefits of this mechanism can be easily seen through the following example. The following nodes are defined in the System.Web
<Caching> <outputcachesettings> Span class= "KWRD" ><outputcacheprofiles> <add name= "employee" duration = "ten" enabled= "true" location= "ServerAndClient" varybyparam= "None" / > </outputcacheprofiles> </outputcachesettings> </ Caching>
The profile can then be used directly in the code.
Using System;Using System.Collections.Generic;Using System.Linq;using system.web; using SYSTEM.WEB.MVC; using mvcapplicationcachesample.models; using System.Web.UI; namespace mvcapplicationcachesample.controllers{public Class Homecontroller:controller {////GET:/home/[OutputCache (cacheprofile=< span class= "str" > "employee")] public actionresult Index () {// Response.Cache.SetOmitVaryStar (TRUE); Viewbag.currenttime = DateTime.Now.ToString (); //here currently as a demonstration, is directly hard-coded, which may actually be reading data from the database var employees = new[]{new Employee () {id=1,name= "Ares", Gender= "Male"}}; return View (employees);} }}
This example is straightforward and with profile, we can easily configure some of the key values of the cache at runtime.
3. Using the Caching API
With the two steps above, we learned that using OutputCache, combined with CacheProfile, is a great way to achieve a flexible cache configuration. But there are times when we might want to control the cache more finely. Because OutputCache is a cache of action, there is no sharing of data between different actions, and if some data is shared between different actions, then simply using OutputCache to do so will result in the same data The problem of caching multiple times.
So, ASP. NET, in addition to providing OutputCache's declaration-based output cache settings, allows us to control what data is cached in code and provides more options.
For more information on how to add or use the cache via the API, refer to
Http://msdn.microsoft.com/zh-cn/library/18c1wd61%28v=VS.80%29.aspx
Basically, using the Httpcontext.cache type, you can do all the work and be flexible enough.
It is worth mentioning that I know that a lot of companies in the project will adopt some ORM framework, some ORM framework also allows the implementation of the cache. For example, NHibernate provides a richer cache function, can refer to the http://www.cnblogs.com/RicCC/archive/2009/12/28/nhibernate-cache-internals.html
It should be noted that the Entity Framework provided by Microsoft itself does not contain the functionality of the cache.
It is still a special reminder that using this API-based caching scheme requires careful scrutiny of the reasonableness of the settings for each layer of caching, as well as issues such as updates.
4. Using cache dependencies
Early on, when designing the cache in ASP, we were able to use caching-dependent technologies. For more information on cache dependencies, please refer to the http://msdn.microsoft.com/zh-cn/library/ms178604.aspx
Actually, this technique is really useful, ASP. NET provides a sqlcachedependency by default, which can be configured to connect to the SQL Server database, and when the table of the database changes, it notifies ASP.
It is worth mentioning that the cache dependency can be used regardless of whether it is a declarative caching method using OutputCache or a caching API. and using the caching API, you can use the standard CacheDependency object in addition to using SqlCacheDependency to implement a dependency on the file.
Http://msdn.microsoft.com/zh-cn/library/system.web.caching.cachedependency%28v=VS.80%29.aspx
5. Distributed Cache
The above mentioned means are very good, if the application system is not very large, but also enough. It is important to note that the caching methods mentioned above are cached in the local memory of the Web server, and the problem is that it is not possible to share these caches among multiple servers if we need to do load balancing (typically there will be more than one server). Because of this, the concept of distributed cache comes into being.
When it comes to distributed caching, an open source framework that is currently being recognized is memcached. As the name implies, it still uses the memory cache, but it is inherently distributed based, its access is directly through TCP, so you can access the remote server, or multiple Web servers can access the same cache server.
About memcached and it's in. NET use, before a friend has written an introduction, you can refer to the use of
Http://www.cnblogs.com/zjneter/archive/2007/07/19/822780.html
It is important to note that distributed caching is not intended to improve performance (which can be a myth), and it is certain that its speed will be slower locally. If your app has only one server to meet your requirements, you won't need to use memcached. The best thing about it is to cross-server and share the cache across applications.
Category: ASP. NET MVC
Discussion on the design of cache function in MVC Project