一個簡單的MemoryCache的實現

工作中需要用到內存緩存,最開始打算用個的是.net自帶的MemoryCache這麼個東西,用的時候發現在服務端有時會莫名其妙的丟失緩存並且丟失後就緩存不上了。本來網上關於使用MemoryCache的不多,一直也沒有找到原因和解決辦法,所以就自己仿着它寫了一個簡單的實現。


首先看下目錄結構


其中:

ChangeMoniter:是緩存對象的過期策略的檢測器,包括文件改變監視器(FileChangeMoniter)和時間改變監視器(TimeChangeMoniter)

IMoniter:監視器接口,用來實現自定義擴展的監視器

IRemoveCache:定義了調用了MemoryCache回調函數刪除緩存對象的接口。

MemoryCache:內存緩存類。

MemoryCacheEntry:緩存對象類。

MemoryCachePolicy:緩存過期策略類。

MemoryCacheManager:緩存管理器類。


FileChangeMoniter

主要是用來實時監控文件的改變,一但文件改變,會在判斷緩存對象是否存在時主動刪除緩存對象。
代碼如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;

using Cache.MemoryCache.Interface;

namespace Cache.MemoryCache.Moniter
{
    public class FileChangeMoniter:IMoniter
    {
        #region 變量
        private FileSystemWatcher fileWatcher = null;//文件監視器對象
        private string _filePath = string.Empty;
        private DateTime _activityTime;//對象的激活時間
        private bool _isRemove = false;//該緩存對象是否移除
        #endregion

        #region 屬性
        /// <summary>
        /// 文件路徑
        /// </summary>
        public string FilePath
        {
            get { return _filePath; }
        }
        public DateTime ActivityTime
        {
            get { return _activityTime; }
        }
        #endregion

        #region 構造函數
        public FileChangeMoniter(string filePath)
        {
            this._filePath = filePath;
            SetFileWatcher();
        }
        #endregion

