Фон
Я сравниваю производительность 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" и не перечислены при повторении через общий кэш).