1:.NET Core 已經沒System.Web,也木有了HttpRuntime.Cache,因此,該空間下Cache也木有了。
2:.NET Core 有新的Memory Cache提供,不過該內存類我看了一下,並沒有支持文件的緩存依賴。
因此,在此前提下,預計.NET Core明年出來2.0版本時,可能也沒支持文件的緩存依賴,因此,有必要提前准備實現。
在寫此文前,我掃了一下園子裡關於自定義緩存類的相關文章。
發現很多自定義的緩存類文章都簡單停留在對字典的增刪改查。
因此,決定補充這一篇完整思路的。
下面,就介紹一下這個緩存類的實現過程及原理。
1:用static Dictionary<string,object> 來存檔。
A:為了處理並發,V4.0或以上,可以用System.Collections.Concurrent.ConcurrentDictionary<string,object> 來存檔。
B:如果為了支持.NET 2.0,則需要自己實現一個加鎖的字典(本文即此種情況)
2:對該Dictionary提供增刪改查方法。
3:提供定時緩存的過期策略。
4:提供文件監控策略。
5:測試並發、性能、和內存占用問題。
以下內容,重點介紹我的思路,源碼截圖以片斷方式提供,具體的源碼,會在鏈接中。
如果要支持2.0,那麼就只能自己實現了:實現的思路也很簡單,只要對操作都加上鎖即可:
詳情源碼見:https://github.com/cyq1162/cyqdata/blob/master/Tool/MDictionary.cs
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。
缺點:增加處理邏輯。
優點:過期策略不再有鎖,能快速直接定位過期數據並清除。
【一開始我的思路是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; } }
這個簡而言之,就是文件被修改的時候,如何使緩存自動過期。
我要支持這個策略的原因:是因為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,當文件變更時,可以快速定位到文件。
一個緩存類寫好後,測試是少不了的,特別是並發,畢竟緩存是屬於高並發的操作。
因此,緩存哪些地方要加lock的,哪些可以不加的,都需要仔細思考。
測試是通過的,就不截圖了。
性能測試,是通過和HttpRuntime.Cache做的比較。
100萬次的插入:
100萬次的移除:
暫無測試。
https://github.com/cyq1162/cyqdata/blob/master/Cache/LocalCache.cs
本來是計劃昨天就寫此文的,結果臨時開了培訓課,因此只能深夜來寫此文了。
關於培訓見:http://www.cnblogs.com/cyq1162/p/6097445.html
在培訓的過程,大伙都問怎麼提升技術?我答:造輪子。
另外,有人問我怎麼看.NET Core,還能怎麼看,拉好板凳,就等你了:.NET Core 2.0。
夜又深深,該入眠了~~~~