19

背景

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

我遇到的问题是 ASP.NET 似乎正在缓存来自 HttpHandler 的每个唯一 URI 请求的响应,迅速导致巨大的内存压力(3+ GB)和垃圾收集器过度工作(大约 25% 的时间消耗GC)。到目前为止,我一直无法禁用对象的缓存和构建,并且正在寻找有关如何禁用此行为的建议。

细节

请求循环基本如下:

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 Resonse Headers' 中,将 'Expire Web content' 设置为 'immediately'。
  • 在 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%(FileAuthorizationModule 类缓存 FileSecurityDescriptors,当UrlMetadataSlidingExpiration是非零)。

这是通过更新 web.config 并将以下内容放入:

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

如果可能,我将尝试完全禁用 FileAuthorizationModule 运行,看看是否有帮助。但是,ASP.NET 仍在生成 2*NMapPathCacheInfoCacheEntry对象,因此内存仍在消耗,只是速度要慢得多。

更新#2

问题的另一半与此处描述的问题相同:防止许多不同的 MVC URL 填充 ASP.NET Cache。环境

<cache percentagePhysicalMemoryUsedLimit="1" privateBytesPollTime="00:00:01"/> 有帮助,但即使使用这些非常激进的设置,内存使用量也会迅速上升到 2.5GB(与 4GB 相比)。理想情况下,这些对象一开始就不会被创建。如果做不到这一点,我可能会求助于使用反射来清除缓存的 hacky 解决方案(所有这些条目都是“私有的”,并且在迭代公共缓存时不会枚举)。

4

3 回答 3

2

对遇到同样问题的其他人的迟到回应:

这是一个已知问题: KB 2504047

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

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

您可以在此处下载修补程序

于 2013-01-31T21:45:50.427 回答
2

我认为这不是缓存问题,而是“高内存利用率”问题。

两件事情,

如果您使用 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);

显然,最好的解决方案是使用您想要检索的用户列表进行一次调用,然后只处理一个 web 请求/响应。

于 2011-12-23T17:24:08.770 回答
1

确保 IsReusable 属性设置为 false,以便 IIS 不会重用相同的请求进程来处理请求。

http://support.microsoft.com/kb/308001

于 2011-08-04T01:51:15.133 回答