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

来源:https://www.cnblogs.com/CherishTheYouth/archive/2019/05/09/CherishTheYouth_2019_0509.html
-Advertisement-
Play Games

本片博客記錄一下我在實習的公司的後臺管理系統開發框架中學習到的一種關於網站的緩存(Cache)的實現方法,我會在弄懂的基礎上,將該方法在.net core上進行實現。因為公司開發都是基於.net framework的,但是在.net 這一塊,.net framework正在逐漸被.net core所 ...


本片博客記錄一下我在實習的公司的後臺管理系統開發框架中學習到的一種關於網站的緩存(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,所有的代碼都已貼出。

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 5.9自我總結 1.集合數據類型 1.定義 用{}中間值用,隔開,且集合中元素排列是無序的,而且如果有相同元素會只保留一個 2.演算法 1.必須掌握 1.長度len 2.成員運算in和not in 3.並集(union或者|) ) 4.交集(intersection或者&) ) 5差集(differe ...
  • 這裡列舉幾個經過驗證的可用的CORS使用示例, 方便在需要的時候可以直接使用 示例1 這種比較常見,即在ConfigureServices中添加中間件及定義其策略;而在Configure中把中間件設置到管道中 示例2 註意,這個示例中,直接在Configure中的app.UseCors中設置的跨域的 ...
  • 一、Excel理論知識 最新版NPOI2.4.1鏈接:https://pan.baidu.com/s/1iTgJi2hGsRQHyw2S_4dIUw 提取碼:adnq • 整個Excel表格叫做工作簿:WorkBook • 工作簿由以下幾部分組成 a.頁(Sheet); b.行(Row); c.單元 ...
  • 類(class)是C#類型中最基礎的類型。類是一個數據結構,將狀態(欄位)和行為(方法和其他函數成員)組合在一個單元中。類提供了用於動態創建類實例的定義,也就是對象(object)。類支持繼承(inheritance)和多態(polymorphism),即派生類能夠擴展和特殊化基類的機制。使用類聲明 ...
  • 一般我們與後端對接的時候會用到UnityWebRequest這裡簡單使用這個與後端進行交互這個是總類 using UnityEngine;using System.Collections;using System.Collections.Generic;using System;using Unit ...
  • 在asp.net mvc中,很常使用using(Html.BeginForm()){}來生成表單提交 不傳入參數時,預設提交到原始url 最坑的是,此表單自動提交時,會將所在頁面的原始url的參數也一併提交到後臺 故,如果用Html.BeginForm生成的表單要在js中提交,則必須確保原始url的 ...
  • 視頻與PR:https://github.com/terrajobst/minsk/blob/master/docs/episode-03.md 作者是 Immo Landwerth(https://twitter.com/terrajobst),微軟 .NET 團隊的項目經理。 這一集前半段主要是 ...
  • 1:引用dll MySql.Data.dll, MySqlbackup.dll 2:建一個數據連接靜態類 public static class mysql{public static string constr = "database=test;Password=密碼;user ID=root;s ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...