ASP.NET output caching (i.e. static HTML) was memory-based until .NET 4.0. This means that if our site contains a lot of cache, it is easy to consume local memory. Now, with the help of . OutputCacheProvider in .NET 4.0, we have several options to create our own cache. For example, we can store the HTML output cache in a memcached distributed cluster server or MongoDB (a commonly used document-oriented database, read this http://msdn.microsoft.com/zh-cn/magazine/gg650661.aspx). Of course, we can also store the cache as a file on the hard drive, which is the cheapest way to do it considering scalability, and this article is about how to build a custom file cache.
1:OutputCacheProvider OutputCacheProvider is an abstract base class that we need to override four of its methods, which are: Add method to insert the specified item into the output cache. Get method, which returns a reference to the specified item in the output cache. Remove method to remove the specified item from the output cache. Set method, inserts the specified item into the output cache, and overwrites the item if it is cached.
2: Create your own file caching handling class The type is FileCacheProvider, and the code is as follows:
- public class FileCacheProvider : OutputCacheProvider
- {
- private static readonly ILog log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
-
- public override void Initialize(string name, NameValueCollection attributes)
- {
- base.Initialize(name, attributes);
- CachePath = HttpContext.Current.Server.MapPath(attributes["cachePath"]);
- }
-
- public override object Add(string key, object entry, DateTime utcExpiry)
- {
- Object obj = Get(key);
- if (obj != null) //这一步很重要
- {
- return obj;
- }
- Set(key,entry,utcExpiry);
- return entry;
- }
-
- public override object Get(string key)
- {
- string path = ConvertKeyToPath(key);
- if (!File.Exists(path))
- {
- return null;
- }
- CacheItem item = null;
- using (FileStream file = File.OpenRead(path))
- {
- var formatter = new BinaryFormatter();
- item = (CacheItem)formatter.Deserialize(file);
- }
-
- if (item.ExpiryDate <= DateTime.Now.ToUniversalTime())
- {
- log.Info(item.ExpiryDate + "*" + key);
- Remove(key);
- return null;
- }
- return item.Item;
- }
-
-
- public override void Set(string key, object entry, DateTime utcExpiry)
- {
- CacheItem item = new CacheItem(entry, utcExpiry);
- string path = ConvertKeyToPath(key);
- using (FileStream file = File.OpenWrite(path))
- {
- BinaryFormatter formatter = new BinaryFormatter();
- formatter.Serialize(file, item);
- }
- }
-
- public override void Remove(string key)
- {
- string path = ConvertKeyToPath(key);
- if (File.Exists(path))
- File.Delete(path);
- }
-
- public string CachePath
- {
- get;
- set;
- }
-
- private string ConvertKeyToPath(string key)
- {
- string file = key.Replace('/', '-');
- file += ".txt";
- return Path.Combine(CachePath, file);
- }
- }
-
- [Serializable]
- public class CacheItem
- {
- public DateTime ExpiryDate;
- public object Item;
-
- public CacheItem(object entry, DateTime utcExpiry)
- {
- Item = entry;
- ExpiryDate = utcExpiry;
- }
- }
Copy code There are two places that need special attention: In the Add method, there is a conditional judgment that must be handled in this way, otherwise the caching mechanism will cache the first result, and the cache will expire after the expiration date and will not be rebuilt. In the example program, we simply put the cache in the cache directory, and in actual project practice, considering that the cached pages will be thousands, we must do directory classification, otherwise finding and reading cache files will become an efficiency bottleneck, which will drain the CPU.
3: Configuration file
We need to configure in Web.config that the cache handler is a custom FileCacheProvider, i.e. add a node under FileCacheProvider:- <caching>
- <outputCache defaultProvider="FileCache">
- <providers>
- <add name="FileCache" type="MvcApplication2.Common.FileCacheProvider" cachePath="~/Cache" />
- </providers>
- </outputCache>
- </caching>
Copy code
4: Use of cache
We assume that using it in MVC's control (if you want to use it in a ASP.NET page, include <% in the page@OutputCache VaryByParam="none" Duration="10" %>), and you can see that Index is not output cached, while Index2 is output cached for 10 seconds.
- public class HomeController : Controller
- {
- private static readonly ILog log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
- static string s_conn = "Data Source=192.168.0.77;Initial Catalog=luminjidb;User Id=sa;Password=sa;";
- public ActionResult Index()
- {
- using (DataSet ds = Common.SqlHelper.ExecuteDataset(s_conn, CommandType.Text, "select top 1* from NameTb a, DepTb b where a.DepID = b.ID ORDER BY NEWID()"))
- {
- ViewBag.Message = ds.Tables[0].Rows[0]["name"].ToString();
- }
- return View();
- }
-
- [OutputCache(Duration = 10, VaryByParam = "none")]
- public ActionResult Index2()
- {
- using (DataSet ds = Common.SqlHelper.ExecuteDataset(s_conn, CommandType.Text, "select top 1* from NameTb a, DepTb b where a.DepID = b.ID ORDER BY NEWID()"))
- {
- ViewBag.Message = ds.Tables[0].Rows[0]["name"].ToString();
- }
- return View();
- }
- }
Copy code 5: Check the effect The above code, after accessing Index2, will generate a cache file in the Cache folder, as follows:
Now, let's evaluate the performance comparison between the output cache and the output cache, simulating 100 concurrent requests from 100 users as follows:
|