一種小型后台管理系統通用開發框架中的Cache緩存設計


本篇博客記錄一下我在實習的公司的后台管理系統開發框架中學習到的一種關於網站的緩存(Cache)的實現方法,我會在弄懂的基礎上,將該方法在.net core上進行實現。因為公司開發都是基於.net framework的,但是在.net 這一塊,.net framework正在逐漸被.net core所取代,而目前公司的前輩們由於開發任務較重,並沒有着手使用.net core的打算,所以,我自己打算為公司搭建一個基於.net core的后台開發框架,這對自己是一個挑戰,但收獲還是很大的,在這個過程中,我學到了很多。下面我記錄一下我們公司關於網站設計中Cache的一種設計與實現方法(先說在.net mvc下的實現方法,后續會寫另一篇.net core的實現方法): 

  • 總體設計:

 

 

  我們知道的緩存一般分為3種,分別是 Cookies,Session和Cache,這三種的區別和使用場景我在這里就不說了,網上有大神的博客說的很清楚。Cookies主要用於客戶端,Session和Cache用於服務端,本篇主要講Cahe的使用。Cache存儲於服務器的內存中,允許自定義如何緩存數據項,以及緩存時間有多長。當系統內存缺乏時,緩存會自動移除很少使用或者使用優先級較低的緩存項以釋放內存。Cache的使用可以提高整個系統的運行效率。

Cache在使用上也是(key,value)形式的,關於插入、獲取、移除什么的,都可以在Cache類中去查看,這里貼出Cache這個類的內容:

#region 程序集 System.Web, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
// C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5\System.Web.dll
#endregion

using System.Collections;
using System.Reflection;

namespace System.Web.Caching
{
    //
    // 摘要:
    //     Implements the cache for a Web application. This class cannot be inherited.
    [DefaultMember("Item")]
    public sealed class Cache : IEnumerable
    {

        public static readonly DateTime NoAbsoluteExpiration;

        public static readonly TimeSpan NoSlidingExpiration;

        public Cache();

        public object this[string key] { get; set; }

        public int Count { get; }

        public long EffectivePrivateBytesLimit { get; }

        public long EffectivePercentagePhysicalMemoryLimit { get; }

        public object Add(string key, object value, CacheDependency dependencies, DateTime absoluteExpiration, TimeSpan slidingExpiration, CacheItemPriority priority, CacheItemRemovedCallback onRemoveCallback);

        public object Get(string key);

        public IDictionaryEnumerator GetEnumerator();

        public void Insert(string key, object value);

        public void Insert(string key, object value, CacheDependency dependencies);
       
        public void Insert(string key, object value, CacheDependency dependencies, DateTime absoluteExpiration, TimeSpan slidingExpiration, CacheItemPriority priority, CacheItemRemovedCallback onRemoveCallback);
       
        public object Remove(string key);
    }
}

 

可以看到里面有Add,Get,Insert之類的東西,這些用法網上也有很清楚的講述,我也不贅述,也說不清楚,哈哈。

下面,結合上面那張示意圖,來說明一下我要講的緩存設計,個人感覺還是比較好的。

  <key,value>的形式,就是一個value對應一個key,通過key可以設置value的值,也可以獲取value的值。在這里我們把 每個用戶登錄時生成的一個唯一的 id 做為 cache的key,然后把希望放到緩存中的數據作為value,進行緩存數據的處理。但是,我們放到value中的值,可能是有不同用途不同種類的一些值,比如,登錄用戶的基本信息,該系統存儲的菜單數據,這兩個就是用途完全不相干的兩類數據,怎么存儲呢?再另外使用一個key值不同的cache,這應該也是可以的。但是,我們這里講的方法只使用一個Cache。

  具體做法呢,就是把這個value定義為一個 Dictionary<key,value>類型的值,這樣在value里面,我們就可以通過設置不同的key值,來存儲不同用途的緩存數據了。

  • 第一步

 首先,先定義一個存儲value數據的類,代碼如下:

UserCache.cs

using System.Collections.Generic;

namespace Common
{
    public class UserCache
    {
        private readonly Dictionary<string, object> cacheDictionary = new Dictionary<string, object>();
        private readonly object lockObj = new object();

        /// <summary>
        /// 索引器
        /// </summary>
        /// <param name="key">key</param>
        /// <returns>緩存對象</returns>
        public object this[string key]
        {
            get
            {
                lock (lockObj)
                {
                    return cacheDictionary.ContainsKey(key) ? cacheDictionary[key] : null;
                }
            }
            set
            {
                lock(lockObj)
                {
                    if (cacheDictionary.ContainsKey(key))
                    {
                        cacheDictionary[key] = value;
                    }
                    else
                    {
                        cacheDictionary.Add(key, value);
                    }
                }
            }
        }

