从代码中可以看出,MemoryCacheEqualityComparer的真正作用就是定义MemoryCacheKey的比较方法。判断两个两个MemoryCacheKey是否相等使用的就是MemoryCacheKey中的Key属性。因此我们在MemoryCache中获取和设置相关的内容时,使用的都是对于MemoryCacheKey的相关运算结果。
MemoryCacheEntry此类型是缓存项在内存中真正的存在形式。它继承自MemoryCacheKey类型,并在此基础上增加了很多的属性和方法,比如判断是否超时等。
先来看下该类的整体情况:
总的来说,MemoryCacheEntry中的属性和方法主要为三类:
缓存的内容相关,如Key、Value
缓存内容的状态相关,如State、HasExpiration方法等
缓存内容的相关事件相关,如CallCacheEntryRemovedCallback方法、CallNotifyOnChanged方法等
理解了MemoryCache中数据的组织方式后,可以帮助理解数据是如何从MemoryCache中被一步步查询得到的。
如何从MemoryCahe中查询数据从MemoryCache中获取数据经历了哪些过程呢?从整体来讲,大致可以分为两类:获取数据和验证有效性。
以流程图的方式表达上述步骤如下:
详细的步骤是这样的:
校验查询参数RegionName和Key,进行有效性判断
构造MemoryCacheKey对象,用于后续步骤查询和比对现有数据
获取MemoryCacheStore对象,缩小查询范围
从MemoryCacheStore的HashTable类型属性中提取MemoryCacheEntry对象,得到key对应的数据
判断MemoryCacheEntry对象的有效性,进行数据验证工作
处理MemoryCacheEntry的滑动超时时间等访问相关的逻辑
看到此处,不禁想起之前了解的其他缓存系统中的设计,就像历史有时会有惊人的相似性,进行了良好设计的缓存系统在某些时候看起来确实有很多相似的地方。通过学习他人的优良设计,从中可以学到很多的东西,比如接下来的缓存超时机制。
MemoryCache超时机制MemoryCache在设置缓存项时可以选择永久缓存或者在超时后自动消失。其中缓存策略可以选择固定超时时间和滑动超时时间的任意一种(注意这两种超时策略只能二选一,下文中会解释为什么有这样的规则)。
缓存项的超时管理机制是缓存系统(比如)的必备功能,Redis中有主动检查和被动触发两种,MemCached采用的是被动触发检查,那么内存缓存MemoryCache内部是如何管理缓存项的超时机制?
MemoryCache对于缓存项的超时管理机制与Redis类似,也是有两种:定期删除和惰性删除。
定期删除既然MemoryCache内部的数据是以MemoryCacheStore对象为单位进行管理,那么定期检查也很有可能是MemoryCacheStore对象内部的一种行为。
通过仔细阅读源码,发现MemoryCacheStore的构造函数中调用了InitDisposableMembers()这个方法,该方法的代码如下:
private void InitDisposableMembers() { //_insertBlock是MemoryCacheStore的私有属性 //_insertBlock的声明方式是:private ManualResetEvent _insertBlock; _insertBlock = new ManualResetEvent(true); //_expires是MemoryCacheStore的私有属性 //_expires的声明方式是:private CacheExpires _expires; _expires.EnableExpirationTimer(true); }其中跟本章节讨论的超时机制有关的就是_expires这个属性。由于《.NET reference source》中并没有这个CacheExpires类的相关源码,无法得知具体的实现方式,因此从Mono项目中找到探索该类型的具体实现。
class CacheExpires : CacheEntryCollection { public static TimeSpan MIN_UPDATE_DELTA = new TimeSpan (0, 0, 1); public static TimeSpan EXPIRATIONS_INTERVAL = new TimeSpan (0, 0, 20); public static CacheExpiresHelper helper = new CacheExpiresHelper (); Timer timer; public CacheExpires (MemoryCacheStore store) : base (store, helper) { } public new void Add (MemoryCacheEntry entry) { entry.ExpiresEntryRef = new ExpiresEntryRef (); base.Add (entry); } public new void Remove (MemoryCacheEntry entry) { base.Remove (entry); entry.ExpiresEntryRef = ExpiresEntryRef.INVALID; } public void UtcUpdate (MemoryCacheEntry entry, DateTime utcAbsExp) { base.Remove (entry); entry.UtcAbsExp = utcAbsExp; base.Add (entry); } public void EnableExpirationTimer (bool enable) { if (enable) { if (timer != null) return; var period = (int) EXPIRATIONS_INTERVAL.TotalMilliseconds; timer = new Timer ((o) => FlushExpiredItems (true), null, period, period); } else { timer.Dispose (); timer = null; } } public int FlushExpiredItems (bool blockInsert) { return base.FlushItems (DateTime.UtcNow, CacheEntryRemovedReason.Expired, blockInsert); } }