.NET4.0的可扩展缓存系统
.NET Framework中,叫做System.Runtime.Caching,这不仅是个缓存库,还是个框架,可以在上面开发自己的库。ObjectCache定义了所有缓存都要实现的通用操作。与之搭配的是个内存缓存实现,叫做MemoryCache。这个缓存系统的结构如下:
上图大家可以看出来对应那些产品了吗?
下面我给大家介绍一个实现这样一个架构的代码示例,代码的核心就是ObjectCache,定义一个抽象的Provider接口:
1: public interface ICacheBuilder
2: {
3: ObjectCache GetInstance();
4: string DefaultRegionName { get; }
5: }
In-memory提供者的实现使用MemoryCache:
1: public class MemoryCacheBuilder : ICacheBuilder
2: {
3: public MemoryCacheBuilder() { }
4:
5: public ObjectCache GetInstance()
6: {
7: return MemoryCache.Default;
8: }
9:
10: public string DefaultRegionName
11: {
12: get { return null; }
13: }
14: }
分布式缓存提供者Memcached:
1: public class MemcachedCache : ObjectCache, ICacheBuilder
2: {
3: private long _lDefaultExpireTime = 3600; // default Expire Time
4: private MemcachedClient _client = null;
5: #region ICache Members
6:
7: public MemcachedCache()
8: {
9: this._client = MemcachedClientService.Instance.Client;
10: }
11:
12: public override void Set(string key, object value, System.DateTimeOffset absoluteExpiration, string regionName = null)
13: {
14: Enforce.NotNull(key, "key");
15: CacheItem item = new CacheItem(key, value, regionName);
16: CacheItemPolicy policy = new CacheItemPolicy();
17: policy.AbsoluteExpiration = absoluteExpiration;
18:
19: Set(item, policy);
20: }
21:
22: public override void Set(CacheItem item, CacheItemPolicy policy)
23: {
24: if (item == null || item.Value == null)
25: return;
26:
27: item.Key = item.Key.ToLower();
28:
29: if (policy != null && policy.ChangeMonitors != null && policy.ChangeMonitors.Count > 0)
30: throw new NotSupportedException("Change monitors are not supported");
31:
32: // max timeout in scaleout = 65535
33: TimeSpan expire = (policy.AbsoluteExpiration.Equals(null)) ?
34: policy.SlidingExpiration :
35: (policy.AbsoluteExpiration - DateTimeOffset.Now);
36:
37: double timeout = expire.TotalMinutes;
38: if (timeout > 65535)
39: timeout = 65535;
40: else if (timeout > 0 && timeout < 1)
41: timeout = 1;
42:
43: this._client.Store(Enyim.Caching.Memcached.StoreMode.Set, item.Key.ToString(), item.Value);
44:
45: }
46:
47: public override object this[string key]
48: {
49: get
50: {
51: return Get(key);
52: }
53: set
54: {
55: Set(key, value, null);
56: }
57: }
58:
59: public override object AddOrGetExisting(string key, object value, CacheItemPolicy policy, string regionName = null)
60: {
61: CacheItem item = GetCacheItem(key, regionName);
62: if (item == null)
63: {
64: Set(new CacheItem(key, value, regionName), policy);
65: return value;
66: }
67:
68: return item.Value;
69: }
70:
71: public override CacheItem AddOrGetExisting(CacheItem value, CacheItemPolicy policy)
72: {
73: CacheItem item = GetCacheItem(value.Key, value.RegionName);
74: if (item == null)
75: {
76: Set(value, policy);
77: return value;
78: }
79:
80: return item;
81: }
82:
83: public override object AddOrGetExisting(string key, object value, System.DateTimeOffset absoluteExpiration, string regionName = null)
84: {
85: CacheItem item = new CacheItem(key, value, regionName);
86: CacheItemPolicy policy = new CacheItemPolicy();
87: policy.AbsoluteExpiration = absoluteExpiration;
88:
89: return AddOrGetExisting(item, policy);
90: }
91:
92: public override bool Contains(string key, string regionName = null)
93: {
94: return false;
95: }
96:
97: public override CacheEntryChangeMonitor CreateCacheEntryChangeMonitor(System.Collections.Generic.IEnumerable<string> keys, string regionName = null)
98: {
99: throw new System.NotImplementedException();
100: }
101:
102: public override DefaultCacheCapabilities DefaultCacheCapabilities
103: {
104: get
105: {
106: return
107: DefaultCacheCapabilities.OutOfProcessProvider |
108: DefaultCacheCapabilities.AbsoluteExpirations |
109: DefaultCacheCapabilities.SlidingExpirations |
110: DefaultCacheCapabilities.CacheRegions;
111: }
112: }
113:
114: public override object Get(string key, string regionName = null)
115: {
116: key = key.ToLower();
117:
118: return this._client.Get(key);
119: }
120:
121: public override CacheItem GetCacheItem(string key, string regionName = null)
122: {
123: object value = Get(key, regionName);
124: if (value != null)
125: return new CacheItem(key, value, regionName);
126:
127: return null;
128: }
129:
130: public override long GetCount(string regionName = null)
131: {
132: return -1;
133: }
134:
135: protected override System.Collections.Generic.IEnumerator<System.Collections.Generic.KeyValuePair<string, object>> GetEnumerator()
136: {
137: throw new System.NotImplementedException();
138: }
139:
140: public override System.Collections.Generic.IDictionary<string, object> GetValues(System.Collections.Generic.IEnumerable<string> keys, string regionName = null)
141: {
142: throw new System.NotImplementedException();
143: }
144:
145: public override string Name
146: {
147: get { return "MemcachedProvider"; }
148: }
149:
150: public override object Remove(string key, string regionName = null)
151: {
152: key = key.ToLower();
153: return this._client.Remove(key);
154: }
155:
156: public override void Set(string key, object value, CacheItemPolicy policy, string regionName = null)
157: {
158: Set(new CacheItem(key, value, regionName), policy);
159: }
160:
161: #endregion
162:
163: #region ICacheBuilder Members
164:
165: public ObjectCache GetInstance()
166: {
167: return this;
168: }
169:
170: public string DefaultRegionName
171: {
172: get { throw new NotImplementedException(); }
173: }
174:
175: #endregion
176: }
分布式缓存提供者Windows Server AppFabric Caching:
1: public class AppFabricCacheProvider : ObjectCache, ICacheBuilder
2: {
3: public static DataCache factory = null;
4: public static object syncObj = new object();
5:
6: public override object AddOrGetExisting(string key, object value, CacheItemPolicy policy, string regionName = null)
7: {
8: CacheItem item = GetCacheItem(key, regionName);
9: if (item == null)
10: {
11: Set(new CacheItem(key, value, regionName), policy);
12: return value;
13: }
14:
15: return item.Value;
16: }
17:
18: public override CacheItem AddOrGetExisting(CacheItem value, CacheItemPolicy policy)
19: {
20: CacheItem item = GetCacheItem(value.Key, value.RegionName);
21: if (item == null)
22: {
23: Set(value, policy);
24: return value;
25: }
26:
27: return item;
28: }
29:
30: public override object AddOrGetExisting(string key, object value, System.DateTimeOffset absoluteExpiration, string regionName = null)
31: {
32: CacheItem item = new CacheItem(key, value, regionName);
33: CacheItemPolicy policy = new CacheItemPolicy();
34: policy.AbsoluteExpiration = absoluteExpiration;
35:
36: return AddOrGetExisting(item, policy);
37: }
38:
39: public override bool Contains(string key, string regionName = null)
40: {
41: return Get(key, regionName) != null;
42: }
43:
44: public override CacheEntryChangeMonitor CreateCacheEntryChangeMonitor(System.Collections.Generic.IEnumerable<string> keys, string regionName = null)
45: {
46: throw new NotImplementedException();
47: }
48:
49: public override DefaultCacheCapabilities DefaultCacheCapabilities
50: {
51: get
52: {
53: return
54: DefaultCacheCapabilities.OutOfProcessProvider |
55: DefaultCacheCapabilities.AbsoluteExpirations |
56: DefaultCacheCapabilities.SlidingExpirations |
57: DefaultCacheCapabilities.CacheRegions;
58: }
59: }
60:
61: public override object Get(string key, string regionName = null)
62: {
63: key = key.ToLower();
64: CreateRegionIfNeeded();
65:
66: return (regionName == null) ?
67: CacheFactory.Get(key) :
68: CacheFactory.Get(key, regionName);
69: }
70:
71: public override CacheItem GetCacheItem(string key, string regionName = null)
72: {
73: object value = Get(key, regionName);
74: if (value != null)
75: return new CacheItem(key, value, regionName);
76:
77: return null;
78: }
79:
80: public override long GetCount(string regionName = null)
81: {
82: if (string.IsNullOrEmpty(regionName))
83: throw new NotSupportedException();
84:
85: return CacheFactory.GetObjectsInRegion(regionName).LongCount();
86: }
87:
88: protected override System.Collections.Generic.IEnumerator<System.Collections.Generic.KeyValuePair<string, object>> GetEnumerator()
89: {
90: throw new NotSupportedException();
91: }
92:
93: public override System.Collections.Generic.IDictionary<string, object> GetValues(System.Collections.Generic.IEnumerable<string> keys, string regionName = null)
94: {
95: if (string.IsNullOrEmpty(regionName))
96: throw new NotSupportedException();
97:
98: return CacheFactory.GetObjectsInRegion(regionName).ToDictionary(x => x.Key, x => x.Value);
99: }
100:
101: public override string Name
102: {
103: get { return "AppFabric"; }
104: }
105:
106: public override object Remove(string key, string regionName = null)
107: {
108: key = key.ToLower();
109: CreateRegionIfNeeded();
110:
111: return (regionName == null) ?
112: CacheFactory.Remove(key) :
113: CacheFactory.Remove(key, regionName);
114: }
115:
116: public override void Set(string key, object value, CacheItemPolicy policy, string regionName = null)
117: {
118: Set(new CacheItem(key, value, regionName), policy);
119: }
120:
121: public override void Set(CacheItem item, CacheItemPolicy policy)
122: {
123: if (item == null || item.Value == null)
124: return;
125:
126: if (policy != null && policy.ChangeMonitors != null && policy.ChangeMonitors.Count > 0)
127: throw new NotSupportedException("Change monitors are not supported");
128:
129: item.Key = item.Key.ToLower();
130: CreateRegionIfNeeded();
131:
132: TimeSpan expire = (policy.AbsoluteExpiration.Equals(null)) ?
133: policy.SlidingExpiration :
134: (policy.AbsoluteExpiration - DateTimeOffset.Now);
135:
136: if (string.IsNullOrEmpty(item.RegionName))
137: CacheFactory.Put(item.Key, item.Value, expire);
138: else
139: CacheFactory.Put(item.Key, item.Value, expire, item.RegionName);
140: }
141:
142: private static DataCache CacheFactory
143: {
144: get
145: {
146: if (factory == null)
147: {
148: lock (syncObj)
149: {
150: if (factory == null)
151: {
152: DataCacheFactory cacheFactory = new DataCacheFactory();
153: factory = cacheFactory.GetDefaultCache();
154: }
155: }
156: }
157:
158: return factory;
159: }
160: }
161:
162: private void CreateRegionIfNeeded()
163: {
164: try
165: {
166: CacheFactory.CreateRegion(DefaultRegionName);
167: }
168: catch (DataCacheException ex)
169: {
170: if (!ex.ErrorCode.Equals(DataCacheErrorCode.RegionAlreadyExists))
171: throw ex;
172: }
173: }
174:
175: public override void Set(string key, object value, System.DateTimeOffset absoluteExpiration, string regionName = null)
176: {
177: CacheItem item = new CacheItem(key, value, regionName);
178: CacheItemPolicy policy = new CacheItemPolicy();
179: policy.AbsoluteExpiration = absoluteExpiration;
180:
181: Set(item, policy);
182: }
183:
184: public override object this[string key]
185: {
186: get
187: {
188: return Get(key, DefaultRegionName);
189: }
190: set
191: {
192: Set(key, value, null, DefaultRegionName);
193: }
194: }
195:
196: public ObjectCache GetInstance()
197: {
198: return this;
199: }
200:
201: public string DefaultRegionName
202: {
203: get
204: {
205: string defaultRegion= FrameworkConfiguationManager.GetConfiguration().GetAppVariable("AppFabricCacheDefaultRegion");
206: if (string.IsNullOrEmpty(defaultRegion))
207: {
208: defaultRegion = "Default";
209: }
210: return defaultRegion;
211: }
212: }
213: }
输出缓存对于改善性能有很大好处,在ASP.NET 4.0中可以自定义输出缓存的策略,比如把输出保存在磁盘中,外部的memcached服务中等等。甚至还可以定义一些高级规则,比如为A页面使用A输出缓存策略来把数据保存于内存中,为B页面使用B输出缓存策略来把数据保存于磁盘中。 代码例子可以参看文章http://www.buraksenyurt.com/post/AspNet-40-Custom-Cache-Provider.aspx,在web.config中配置:
1: <caching>
2: <outputCache defaultProvider="AspNetInternalProvider">
3: <providers>
4: <add name="DiskBasedCacheProvider" type="CustomCaching.DiskCacheProvider,CustomCaching"/>
5: </providers>
6: </outputCache>
7: </caching>
在ASP.NET 4 的默认输出缓存策略中。所有的HTTP响应、所呈现的页面和控件缓存均使用上例所示的默认输出缓存提供程序(其中defaultProvider属性值为AspNetInternalProvider)。通过为defaultProvider指定不同的提供程序。就可以更改web应用程序的默认输出缓存提供程序。
另外,还可以针对每个用户控件和各个请求选择不同的输出缓存提供程序。要为不同的Web用户控件选择不同的输出缓存提供程序,最简便的方法是设置页面或控件指令中新增加的providerName属性,如下面的示例所示:
<%@ OutputCache Duration="60" VaryByParam="None" providerName="DiskBasedCacheProvider" %>
若要为某个HTTP请求指定不同的输出缓存提供程序,可以覆盖Global.asax文件中新增加的GetOutputCacheProviderName方法,以编程的方式指定要用于特定请求的提供程序。
参看文章:.NET 4.0新增可扩展缓存