        #region 文件監視器
        /// <summary>
        /// 設置文件監視器
        /// </summary>
        private void SetFileWatcher()
        {
            fileWatcher = new FileSystemWatcher();
            fileWatcher.Path = this.FilePath.Substring(0, this.FilePath.LastIndexOf(@"\"));
            fileWatcher.Filter = this.FilePath.Substring(this.FilePath.LastIndexOf(@"\") + 1, this.FilePath.Length - 1 - this.FilePath.LastIndexOf(@"\"));
            fileWatcher.NotifyFilter = NotifyFilters.LastWrite;
            fileWatcher.EnableRaisingEvents = true;

            fileWatcher.Changed += fileWatcher_Changed;

        }

        /// <summary>
        /// 文件修改事件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void fileWatcher_Changed(object sender, FileSystemEventArgs e)
        {
            _isRemove = true;
        }
        #endregion

        #region IMoniter接口方法
        // 判斷對象是否過期
        public bool IsExpire()
        {
            return this._isRemove;
        }

        // 刷新對象激活時間
        public void RefreshActivityTime(DateTime time)
        {
            this._activityTime = time;
        }
        #endregion

TimeChangeMoniter

用來通過時間策略來判斷緩存對象是否過期,並在刪除緩存對象。
它包括絕對時間和相對時間兩種形式。其中相對時間是相對於該緩存對象上次操作時間來計算的。
代碼如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using Cache.MemoryCache.Interface;

namespace Cache.MemoryCache.Moniter
{
    public class TimeChangeMonitor:IMoniter
    {
        #region 變量
        private DateTime _createTime;//創建時間
        private DateTime _activityTime;//激活時間
        private DateTime _disposeTime;//消除時間
        private TimeSpan _spanTime;//相對時間
        private TimeType _timeType;
        private bool _isRemove = false;
        #endregion

        #region 屬性
        public DateTime CreateTime
        {
            get { return _createTime; }
        }
        public DateTime ActivityTime
        {
            get { return _activityTime; }
        }
        public DateTime DisposeTime
        {
            get { return _disposeTime; }
        }
        public TimeSpan SpanTime
        {
            get { return _spanTime; }
        }
        public TimeType TimeType
        {
            get { return _timeType; }
        }
        #endregion

        #region 構造函數
        public TimeChangeMonitor(DateTime disposeTime)
        {
            this._createTime = DateTime.Now;
            this._activityTime = this._createTime;
            this._disposeTime = disposeTime;
            this._timeType = TimeType.Absolute_Time;
        }

        public TimeChangeMonitor(TimeSpan spanTime)
        {
            this._createTime = DateTime.Now;
            this._activityTime = this._createTime;
            this._spanTime = spanTime;
            this._timeType = TimeType.Relative_Time;
        }
        #endregion
        
        /// <summary>比較絕對時間的過期策略</summary>
        /// <returns>過期:true,不過期:false</returns>
        private bool SetAbsoluteTime()
        {
            DateTime nowTime = DateTime.Now;
            if (nowTime.CompareTo(DisposeTime) < 0)
            {
                return false;
            }
            else
            {
                return true;
            }
        }

        /// <summary>比較相對時間的過期策略</summary>
        /// <returns>過期:true,不過期:false</returns>
        private bool SetRelativeTime()
        {
            DateTime endTime = this._activityTime + this._spanTime;
            DateTime nowTime = DateTime.Now;
            if (nowTime.CompareTo(endTime) < 0)
            {
                return false;
            }
            else
            {
                return true;
            }
        }

        #region IMoniter接口方法
        // 判斷對象是否過期
        public bool IsExpire()
        {
            switch (_timeType)
            {
                case TimeType.Absolute_Time://絕對時間
                    _isRemove = SetAbsoluteTime();
                    break;
                case TimeType.Relative_Time://相對時間
                    _isRemove = SetRelativeTime();
                    break;
            }

            return _isRemove;
        }

        //刷新對象的激活時間
        public void RefreshActivityTime(DateTime time)
        {
            this._activityTime = time;
        }
        #endregion

IMoniter和IRemoveCache接口如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Cache.MemoryCache.Interface
{
    /// <summary>
    /// Moniter的回調方法接口
    /// </summary>
    public interface IMoniter
    {
        /// <summary>
        /// 當前對象是否過期
        /// </summary>
        /// <returns></returns>
        bool IsExpire();

        /// <summary>
        /// 刷新激活時間
        /// </summary>
        void RefreshActivityTime(DateTime time);
    }
}

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Cache.MemoryCache.Interface
{
    /// <summary>
    /// 定義了調用了MemoryCache回調函數刪除緩存對象的接口
    /// </summary>
    internal interface IRemoveCache
    {
        /// <summary>
        /// 判斷緩存對象是否過期並移除
        /// </summary>
        /// <returns></returns>
        void CheckExpireAndRemove();
    }
}

MemoryCache

這個是內存緩存的類,主要用來操作緩存對象的。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using Cache.MemoryCache.Interface;
using Cache.MemoryCache.Moniter;

namespace Cache.MemoryCache
{
    public class MemoryCache
    {
        #region 屬性
        private string _name = string.Empty;
        private static readonly object lockobj = new object();
        public string Name
        {
            get { return _name; }
        }

        private Dictionary<string, MemoryCacheEntry> _cache;
        #endregion

        #region 構造函數
        private MemoryCache(string cacheName)
        {
            this._name = cacheName;
            this._cache = new Dictionary<string, MemoryCacheEntry>();
            MemoryCacheManager.GetManager().Add(this);
        }
        #endregion

        #region 默認的MemoryCache
        private static MemoryCache _defaultMemoryCache;

        /// <summary>默認的MemoryCache</summary>
        public static MemoryCache DefaultCache
        {
            get
            {
                lock (lockobj)//多線程併發鎖
                {
                    if (_defaultMemoryCache == null)
                    {
                        _defaultMemoryCache = new MemoryCache("Default");
                    }
                }
                return _defaultMemoryCache;
            }
        }
        #endregion

        #region 創建一個新的非默認的MemoryCache
        /// <summary>
        /// 該方法是獲取一個新緩存對象
        /// </summary>
        /// <param name="cacheName">MemoryCache名字</param>
        /// <returns></returns>
        public static MemoryCache GetMemoryCache(string cacheName)
        {
            if (MemoryCacheManager.GetManager().ContainCache(cacheName))
            {
                return MemoryCacheManager.GetManager().Get(cacheName);
            }
            else
            {
                return new MemoryCache(cacheName);
            }
        }
        #endregion

        #region 緩存對象操作方法
        /// <summary>將對象放入緩存</summary>
        /// <param name="key">緩存對象的key</param>
        /// <param name="value">需要緩存的對象</param>
        /// <param name="policy">緩存的處理策略</param>
        public void SetCache(string key, object value, MemoryCachePolicy policy)
        {
            lock (lockobj)
            {
                MemoryCacheEntry cacheEntry = new MemoryCacheEntry(this, key, value, policy);
                this._cache.Add(key, cacheEntry);

                //刷新激活時間
                cacheEntry.RefreshActivityTime();
            }
        }

        /// <summary>獲取緩存對象</summary>
        /// <typeparam name="T">緩存對象類型</typeparam>
        /// <param name="key">緩存對象的key</param>
        /// <returns></returns>
        public T GetCache<T>(string key)
        {
            //判斷緩存是否過期
            CheckExpireAndRemove(key);

            if (_cache.ContainsKey(key))
            {   
                MemoryCacheEntry cacheEntry = this._cache[key];
                //刷新激活時間
                cacheEntry.RefreshActivityTime();

                return (T)cacheEntry.Value;
            }
            else
            {
                throw new Exception(string.Format("不存在key爲{0}的緩存對象", key));
            }

        }

        /// <summary>從MemoryCache中移除緩存對象</summary>
        /// <param name="key">緩存對象的key</param>
        public void RemoveCache(string key)
        {
            if (_cache.ContainsKey(key))
            {
                _cache.Remove(key);
            }
        }

        /// <summary>從MemoryCache中移除緩存對象(供MemoryCacheEntry的回調)</summary>
        /// <param name="cacheName"></param>
        /// <param name="key"></param>
        internal void RemoveCache(string cacheName, string key)
        {
            MemoryCacheManager.GetManager().Get(cacheName).RemoveCache(key);
        }

        public bool ContainCache(string key)
        {
            //判斷緩存是否過期
            CheckExpireAndRemove(key);

            return _cache.ContainsKey(key);
        }

        /// <summary>
        /// 判斷緩存對象是否過期並移除
        /// </summary>
        /// <returns></returns>
        private void CheckExpireAndRemove(string key)
        {
            if (_cache.ContainsKey(key))
            {
                //MemoryCacheEntry cacheEntry = this._cache[key];
                IRemoveCache cacheEntry = this._cache[key];
                cacheEntry.CheckExpireAndRemove();
            }
        }
        #endregion
    }
}

MemoryCacheEntry

這是緩存對象類,用來表示一個緩存對象實體的。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using Cache.MemoryCache.Interface;
using Cache.MemoryCache.Moniter;


namespace Cache.MemoryCache
{
    internal class MemoryCacheEntry:IRemoveCache
    {
        #region 變量
        private MemoryCache _memoryCache;
        #endregion

        #region 屬性
        public MemoryCachePolicy CachePolicy { set; get; }
        public object Value { set; get; }
        public string Key { set; get; }
        #endregion

        #region 構造函數
        public MemoryCacheEntry(MemoryCache memoryCache, string key, object value, MemoryCachePolicy policy)
        {
            this._memoryCache = memoryCache;
            this.Key = key;
            this.Value = value;
            this.CachePolicy = policy;
        }
        #endregion

        /// <summary> 刷新對象激活時間 </summary>
        public void RefreshActivityTime()
        {
            switch (CachePolicy.PolicyType)
            { 
                case PolicyType.FileChange:
                    CachePolicy.FileChangeMoniter.RefreshActivityTime(DateTime.Now);
                    break;
                case PolicyType.TimeChange:
                    CachePolicy.TimeChangeMoniter.RefreshActivityTime(DateTime.Now);
                    break;
                case PolicyType.Custom:
                    CachePolicy.CustomChangeMoniters.RefreshActivityTime(DateTime.Now);
                    break;                    
            }
        }

        /// <summary>刪除緩存對象</summary>
        /// <param name="cacheName"></param>
        /// <param name="key"></param>
        private void RemoveCache(string cacheName, string key)
        {
            this._memoryCache.RemoveCache(cacheName, key);
        }

        #region IRemoveCache接口方法
        public void CheckExpireAndRemove()
        {
            bool isRemove = false;
            if (CachePolicy != null)
            {
                switch (CachePolicy.PolicyType)
                { 
                    case PolicyType.FileChange:
                        isRemove = CachePolicy.FileChangeMoniter.IsExpire();
                        break;
                    case PolicyType.TimeChange:
                        isRemove = CachePolicy.TimeChangeMoniter.IsExpire();
                        break;
                    case PolicyType.Custom:
                        isRemove = CachePolicy.FileChangeMoniter.IsExpire();
                        break;
                }
            }

            if (isRemove)
            {
                //回調刪除緩存對象
                RemoveCache(this._memoryCache.Name, Key);
            }
        }
        #endregion
    }
}

MemoryCachePolicy

這是緩存對象的過期策略類,其中包括了FileChangeMoniter,TimeChangeMoniter以及支持實現接口IMoniter的自定義Moniter類型
using System;
using System.Collections.ObjectModel;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using Cache.MemoryCache.Interface;
using Cache.MemoryCache.Moniter;

namespace Cache.MemoryCache
{
    public class MemoryCachePolicy
    {
        #region 變量
        private PolicyType _policyType;

        #endregion

        #region 屬性
        /// <summary>
        /// Policy類型
        /// </summary>
        public PolicyType PolicyType
        {
            get { return _policyType; }
        }

        /// <summary>
        /// 文件改變Moniter
        /// </summary>
        public FileChangeMoniter FileChangeMoniter { set; get; }

        /// <summary>
        /// 時間改變Moniter
        /// </summary>
        public TimeChangeMonitor TimeChangeMoniter { set; get; }

        /// <summary>
        /// 用來自定義擴展的Moniter
        /// </summary>
        public IMoniter CustomChangeMoniters { set; get; }
        #endregion

        #region 構造函數
        public MemoryCachePolicy(PolicyType type)
        {
            this._policyType = type;
        }
        #endregion
    }

    public enum PolicyType
    {
        FileChange = 0,
        TimeChange = 1,
        Custom = 2
    }
}

MemoryCacheManager

這是MemoryCache的管理器,用來保證每個MemoryCache都是單例的,並且提供對MemoryCache管理的一些方法。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using Cache.MemoryCache.Interface;
using Cache.MemoryCache.Moniter;


namespace Cache.MemoryCache
{
    /// <summary>
    /// MemoryCache的管理工具類
    /// 該類是單例類
    /// </summary>
    internal class MemoryCacheManager
    {
        private static MemoryCacheManager _memoryCacheManager;
        private Dictionary<string, MemoryCache> _cacheManager = new Dictionary<string, MemoryCache>();
        private static readonly object lockobj = new object();

        private MemoryCacheManager() { }

        public static MemoryCacheManager GetManager()
        {
            lock (lockobj)//控制多線程的併發問題
            {
                if (_memoryCacheManager == null)
                {
                    _memoryCacheManager = new MemoryCacheManager();
                }
            }
            return _memoryCacheManager;
        }

        /// <summary>將緩存對象存入緩存管理器</summary>
        /// <param name="memoryCache">MemoryCache對象</param>
        public void Add(MemoryCache memoryCache)
        {
            lock (lockobj)
            {
                if (_cacheManager.ContainsKey(memoryCache.Name))
                {
                    _cacheManager.Remove(memoryCache.Name);
                }
                _cacheManager.Add(memoryCache.Name, memoryCache);
            }
        }

        /// <summary>獲取memoryCacheName對應的MemoryCache對象</summary>
        /// <param name="memoryCacheName">MemoryCache名稱</param>
        /// <returns></returns>
        public MemoryCache Get(string memoryCacheName)
        {
            if (_cacheManager.ContainsKey(memoryCacheName))
            {
                return _cacheManager[memoryCacheName];
            }
            else
            {
                throw new Exception("MemoryCache緩存對象找不到!");
                //return default(MemoryCache);
            }
        }

        /// <summary>判斷當前的MemoryCache是否存在</summary>
        /// <param name="memoryCacheName"></param>
        /// <returns></returns>
        public bool ContainCache(string memoryCacheName)
        {
            if (_cacheManager.ContainsKey(memoryCacheName))
            {
                return true;
            }
            else
            {
                return false;
            }
        }

        /// <summary>MemoryCacheManager索引器</summary>
        /// <param name="memoryCacheName">MemoryCache名稱</param>
        /// <returns></returns>
        public MemoryCache this[string memoryCacheName]
        {
            get
            {
                if (_cacheManager.ContainsKey(memoryCacheName))
                {
                    return _cacheManager[memoryCacheName];
                }
                else
                {
                    throw new Exception("MemoryCache緩存對象找不到!");
                    //return default(MemoryCache);
                } 
            }
        }


    }
}

Demo

使用該緩存主要是通過MemoryCache這兩個類來使用。
創建一個MemoryCache可以用過其屬性 DefaultCache獲取一個名爲“Default”的Cache或者GetMemoryCache(string name)獲取一個指定名稱的Cache,這兩種方式獲取的都是一個單例的MemoryCache對象。

如下:
 MemoryCache cache = MemoryCache.DefaultCache;//獲取緩存
            if (cache.ContainCache(CACHE_NAME) == false)//判斷當前key的緩存對象是否存在
            {
                //這裏處理自己的邏輯

                MemoryCachePolicy policy = new MemoryCachePolicy(PolicyType.TimeChange);//設置緩存策略
                TimeChangeMonitor moniter = new TimeChangeMonitor(TimeSpan.FromSeconds(600));//設置相對時間的時間監視器
                //TimeChangeMonitor moniter = new TimeChangeMonitor(DateTime.Now.AddSeconds(600));//設置絕對時間的時間監視器
                policy.TimeChangeMoniter = moniter;

                //存入緩存
                cache.SetCache(CACHE_NAME, dsCheck, policy);
            }
            else
            {
                //從緩存獲取數據
                dsCheck = cache.GetCache<DataSet>(CACHE_NAME);;
            }








發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章