禁用ASP.NET HttpHandler响应缓存

19

背景

我正在比较在IIS 7(在Windows 7主机上测试)下运行的NancyFx和ServiceStack.NET的性能。两者都非常快-在本地测试时,每个框架处理超过10,000次/秒的请求,其中ServiceStack大约快了20%。

我遇到的问题是,ASP.NET似乎会缓存HttpHandler每个唯一URI请求的响应,很快就会导致巨大的内存压力(3+ GB)并且使垃圾收集器过载(GC耗时25%)。到目前为止,我无法禁用缓存和对象的积累,并正在寻求有关如何禁用此行为的建议。

细节

请求循环基本上如下:

for i = 1..100000:
    string uri = http://localhost/users/{i}
    Http.Get(uri)
响应是一个简单的JSON对象,格式为{ UserID: n }。
我已经打开了WinDBG,对于每个请求,有以下内容:
- 一个 System.Web.FileChangeEventHandler - 两个 System.Web.Configuration.MapPathCacheInfos - 两个 System.Web.CachedPathDatas - 三个 System.Web.Caching.CacheDependencys - 五个 System.Web.Caching.CacheEntrys
显然,这些缓存项让我相信这是一个缓存膨胀问题(我希望能够摆脱150,000个无用的对象!)。
我已经尝试过:
- 在IIS“HTTP响应标头”中,将“过期Web内容”设置为“立即”。 - 在web.config中
<system.web>
 <caching>
    <outputCache enableOutputCache="false" enableFragmentCache="false"/>
  </caching>
</system.web>
  • 同样也可以在web.config文件中进行配置(还有很多政策的变体,包括不配置)。

  • <caching enabled="false" enableKernelCache="false">
      <profiles>
        <add policy="DontCache" kernelCachePolicy="DontCache" extension="*/>
      </profiles>
    </caching>
    
  • 查看了框架的源代码,看是否内置了使用ASP.NET缓存的“特性”。虽然有缓存助手,但它们是私有的,不会利用ASP.NET缓存。

  • 更新 #1

    通过反射器深入挖掘后,我发现将UrlMetadataSlidingExpiration的值设置为零可以消除大量过度的内存使用,但吞吐量会降低50%(当UrlMetadataSlidingExpiration非零时,FileAuthorizationModule类会缓存FileSecurityDescriptors,这可能相对昂贵)。

    这可以通过更新web.config并放置以下内容来实现:

    <hostingEnvironment urlMetadataSlidingExpiration="00:00:00"/>
    

    我将尝试完全禁用FileAuthorizationModule的运行(如果可能的话)来查看是否有所帮助。然而,ASP.NET仍会生成2*N个MapPathCacheInfoCacheEntry对象,因此内存仍将被占用,只是速度慢了很多。

    更新#2

    问题的另一半与此处描述的问题相同:Prevent many different MVC URLs from filling ASP.NET Cache。设置

    <cache percentagePhysicalMemoryUsedLimit="1" privateBytesPollTime="00: 00: 01"/>可以有所帮助,但即使使用这些非常激进的设置,内存使用也会迅速上升到2.5GB(相对于4GB)。理想情况下,这些对象本身就不应该被创建。如果失败,我可能会采用反射的hacky解决方案来清除缓存(所有这些条目都是“私有”的,在枚举公共缓存时不会列举它们)。


    1
    作为IIS托管的替代方案,我使用了两个框架的“自托管”功能,这使得我完全控制缓存并完全避免了IIS内存膨胀问题(自托管最多使用60MB,而IIS托管最多使用4GB)。但我仍然觉得自己错过了一些显而易见的东西——这是如此简单的拒绝服务攻击,肯定有什么方法可以避免它……对吗? - Kevin Pullin
    3个回答

    2

    对于其他遇到同样问题的人,这是一个晚期响应:

    这是一个已知的问题: KB 2504047

    此问题是因为尝试访问相同资源的唯一请求被缓存为MapPathCacheInfo对象,时间为10分钟。

    当对象被缓存10分钟时,W3wp.exe进程的内存消耗显著增加。

    您可以在此处下载热补丁。


    请注意,这个补丁似乎已经合并到.NET 4.5中。 - Kevin Pullin

    2
    我认为这不是缓存问题,而更多是“高内存利用率”的问题。
    有两件事:
    如果您使用一个能够使用“using”关键字的IDisposable友好对象。这将使您能够尽早处理该对象,从而在长期内减少垃圾回收器的压力。
    for (int i = 0; i < 10000; i++) {
        using (System.Net.WebClient c = new System.Net.WebClient()) {
            System.IO.Stream stream = c.OpenRead(String.Format("http://url.com/{0}", i));
    
        }
    }
    

    从您的伪代码中,我只能假设您正在使用System.Net.WebHttpRequest,它不是可处理的并且如果您进行连续调用,它可能会存在比应该更长的时间。

    其次,如果您对外部服务器进行连续调用,我建议在每个调用之间设置延迟。这将给处理器一些喘息时间,因为处理器将比网络更快地处理for循环,并且将保持请求排队,减慢实际被处理的请求的速度。

    System.Threading.Thread.Sleep(250);
    

    显然,最好的解决方案是一次性调用,使用一个用户列表来检索数据,并处理一个网络请求/响应。

    1

    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接