互聯網的項目用戶基數很大,有時候瞬間併發量很是大,這個時候對於數據訪問來講是個災難。爲了應對這種場景,通常都會大量採用web服務器集羣,緩存集羣。採用集羣后基本上就能解決大量併發的數據訪問。固然這個時候內網的網速會成爲緩存速度的瓶頸。web
固然咱們但願能有更好的緩存結構,好比一級緩存和二級緩存。一級緩存直接緩存在宿主主機內存上,二級緩存緩存在redis集羣上,若是一個緩存實例被訪問的頻率很是高,咱們但願這個緩存實例能緩存在宿主主機內存上,若是一個實例的訪問頻率很是低,咱們甚至可能不會爲此實例進行緩存處理。redis
基於這種設想,咱們但願可以跟蹤監視緩存實例,並根據監視結果,對實例的緩存級別進行動態調整,以達到最佳的緩存效果。(事實上dotNet4.0裏面的System.Runtime.Caching.MemoryCache對此已經有很好的實現和支持了。固然咱們的應用必須知道要緩存在宿主主機內存上,仍是redis集羣上,那就必須實現相似System.Runtime.Caching.MemoryCache的監視功能和動態調整功能)緩存
首先咱們須要附加一些監視信息到緩存實例上,服務器
public class CacheAttach { public CacheAttach(string key) { this.Key = key; this.InsertedTime = DateTime.Now; } public string Key { get; set; } public DateTime InsertedTime { get; private set; } public int QueryTimes { get; set; } public int AccessTimes { get; set; } public override bool Equals(object obj) { if (obj == null) return false; return obj.GetHashCode() == this.GetHashCode(); } public override int GetHashCode() { return Key.GetHashCode(); } public static implicit operator CacheAttach(string value) { return new CacheAttach(value); } } public class CacheAttachCollection : List<CacheAttach>, ICollection<CacheAttach> { public bool Contains(string Key) { return this.Find(i => i.Key == Key) == null; } public CacheAttach this[string key] { get { CacheAttach item =this.Find(i => i.Key == key); if (item == null) { item = new CacheAttach(key); this.Add(item); } return item; } set { CacheAttach item = this.Find(i => i.Key == key); if (item == null) { item = new CacheAttach(key); this.Add(item); } item = value; } } }
這裏採用的是一種附加形式的監視,不去破壞原來的K/V緩存方式。這個時候咱們可能須要從新包裝一下原有的緩存訪問,使得對緩存的操做能被監視。併發
public class MonitorCache: ICache { private ICache proxyCache; CacheAttachCollection cacheMonitor = new CacheAttachCollection(); public MonitorCache(ICache cache) { this.proxyCache = cache; } #region ICache Implement public bool Set<T>(string key, T value) { cacheMonitor[key].QueryTimes++; cacheMonitor[key].AccessTimes++; return proxyCache.Set(key, value); } public bool Set<T>(string key, T value, DateTime absoluteTime, TimeSpan slidingTime, Action<string, T> removingHandler) { cacheMonitor[key].QueryTimes++; cacheMonitor[key].AccessTimes++; return this.proxyCache.Set(key, value, absoluteTime, slidingTime, removingHandler); } public object Get(string key) { cacheMonitor[key].QueryTimes++; cacheMonitor[key].AccessTimes++; return this.proxyCache.Get(key); } public T Get<T>(string key) { cacheMonitor[key].QueryTimes++; cacheMonitor[key].AccessTimes++; return this.proxyCache.Get<T>(key); } public bool Contains(string key) { cacheMonitor[key].QueryTimes++; return this.proxyCache.Contains(key); } public bool Remove(string key) { if (this.proxyCache.Remove(key)) { cacheMonitor.Remove(key); return true; } return false; } #endregion public object this[string key] { get { return this.Get(key); } set { this.Set(key, value); } } public CacheAttachCollection Monitor { get { return this.cacheMonitor; } } }
經過對原有的緩存訪問進行包裝,咱們已經實現對原有緩存的重構,實現監視的意圖。ide
public class CacheHelper : ICache { private MonitorCache level1 = null; private MonitorCache level2 = null; private CacheHelper() { this.level1 = new MonitorCache(new MemoryCache()); this.level2 = new MonitorCache(new RedisCache()); } public bool Set<T>(string key, T value) { if (this.level1.Set(key, value)) return true; if (this.level2.Set(key, value)) return true; return false; } public bool Set<T>(string key, T value, DateTime absoluteTime, TimeSpan slidingTime, Action<string, T> removingHandler) { if (this.level1.Set(key, value, absoluteTime, slidingTime, removingHandler)) return true; if (this.level2.Set(key, value, absoluteTime, slidingTime, removingHandler)) return true; return false; } public object Get(string key) { return this.level1.Get(key) ?? this.level2.Get(key) ?? null; } public T Get<T>(string key) { if (this.level1.Contains(key)) return this.level1.Get<T>(key); if (this.level2.Contains(key)) return this.level2.Get<T>(key); return default(T); } public T Get<T>(string key, Func<T> valueGetter) { var result = default(T); if (this.level1.Contains(key)) result = this.level1.Get<T>(key); else if (this.level2.Contains(key)) result = this.level2.Get<T>(key); if (result == null && valueGetter != null) result = valueGetter(); return result; } public bool Contains(string key) { if (this.level1.Contains(key)) return true; if (this.level2.Contains(key)) return true; return false; } public bool Remove(string key) { if (this.level1.Contains(key)) this.level1.Remove(key); if (this.level2.Contains(key)) this.level2.Remove(key); return true; } public object this[string key] { get { return this.Get(key); } set { this.Set(key, value); } } public void Trim() { //對一級緩存進行整理 for (int i = 0, lengh = this.level1.KeyMonitor.Count; i < lengh; i++) { CacheAttach item = this.level1.KeyMonitor[i]; //頻率小於10次/秒的緩存須要移除一級緩存 if (item.AccessRate < 10) { //頻率大於1次/秒的緩存移到二級緩存 if (item.AccessRate >= 1) { this.level2.Set(item.Key, this.level1[item.Key]); this.level2.KeyMonitor[item.Key] = item; } this.level1.Remove(item.Key); } } //對二級緩存進行整理 for (int i = 0, lengh = this.level2.KeyMonitor.Count; i < lengh; i++) { CacheAttach item = this.level1.KeyMonitor[i]; //頻率大於等於10次/秒的緩存須要移至一級緩存 if (item.AccessRate >= 10) { this.level1.Set(item.Key, this.level2[item.Key]); this.level1.KeyMonitor[item.Key] = item; this.level1.Remove(item.Key); continue; } if (item.AccessRate < 1) { this.level2.Remove(item.Key); continue; } } } private static CacheHelper _Current = new CacheHelper(); public static CacheHelper Current { get { return _Current; } } public static CacheHelper() { System.Threading.Timer timer = new System.Threading.Timer(delegate { Current.Trim(); }); } }