ASP.NET Core 折騰筆記二:本身寫個完整的Cache緩存類來支持.NET Core

背景:

1:.NET Core 已經沒System.Web,也木有了HttpRuntime.Cache,所以,該空間下Cache也木有了。html

2:.NET Core 有新的Memory Cache提供,不過該內存類我看了一下,並無支持文件的緩存依賴。git

所以,在此前提下,預計.NET Core明年出來2.0版本時,可能也沒支持文件的緩存依賴,所以,有必要提早準備實現。github

在寫此文前,我掃了一下園子裏關於自定義緩存類的相關文章。緩存

發現不少自定義的緩存類文章都簡單停留在對字典的增刪改查。安全

所以,決定補充這一篇完整思路的。併發

下面,就介紹一下這個緩存類的實現過程及原理。函數

實現緩存的類的核心思路介紹:

1:用static Dictionary<string,object> 來存檔。高併發

A:爲了處理併發,V4.0或以上,能夠用System.Collections.Concurrent.ConcurrentDictionary<string,object> 來存檔。性能

B:若是爲了支持.NET 2.0,則須要本身實現一個加鎖的字典(本文即此種狀況)測試

2:對該Dictionary提供增刪改查方法。

3:提供定時緩存的過時策略。

4:提供文件監控策略。

5:測試併發、性能、和內存佔用問題。

如下內容,重點介紹個人思路,源碼截圖以片段方式提供,具體的源碼,會在連接中。

1:自定義線程安全的MDictionary(支持.NET 2.0)

若是要支持2.0,那麼就只能本身實現了:實現的思路也很簡單,只要對操做都加上鎖便可:

詳情源碼見:https://github.com/cyq1162/cyqdata/blob/master/Tool/MDictionary.cs

2:時間過時策略:

 private MDictionary<string, object> theCache = new MDictionary<string, object>(2048, StringComparer.OrdinalIgnoreCase);//key,cache
 private MDictionary<string, DateTime> theKeyTime = new MDictionary<string, DateTime>(2048, StringComparer.OrdinalIgnoreCase);//key,time

有了theKeyTime,在每取get cache的時候,根據時間能夠判斷出,該Key是否是,若是已過時,則放棄。

可是有一個問題,若是緩存已通過期,但一直不被調用,那不是一直存在?

爲了解決這個問題,須要一個定時器,定時清理過時的Cache。

因爲Cache已經被設計成單例,所以能夠在構造函數啓動一個線程,來作定時任務清理過時的緩存。

下面有兩種策略,之前的,和如今的,我分別介紹一下:

之前的:

定時遍歷theKeyTime,找到過時時間的Cache進行刪除。

由於遍歷期間集合不能修改或刪除,所以將遍歷的符合條件的存檔到新的對象,再統一處理新的對象去清除。

優勢:邏輯簡單。

缺點:遍歷的過程,緩存不能被修改,須要鎖住(緩存的對象越多,鎖住的時間越長),另外每次都要遍歷全部。

如今的:

private SortedDictionary<int, MList<string>> theTime = new SortedDictionary<int, MList<string>>();//worktime,keylist

新增長了一個時間片字典,以固定的時間(如5分鐘)爲1個單位。

這樣全部緩存的時間就有序的分散在這些時間片上,定時器只要按節奏處理一個就能夠了。

每一個時間片都記錄全部的Key。

缺點:增長處理邏輯。

優勢:過時策略再也不有鎖,能快速直接定位過時數據並清除。

3:關於List的性能

【一開始個人思路是List<key> keys來存檔全部key,移除的時候只移除key,而後其它交給定時器去清理。

因爲只考慮它是線程安全,結果作性能測試時,很明顯的發現問題】

List是鏈表實現,所以,隨着數據量的增長,Contains方法的性能會極速降低。

所以,須要簡單的處理一下解決性能問題,臨時折騰了個MList:

 internal class MList<T>
    {
        List<T> list;
        Dictionary<T, int> dic;
        public MList()
        {
            list = new List<T>();
            dic = new Dictionary<T, int>();
        }
        public MList(int num)
        {
            list = new List<T>(num);
            dic = new Dictionary<T, int>(num);
        }
        public void Add(T key)
        {
            dic.Add(key, 0);
            list.Add(key);
        }
        public bool Contains(T key)
        {
            return dic.ContainsKey(key);
        }
        public void Remove(T key)
        {
            dic.Remove(key);
            list.Remove(key);
        }
        public void Clear()
        {
            dic.Clear();
            list.Clear();

        }
        public int Count
        {
            get
            {
                return list.Count;
            }
        }
        public List<T> GetList()
        {
            return list;
        }
    }

4:文件緩存依賴策略:

這個簡而言之,就是文件被修改的時候,如何使緩存自動過時。

我要支持這個策略的緣由:是由於Taurus.MVC,對View加載的html會被緩存在內存中的,當html被修改時,須要及時反應清掉緩存並從新加載。

 private MDictionary<string, string> theFileName = new MDictionary<string, string>();//key,filename

 private MDictionary<string, FileSystemWatcher> theFolderWatcher = new MDictionary<string, FileSystemWatcher>();//folderPath,watch
 private MDictionary<string, MList<string>> theFolderKeys = new MDictionary<string, MList<string>>();//folderPath,keylist

重點講解:

1:用FileSystemWatcher來作文件監控(發現.NET Core里居然有支持這個類)

2:問題:一開始,也是想的很簡單,每個文件開一個監控就完事了,結果沒那麼簡單:

A:FileSystemWatcher對象太多,性能降低很快。

B:不一樣的Key指向同一個路徑問題。

3:解決:後來,想到監控是以文件夾爲單位,那麼經過文件夾來搞搞實現:

A:以文件夾爲單位:所以,文件對象便可以減小不少,提高性能問題。

B:以文件夾爲單位:能夠彙總對應的Keys,當文件變動時,能夠快速定位到文件。

5:併發:

一個緩存類寫好後,測試是少不了的,特別是併發,畢竟緩存是屬於高併發的操做。

所以,緩存哪些地方要加lock的,哪些能夠不加的,都須要仔細思考。

測試是經過的,就不截圖了。

6:性能:

性能測試,是經過和HttpRuntime.Cache作的比較。

100萬次的插入:

100萬次的移除:

7:佔用內存:

暫無測試。

詳細源碼:

https://github.com/cyq1162/cyqdata/blob/master/Cache/LocalCache.cs

總結:

原本是計劃昨天就寫此文的,結果臨時開了培訓課,所以只能深夜來寫此文了。

關於培訓見:http://www.cnblogs.com/cyq1162/p/6097445.html

在培訓的過程,大夥都問怎麼提高技術?我答:造輪子。

另外,有人問我怎麼看.NET Core,還能怎麼看,拉好板凳,就等你了:.NET Core 2.0。

夜又深深,該入眠了~~~~

相關文章
相關標籤/搜索