网站优化之页面级缓存方案
用缓存来优化网站性能的方法,估计是无人不知的。 ASP.NET提供了HttpRuntime.Cache对象来缓存数据,也提供了OutputCache指令来缓存整个页面输出。 虽然OutputCache指令使用起来更方便,也有非常好的效果, 不过,它需要我们在那些页面中添加这样一个指令。
对于设置过OutputCache的页面来说,浏览器在收到这类页面的响应后,会将页面响应内容缓存起来。 只要在指定的缓存时间之内,且用户没有强制刷新的操作,那么就根本不会再次请求服务端, 而对于来自其它的浏览器发起的请求,如果缓存页已生成,那么就可以直接从缓存中响应请求,加快响应速度。 因此,OutputCache指令对于性能优化来说,是很有意义的(除非所有页面页面都在频繁更新)。
在网站的优化阶段,我们可以用Fiddler之类的工具找出一些内容几乎不会改变的页面,给它们设置OutputCache, 但是,按照传统的开发流程,我们需要针对每个页面文件执行以下操作:
方式一、使用ASP.NET平台提供的OutputCache(运行时内存)机制
实现方式:
- 为列表页配置输出缓存声明;
- 根据页面不同的查询参数缓存不同的页面
- E.g.同样是列表页,分类1页面:http://www.wedn.net/cat/1/和 分类二页面:http://www.wedn.net/cat/2/分别缓存成不同的缓存对象
优点:
- 配置简单、
- 不侵入原本系统、
- 访问速度非常快(因为是将执行结果缓存到内存)
缺点:
- 占用资源比较多,无法缓存所有的页面。
- 另外有个问题:没有解决使用时间长了以后访问缓存速度变慢;
方式二、同样使用ASP.NET平台提供的OutputCache(自定义Provider)机制
实现方式:
- 开发自定义Provider程序提供给ASP.NET调用去存取输出缓存;
- 其余同方式一
优点:
- 可以自主控制缓存读写、
- 不侵入原有系统、
- 访问快
缺点:
- 配置相对复杂、
- 需要开发Provider
- 需要额外的资源(分布式缓存)
附:
自定义OutputCacheProvider实现将输出缓存放到分布式缓存中
实现原理:
实现方式:
- 实现抽象基类OutputCacheProvider的增删方法;
- 附上大概的代码(简单的通过文件读写、JSON序列化和二进制序列化实现),思路仅供参考:
OutputCacheProvider
1 using System; 2 using System.Collections.Generic; 3 using System.Diagnostics; 4 using System.IO; 5 using System.Linq; 6 using System.Runtime.Serialization.Formatters.Binary; 7 using System.Security.Cryptography; 8 using System.Text; 9 using System.Web.Caching; 10 using Micua.Utility; 11 namespace Micua.UI.Cache 12 { 13 public class MicuaOutputCacheProvider : OutputCacheProvider, IDisposable 14 { 15 readonly IList<CacheItem> _cacheItems; 16 public MicuaOutputCacheProvider() 17 { 18 if (File.Exists("z:\\cache.json")) 19 { 20 var json = File.ReadAllText("z:\\cache.json"); 21 _cacheItems = JsonHelper.Deserialize<List<CacheItem>>(json) ?? new List<CacheItem>(); 22 } 23 else 24 { 25 _cacheItems = new List<CacheItem>(); 26 } 27 } 28 public override object Get(string key) 29 { 30 Debug.WriteLine(string.Format("Cache.Get({0})", key)); 31 key = MD5(key); 32 var cacheItem = _cacheItems.FirstOrDefault(c => c.Id == key); 33 if (cacheItem != null) 34 { 35 if (cacheItem.Expiration.ToUniversalTime() <= DateTime.UtcNow) 36 { 37 _cacheItems.Remove(cacheItem); 38 } 39 else 40 { 41 return Deserialize(cacheItem.Item); 42 } 43 } 44 return null; 45 } 46 public override object Add(string key, object entry, DateTime utcExpiry) 47 { 48 Debug.WriteLine("Cache.Add({0}, {1}, {2})", key, entry, utcExpiry); 49 key = MD5(key); 50 if (utcExpiry == DateTime.MaxValue) 51 utcExpiry = DateTime.UtcNow.AddMinutes(5); 52 var item = _cacheItems.FirstOrDefault(c => c.Id == key); 53 if (item != null) 54 { 55 if (item.Expiration.ToUniversalTime() <= DateTime.UtcNow) 56 { 57 _cacheItems.Remove(item); 58 } 59 else 60 { 61 SaveChange(); 62 return Deserialize(item.Item); 63 } 64 } 65 _cacheItems.Add(new CacheItem 66 { 67 Id = key, 68 Item = Serialize(entry), 69 Expiration = utcExpiry 70 }); 71 SaveChange(); 72 return entry; 73 } 74 public override void Set(string key, object entry, DateTime utcExpiry) 75 { 76 Debug.WriteLine("Cache.Set({0}, {1}, {2})", key, entry, utcExpiry); 77 key = MD5(key); 78 var item = _cacheItems.FirstOrDefault(c => c.Id == key); 79 if (item != null) 80 { 81 _cacheItems.Remove(item); 82 item.Item = Serialize(entry); 83 item.Expiration = utcExpiry; 84 _cacheItems.Add(item); 85 } 86 else 87 { 88 _cacheItems.Add(new CacheItem 89 { 90 Id = key, 91 Item = Serialize(entry), 92 Expiration = utcExpiry 93 }); 94 } 95 SaveChange(); 96 } 97 public override void Remove(string key) 98 { 99 Debug.WriteLine("Cache.Remove({0})", key); 100 key = MD5(key); 101 _cacheItems.Remove(_cacheItems.FirstOrDefault(c => c.Id == key)); 102 SaveChange(); 103 } 104 private static string MD5(string value) 105 { 106 var cryptoServiceProvider = new MD5CryptoServiceProvider(); 107 var bytes = Encoding.UTF8.GetBytes(value); 108 var builder = new StringBuilder(); 109 bytes = cryptoServiceProvider.ComputeHash(bytes); 110 foreach (var b in bytes) 111 builder.Append(b.ToString("x2").ToLower()); 112 return builder.ToString(); 113 } 114 private static byte[] Serialize(object entry) 115 { 116 var formatter = new BinaryFormatter(); 117 var stream = new MemoryStream(); 118 formatter.Serialize(stream, entry); 119 return stream.ToArray(); 120 } 121 private static object Deserialize(byte[] serializedEntry) 122 { 123 var formatter = new BinaryFormatter(); 124 var stream = new MemoryStream(serializedEntry); 125 return formatter.Deserialize(stream); 126 } 127 private void SaveChange() 128 { 129 var json = JsonHelper.Serialize(_cacheItems); 130 File.WriteAllText("z:\\cache.json", json); 131 } 132 public void Dispose() 133 { 134 var json = JsonHelper.Serialize(_cacheItems); 135 File.WriteAllText("z:\\cache.json", json); 136 } 137 } 138 }
Follow me: https://github.com/zce