ASP. NET Core: Write a complete Cache class to support. NET Core and corecache.
Background:
1:. NET Core does not have System. Web or HttpRuntime. Cache. Therefore, the Cache in this space is also available.
2: the. NET Core has a new Memory Cache, But I have looked at this Memory class and it does not support File Cache dependencies.
Therefore, it is estimated that the File Cache dependency may not be supported when. NET Core comes out of Version 2.0 next year. Therefore, it is necessary to prepare for implementation in advance.
Before writing this article, I scanned some articles about the custom cache class in the garden.
It is found that many custom cache-type articles simply stop adding, deleting, modifying, and querying dictionaries.
Therefore, I decided to add this complete idea.
Next, we will introduce the implementation process and principle of this cache class.
Core Ideas for implementing cache classes:
1: use static Dictionary <string, object> for archiving.
A: To process concurrency, you can use System. Collections. Concurrent. ConcurrentDictionary <string, object> to archive the data.
B: To support. NET 2.0, You need to implement a locking Dictionary (this is the case in this article)
2: adds, deletes, modifies, and queries the Dictionary.
3: provides a timed cache expiration policy.
4: Provides File monitoring policies.
5: Test concurrency, performance, and memory usage problems.
The following content focuses on my ideas. The source code is provided in parts. The specific source code is included in the link.
1: Custom thread-safe MDictionary (supports. NET 2.0)
To support 2.0, you can only implement it by yourself: The Implementation idea is also very simple, as long as you add a lock to the operation:
For details, see the source code: https://github.com/cyq1162/cyqdata/blob/master/Tool/MDictionary.cs
2: Time Expiration Policy:
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
With theKeyTime, when getting get cache, you can determine whether the Key is based on the time. If it has expired, give up.
But there is a problem: If the cache has expired but is never called, isn't it always there?
To solve this problem, a timer is required to regularly clean up expired Cache.
Because the Cache has been designed as a Singleton, you can start a thread in the constructor to perform a scheduled task to clear the expired Cache.
The following two policies are available:
Traverse theKeyTime regularly and find the Cache with the expiration time to delete it.
Because the set cannot be modified or deleted during the traversal process, the retrieved objects are archived to the new object, and the new objects are cleared.
Advantage: the logic is simple.
Disadvantage: During the traversal process, the cache cannot be modified and needs to be locked (the more cached objects, the longer the lock time). In addition, all objects must be traversed each time.
Current:
private SortedDictionary<int, MList<string>> theTime = new SortedDictionary<int, MList<string>>();//worktime,keylist
A new time slice dictionary is added, with a fixed time (such as 5 minutes) as one unit.
In this way, the cache time is distributed in order on these time slices, and the timer only needs to process one timer at a pace.
All keys are recorded for each time slice.
Disadvantage: add processing logic.
Advantage: There is no lock in the expiration Policy, and the expired data can be quickly located and cleared.
3: List Performance
[In the beginning, my idea was to List <key> keys to archive all the keys. When I removed them, I only removed the keys and handed the other keys to the timer for cleanup.
Because it is thread-safe, it is obvious to find problems when performing performance tests]
List is a linked List implementation. Therefore, as the amount of data increases, the performance of the Contains method decreases rapidly.
Therefore, you need to solve the performance problem with a simple solution and temporarily submit a 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: File Cache dependency policy:
In short, this is how to make the cache automatically expire when the file is modified.
I want to support this policy: Because Taurus. MVC, html loaded to the View will be cached in the memory. When the html is modified, You need to promptly clear the cache and reload it.
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
Key points:
1: Use FileSystemWatcher for file monitoring (this class is supported in. NET Core)
2: Problem: At the beginning, I thought it was very simple. It would be easy to open a monitoring file, but the result was not that simple:
A: There are too many FileSystemWatcher objects, and the performance decreases rapidly. B: different keys point to the same path.
3: solution: Later, I thought that monitoring is based on folders, So I implemented it through folders:
A: folders are used as the unit. Therefore, file objects can be much reduced to improve performance. B: In folders: You can summarize the corresponding Keys. When the file is changed, you can quickly locate the file.
5: concurrency:
After a cache class is written, testing is indispensable, especially concurrency. After all, caching is a highly concurrent operation.
Therefore, you need to think carefully about the locations where the cache needs to be locked and those that do not need to be added.
If the test is successful, it will fail.
6: performance:
The performance test is compared with HttpRuntime. Cache.
1 million inserts:
1 million removal:
7: memory usage:
No tests.
Detailed source code:
Https://github.com/cyq1162/cyqdata/blob/master/Cache/LocalCache.cs
Summary:
It was originally planned to write this article yesterday. As a result, the training class was held temporarily, so I had to write this article late at night.
For training, see: http://www.cnblogs.com/cyq1162/p/6097445.html
During the training process, everyone asked how to improve the technology? A: Create a wheel.
In addition, someone asked me how to look at. NET Core and how to look at it. After pulling the bench, I will wait for you:. NET Core 2.0.
Night and deep, it's time to sleep ~~~~