        public void Remove(string key)
        {
            lock (lockObj)
            {
                if(cacheDictionary.ContainsKey(key))
                {
                    cacheDictionary.Remove(key);
                }
            }
        }

        public void Clear()
        {
            lock(lockObj)
            {
                cacheDictionary.Clear();
            }
        }
    }
}

 

上面的代碼,用到了一個索引器,這使得我們可以像數組那樣用 XXX[index]這樣的方法訪問和設置數據,從代碼中我們可以看到,這個類最終都實現對 cacheDictionary 這個字典的操作,因為我們的數據都存儲在這個字典中。不管你想存儲什么數據只需要定義一個key值,然后存儲到字典中即可。

  • 第二步

  定義好UserCache.cs后,我們再來寫關於緩存操作的類:

  先定義緩存操作類,然后書寫關於緩存操作的代碼:

WebCache.cs(部分)

using System;
using System.Web;
using System.Web.Caching;
using Common;

namespace Console
{
    /// <summary>
    /// 緩存操作類
    /// </summary>
    public class WebCache
    {
        #region 私有變量

        private const string UserIdentifyKey = "CacheUserIdentifyKey";

        #endregion

        #region 私有方法

        private static string GetUserIdentify()
        {
            if (HttpContext.Current.Session[UserIdentifyKey] != null)
                return HttpContext.Current.Session[UserIdentifyKey].ToString();
            var identify = Guid.NewGuid().ToString();
            HttpContext.Current.Session[UserIdentifyKey] = identify;
            return identify;
        }

        private static UserCache GetUserCache()
        {
            var identify = GetUserIdentify();
            if (HttpContext.Current.Cache.Get(identify) == null)
            {
                HttpContext.Current.Cache.Insert(identify, new UserCache(), null, Cache.NoAbsoluteExpiration,
                    new TimeSpan(0, 20, 0), CacheItemPriority.High, CacheRemovedCallback);
            }
            return HttpContext.Current.Cache.Get(identify) as UserCache;
        }

        /// <summary>
        /// 緩存被移除時觸發
        /// </summary>
        /// <param name="key">被移除的緩存的key</param>
        /// <param name="value">被移除的緩存的值</param>
        /// <param name="reason">移除原因</param>
        private static void CacheRemovedCallback(string key, object value, CacheItemRemovedReason reason)
        {
            // 緩存被移除時執行的操作
            // 如果是手動移除,則不處理
            //if (reason == CacheItemRemovedReason.Removed)
            //    return;

            // 此處訪問頁面會報錯,暫時注釋掉
            // ShowNotification(MessageType.Warning, "警告", "由於您太久沒操作頁面已過期,請重新登錄!", true);
        }

        #endregion
    }
}

 

首先看上面的代碼:

上面三段代碼中,核心的代碼是第二段,需要注意的是,都是靜態方法:

 GetUserCache() 方法

當然,我們還是從第一段代碼開始說起,

GetUserIdentify()

        private static string GetUserIdentify()
        {
            if (HttpContext.Current.Session[UserIdentifyKey] != null)
                return HttpContext.Current.Session[UserIdentifyKey].ToString();
            var identify = Guid.NewGuid().ToString();
            HttpContext.Current.Session[UserIdentifyKey] = identify;
            return identify;
        }

 

在一個用戶登錄之初,我們首先給這個用戶生成一個唯一的用戶 id ,然后把這個id保存到 Session中 (Session也是<key,value>形式的)。在第二段代碼中,通過 GetUserIdentify()方法獲取用戶的唯一 id,然后把這個唯一 id作為 Cache的key值。

然后,我們來看第二段代碼:

 GetUserCache():
        private static UserCache GetUserCache()
        {
            var identify = GetUserIdentify();
            if (HttpContext.Current.Cache.Get(identify) == null)
            {
                HttpContext.Current.Cache.Insert(identify, new UserCache(), null, Cache.NoAbsoluteExpiration,
                    new TimeSpan(0, 20, 0), CacheItemPriority.High, CacheRemovedCallback);
            }
            return HttpContext.Current.Cache.Get(identify) as UserCache;
        }

 

這段代碼,首先判斷Cache中是否有值(是否存在這個key的Cache),若不存在,則創建一個(代碼中的 new UserCache())。.net framework中Cache操作使用 HttpContext.Current.Cache,Insert后有若干個參數,意思分別是:

identify:key值;

