Отключение кэширования ответов ASP.NET HttpHandler - PullRequest
18 голосов
/ 28 июля 2011

Справочная информация

Я сейчас сравниваю производительность NancyFx и ServiceStack.NET, работающих под управлением IIS 7 (тестирование на хосте Windows 7).И то, и другое безумно быстро - локальное тестирование каждой инфраструктуры обрабатывает более 10000 запросов в секунду, а ServiceStack работает примерно на 20% быстрее.

Проблема, с которой я сталкиваюсь, заключается в том, что ASP.NET, кажется, кэширует ответы длякаждый уникальный запрос URI от HttpHandler быстро приводит к значительному увеличению нагрузки на память (3+ ГБ) и перегружает сборщик мусора (~ 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 непригодных для использования объектов!).

Что я пробовалfar

  • В IIS 'HTTP Resonse Headers' установите для параметра «Expire Web content» значение «немедленно».
  • В файле 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

Копание в отражателе I 'мы обнаружили, что установка значения UrlMetadataSlidingExpiration в ноль устраняет большую часть чрезмерного использования памяти за счет сокращения пропускной способности на 50% (класс FileAuthorizationModule кэширует FileSecurityDescriptors, который должен быть несколько дорогим для генерации, когда UrlMetadataSlidingExpiration не равен нулю).

Это делается путем обновления web.config и размещения следующего в:

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

Я собираюсь попытаться полностью отключить запуск FileAuthorizationModuleЕсли возможно, посмотреть, поможет ли это.Однако ASP.NET по-прежнему генерирует 2 * N MapPathCacheInfo и CacheEntry объектов, поэтому память все еще расходуется, только с гораздо меньшей скоростью.

Update # 2

Другойполовина проблемы - та же самая проблема, как описано здесь: Предотвращение заполнения множества различных URL-адресов MVC ASP.NET Cache .Настройка

<cache percentagePhysicalMemoryUsedLimit="1" privateBytesPollTime="00:00:01"/> помогает, но даже при этих очень агрессивных настройках использование памяти быстро возрастает до 2,5 ГБ (по сравнению с 4 ГБ).В идеале эти объекты никогда бы не создавались.В противном случае я могу прибегнуть к хакерскому решению использования отражения для очистки кешей (все эти записи являются «частными» и не перечисляются при переборе по общему кешу).

Ответы [ 3 ]

2 голосов
/ 23 декабря 2011

Я думаю, что это не проблема кеширования, а проблема «высокого использования памяти».

Две вещи,

Если вы используете дружественный объект IDisposable (попробуйтетот, который может использовать ключевое слово «using»).Это позволит вам избавиться от объекта раньше, чем позже, создавая меньшее давление для сборщика мусора в долгосрочной перспективе.

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);

Очевидно, что лучшим решением было бы сделать один звонок со списком пользователей, которых вы хотите получить, и просто обработать один веб-запрос / ответ.

2 голосов
/ 01 февраля 2013

Поздний ответ для других, кто страдает той же проблемой:

Это известная проблема: KB 2504047

Эта проблема возникает из-за того, что уникальные запросы, пытающиеся получить доступ к тем же ресурсам, кэшируются как объекты MapPathCacheInfo в течение 10 минут.

Пока объекты кэшируются в течение 10 минут, потребление памяти процесс W3wp.exe значительно увеличивается.

Исправление можно загрузить здесь

1 голос
/ 04 августа 2011

Убедитесь, что для свойства IsReusable установлено значение false, чтобы IIS не использовал один и тот же процесс запроса для обработки запроса.

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

...