Подтвердить что ты не робот

Отключение кэширования ответов ASP.NET HttpHandler

Фон

Я сравниваю производительность NancyFx и ServiceStack.NET, работающих под IIS 7 (тестирование на хосте Windows 7). Оба они безумно быстрые - тестирование локально на каждый процесс каркаса более чем на 10 000+ req/sec, причем 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 непригодных объектов!).

То, что я пробовал до сих пор

  • В заголовках ответов HTTP "IIS" установите "Истереть веб-контент" в "немедленно".
  • В 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 * N MapPathCacheInfo и CacheEntry, поэтому память по-прежнему потребляется, причем гораздо медленнее.

Обновление # 2

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

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

4b9b3361

Ответ 1

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

Две вещи,

Если вы используете дружественный объект 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-loop намного быстрее, чем у сети будет время ответить (и просто будет поддерживать очередь запросов и замедлять скорость обработки, которые фактически обрабатываются).

System.Threading.Thread.Sleep(250);

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

Ответ 2

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

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

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

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

Вы можете скачать исправление здесь

Ответ 3

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

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