Asp.Net 何时删除过期的缓存项?
Posted
技术标签:
【中文标题】Asp.Net 何时删除过期的缓存项?【英文标题】:When Does Asp.Net Remove Expired Cache Items? 【发布时间】:2010-11-28 21:20:56 【问题描述】:当您向System.Web.Caching.Cache
添加具有绝对到期日期的项目时,如下例所示,Asp.Net 的行为如何?是吗:
只需将项目标记为过期,然后在下次访问尝试时执行CacheItemRemovedCallback
?
从缓存中删除项目并立即执行CacheItemRemovedCallback
?
HttpRuntime.Cache.Insert(key,
new object(),
null,
DateTime.Now.AddSeconds(seconds),
Cache.NoSlidingExpiration,
CacheItemPriority.NotRemovable,
OnCacheRemove);
MSDN 似乎表明它会立即发生。例如,the "Expiration" section of the "ASP.NET Caching Overview" 表示“当项目过期时,ASP.NET 会自动从缓存中删除项目”。同样,主题 "How to: Notify an Application When an Item Is Removed from the Cache" 中的示例表示“如果调用 GetReport
[示例中的方法] 之间的间隔超过 15 秒,ASP.NET 将从缓存中删除报告。”
不过,这些都不是明确的。他们没有说“回调立即执行”,我可以想象他们的作者可能如何认为上面的选项 1 算作“删除”一个项目。所以我做了一个快速而肮脏的测试,瞧,它似乎正在立即执行 - 即使没有人访问我的网站,我也会定期收到 60 秒的回调。
尽管如此,我的测试又快又脏,在我对Is there a way to run a process every day in a .Net web application without writing a windows service or SQL server jobs 的回答中,有人建议 Asp.Net 实际上推迟了回调的删除和执行,直到有东西再次尝试访问缓存。
任何人都可以权威地解决这个问题还是这只是一个实现细节?
【问题讨论】:
我认为这个问题没有权威的答案,因为正如 Raymond Chen 经常指出的那样,您将一个软件版本的行为解释为对所有未来版本都具有权威性.根据我的经验,这是一个坏主意,因为 ASP.NET 可能随时更改其行为并破坏您的应用程序。 你为什么不测试一下。在您的回调中写入文件并查看创建日期以及它与过期的关系。 @dkackman - 我不需要“现在和永远”的答案,我愿意接受 .NET 2.0 和 3.5 的权威答案。对我来说,这似乎不仅仅是一个实现细节:我希望文档在某处说,如果是这种情况,实际删除不是确定性的。也许MS不同意。我只是想看看我是否忽略了文档中的某些内容。 【参考方案1】:Reflector 万岁!
过期的缓存项实际上在以下情况下被删除(并调用回调):
1) 有东西试图访问缓存项。
2) ExpiresBucket.FlushExpiredItems
方法运行并获取项目。此方法经过硬编码,每 20 秒执行一次(*** 问题Changing frequency of ASP.NET cache item expiration 的公认答案证实了我通过 Reflector 阅读此代码)。但是,这需要额外的限定(请继续阅读)。
Asp.Net 为服务器上的每个 CPU 维护一个缓存(我不确定它们是代表逻辑 CPU 还是物理 CPU);其中每一个都维护一个CacheExpires
实例,该实例具有相应的Timer
,每20 秒调用一次FlushExpiredItems
方法。
此方法串行迭代缓存过期数据的另一个“桶”集合(ExpiresBucket
实例的数组),依次调用每个桶的 FlushExpiredItems
方法。
此方法 (ExpiresBucket.FlushExpiredItems
) 首先迭代存储桶中的所有缓存项,如果一项已过期,则将其标记为过期。然后(我在这里非常简化)它迭代它标记为过期的项目并删除它们,执行CacheItemRemovedCallback
(实际上,它调用CacheSingle.Remove
,它调用CacheInternal.DoRemove
,然后是CacheSingle.UpdateCache
,然后是CacheEntry.Close
,它实际上调用了回调)。
所有这些都是连续发生的,因此有可能某些事情会阻止整个过程并阻止事情发生(并将缓存项的到期时间从其指定的到期时间推回)。
但是,在这个时间分辨率下,最小到期间隔为 20 秒,进程中唯一可能阻塞很长时间的部分是执行 CacheItemRemovedCallbacks
。可以想象,其中任何一个都可以无限期地阻塞给定的Timer
的FlushExpiredItems
线程。 (虽然 20 秒后,Timer
会产生另一个 FlushExpiredItems
线程。)
总而言之,Asp.Net 不保证它会在指定的时间执行回调,但它会在某些条件下这样做。只要过期间隔相隔超过 20 秒,并且只要缓存不必执行耗时的 CacheItemRemovedCallbacks
(全局 - 任何回调都可能干扰任何其他回调),它就可以执行过期回调日程。这对于某些应用程序来说已经足够了,但对于其他应用程序来说就不够了。
【讨论】:
太好了,感谢您深入研究。我很确定自从几年前我在 Reflector 中挖掘它以来,这已经有所改善。此外,ASP.NET 4 包括额外的缓存增强功能,其中一些在此处列出:asp.net/learn/whitepapers/aspnet40 我很高兴 - 结果很有趣。当然,既然你已经提出来了,我周末的一部分时间就得看最新的 4.0 beta 程序集,干掉你! ;)【参考方案2】:过期的项目不会立即从缓存中删除,它们只是被标记为过期。在缓存未命中之前,您不会收到回调。我在 ASP.NET 1.1 天时遇到了这个问题,and it hasn't changed。
可能存在立即删除过期项目的情况 - 例如内存不足和 CPU 高 - 但您不能指望它。
我通常使用定时重新加载缓存的计时器。
【讨论】:
我喜欢您在文章中替换缓存过期的技术!但我仍然持怀疑态度:我的测试站点已经运行了一整天,无人看管,我的机器上几乎没有运行其他任何东西,每分钟都尽职尽责地点击它的回调。为什么说它们只标记为过期?是因为您链接到的史蒂夫史密斯文章吗? (msdn.microsoft.com/en-us/library/aa478965.aspx) 他只断言过期的缓存项不会被删除 - 并且... ...有点不清楚他是在谈论特定的缓存到期还是他使用的缓存到期(NoAbsoluteExpiration / TimeSpan.Zero 配置)。请允许我补充一点,我并不是要对史蒂夫(或您!)不尊重,他为整个 .NET 社区做出了巨大贡献。它与我亲眼所见相矛盾(而且我确信我的测试不会因高内存/cpu 使用率而受到影响)。我想我需要下载 Reflector 自己看看,哪一点我会很乐意更新这个并吃一些当之无愧的乌鸦。 :) 我很想被证明是错误的!我还没有看到回调在 .NET 1.1、2.0 或 3.5 中可靠地发生。你有什么东西可以 ping 网站,让网站保持活力吗?你能提供更多关于你的代码和托管的细节吗? 你好,乔恩,当然 - 我当然感谢您的意见,因为这非常令人烦恼!我现在只是在我的开发机器上运行它,安装了 vanilla XP SP2,IIS 6.0。我编译到 2.0 运行时,设置了一个虚拟,并访问了一次页面。我的日志显示的唯一内容是 Cruise Control 每 5 分钟访问一次我的 ccnet 服务器。该代码基本上与blog.***.com/2008/07/easy-background-tasks-in-aspnet 中的示例完全相同。在 CacheItemRemoved 方法中,我正在向 Windows EventLog 写入一条消息。以上是关于Asp.Net 何时删除过期的缓存项?的主要内容,如果未能解决你的问题,请参考以下文章
实现自定义 CacheDependency 以使 ASP.Net 缓存项无效
Asp.Net Identity2,如何检测 cookie 何时过期