new UserCache():value值;
第三個參數是:緩存依賴項 CacheDependency ,這里是 null;
Cache.NoAbsoluteExpiration:絕對過期時間 ,這里設置為無絕對過期時間;
new TimeSpan(0, 20, 0):這是滑動過期時間,此處設置為 20 minite;
CacheItemPriority.High:緩存優先級,此處為 high;
CacheRemovedCallback: 緩存移除時的回調函數,這個回調函數的參數是固定寫法,必須按照規定寫,三個參數以及參數類型 不可缺少也不可寫錯,否則會報錯;(具體可見上面的第三段代碼)

上面說到,若不存在,則創建一個 ,若存在,那么就直接返回即可。

接下來,在WebCache.cs中定義一些公共方法,用來供外界的方法調用,以實現對緩存的操作,代碼如下:
WebCache.cs(全):
 
using System;
using System.Web;
using System.Web.Caching;
using Common;

namespace Console
{
    /// <summary>
    /// 緩存操作類
    /// </summary>
    public class WebCache
    {
        #region 私有變量

        private const string UserIdentifyKey = "CacheUserIdentifyKey";

        #endregion

        #region 公共方法

        /// <summary>
        /// 獲取緩存
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public static object GetCache(string key)
        {
            return GetUserCache()[key];
        }

        /// <summary>
        /// 設置緩存
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        public static bool SetCache(string key, object value)
        {
            try
            {
                var userCache = GetUserCache();
                userCache[key] = value;
                return true;
            }
            catch
            {
                return false;
            }
        }

        /// <summary>
        /// 清空緩存
        /// </summary>
        /// <returns></returns>
        public static bool ClearCache()
        {
            try
            {
                // 只清除緩存內容
                // GetUserCache().Clear();

                // 直接從Cache里移除
                var identify = GetUserIdentify();
                HttpContext.Current.Cache.Remove(identify);
                return true;
            }
            catch
            {
                return false;
            }
        }

        /// <summary>
        /// 移除緩存
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public static bool RemoveCache(string key)
        {
            try
            {
                GetUserCache().Remove(key);
                return true;
            }
            catch
            {
                return false;
            }
        }

        #endregion

        #region 私有方法

        private static string GetUserIdentify()
        {
            if (HttpContext.Current.Session[UserIdentifyKey] != null)
                return HttpContext.Current.Session[UserIdentifyKey].ToString();
            var identify = Guid.NewGuid().ToString();
            HttpContext.Current.Session[UserIdentifyKey] = identify;
            return identify;
        }

        private static UserCache GetUserCache()
        {
            var identify = GetUserIdentify();
            if (HttpContext.Current.Cache.Get(identify) == null)
            {
                HttpContext.Current.Cache.Insert(identify, new UserCache(), null, Cache.NoAbsoluteExpiration,
                    new TimeSpan(0, 20, 0), CacheItemPriority.High, CacheRemovedCallback);
            }
            return HttpContext.Current.Cache.Get(identify) as UserCache; // as是一種強制類型轉化的方式
        }

        /// <summary>
        /// 緩存被移除時觸發
        /// </summary>
        /// <param name="key">被移除的緩存的key</param>
        /// <param name="value">被移除的緩存的值</param>
        /// <param name="reason">移除原因</param>
        private static void CacheRemovedCallback(string key, object value, CacheItemRemovedReason reason)
        {
            // 緩存被移除時執行的操作
            // 如果是手動移除,則不處理
            //if (reason == CacheItemRemovedReason.Removed)
            //    return;

            // 此處訪問頁面會報錯,暫時注釋掉
            // ShowNotification(MessageType.Warning, "警告", "由於您太久沒操作頁面已過期,請重新登錄!", true);
        }

        #endregion
    }
}

 

依次定義了GetCache(),SetCache(),RemoveCache(),ClearCache()四個方法,供外界調用,來實現對緩存的操作。 
到這里,基本上關於這個Cache的實現就已經講完了,下面,給出一段代碼,做一個使用的示例。
        private const string LoginUserKey = "CacheKey-LoginUserCacheKey";
/// <summary>
        /// 獲取或設置當前登錄用戶
        /// </summary>
        public static User LoginUser
        {
            get { return WebCache.GetCache(LoginUserKey) as User; }
            set { WebCache.SetCache(LoginUserKey, value); }
        }

 

 SetCache():
WebCache.SetCache(key, value);

 

RemoveCache():
 
RemoveCache(key); //移除字典中某個緩存值

 

ClearCache();
ClearCache(); //清空緩存的字典

 

 
        

關於這個緩存設計,就記錄到這里了,關於.net core下的實現,因為.net core下並沒有System.Web這個類,所以它的Cache實現方式,與.net 下的實現方式有些區別,這個,我會另起一篇博客去記錄。

說明:本片博客並沒有完整的demo,所有的代碼都已貼出。

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM