asp.net caching httphandler servicestack nancy

Deshabilitar el almacenamiento en caché de respuestas HttpHandler de ASP.NET



caching servicestack (3)

Fondo

Estoy en medio de comparar el rendimiento de NancyFx y ServiceStack.NET que se ejecutan bajo IIS 7 (pruebas en un host de Windows 7). Ambos son increíblemente rápidos: la prueba local de cada marco procesa más de 10,000 req / s, con ServiceStack siendo aproximadamente un 20% más rápido.

El problema con el que me estoy topando es que ASP.NET parece estar almacenando en caché las respuestas para cada solicitud de URI única del HttpHandler, lo que rápidamente lleva a una presión de memoria masiva (3+ GB) y sobrecarga el recolector de basura (~ 25% de tiempo consumido por el GC). Hasta ahora no he podido deshabilitar el almacenamiento en caché y la acumulación de objetos, y estoy buscando sugerencias sobre cómo deshabilitar este comportamiento.

Detalles

El bucle de solicitud es básicamente el siguiente:

for i = 1..100000: string uri = http://localhost/users/{i} Http.Get(uri)

La respuesta es un objeto JSON simple, formateado como {UserID: n}.

He abierto WinDBG, y para cada solicitud hay:

  • One System.Web.FileChangeEventHandler
  • Dos System.Web.Configuration.MapPathCacheInfos
  • Dos System.Web.CachedPathDatas
  • Tres System.Web.Caching.CacheDependencys
  • Cinco System.Web.Caching.CacheEntrys

Obviamente, estos elementos de caché son los que me llevan a creer que es un problema de acumulación de caché (¡me encantaría deshacerme de 150,000 objetos inutilizables!).

Lo que he probado hasta ahora

  • En ''Encabezados de respuesta HTTP'' de IIS, establezca ''Expire contenido web'' en ''inmediatamente''.
  • En la web.config

    <system.web> <caching> <outputCache enableOutputCache="false" enableFragmentCache="false"/> </caching> </system.web>

  • También en el web.config (y muchas variaciones en las políticas, incluyendo ninguna).

    <caching enabled="false" enableKernelCache="false"> <profiles> <add policy="DontCache" kernelCachePolicy="DontCache" extension="*/> </profiles> </caching>

  • Revisó el código fuente de los marcos para ver si podría haber algunas "características" integradas que usarían el almacenamiento en caché de ASP.NET. Si bien hay ayudantes de almacenamiento en caché, son privados del marco en sí y no parecen aprovechar el almacenamiento en caché de ASP.NET.

Actualización # 1

Examinando a través del reflector, descubrí que establecer el valor de UrlMetadataSlidingExpiration en cero elimina una gran parte del uso excesivo de la memoria, a expensas de reducir el rendimiento en un 50% (la clase FileAuthorizationModule almacena en caché los descriptores de seguridad de archivos, que deben ser algo caros de generar. cuando UrlMetadataSlidingExpiration no es cero).

Esto se hace actualizando el web.config y colocando lo siguiente en:

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

Voy a intentar deshabilitar por completo la ejecución del FileAuthorizationModule, si es posible, para ver si eso ayuda. Sin embargo, ASP.NET sigue generando objetos MapPathCacheInfo y CacheEntry 2 * N, por lo que la memoria aún se consume, solo a una velocidad mucho menor.

Actualización # 2

La otra mitad del problema es el mismo problema que se describe aquí: Evite que muchas URL de MVC diferentes llenen el caché de ASP.NET . Ajuste

<cache percentagePhysicalMemoryUsedLimit="1" privateBytesPollTime="00:00:01"/> ayuda, pero incluso con estos ajustes muy agresivos, el uso de la memoria aumenta rápidamente a 2.5GB (en comparación con 4GB). Idealmente, estos objetos nunca serían creados en primer lugar. En su defecto, puedo recurrir a una solución intrépida de usar la reflexión para borrar los cachés (todas estas entradas son "privadas" y no se enumeran cuando se repite en el caché público).


Asegúrese de que la propiedad IsReusable esté establecida en false para que IIS no reutilice el mismo proceso de solicitud para manejar la solicitud.

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


Creo que esto es menos un problema de almacenamiento en caché, y más de un problema de "alta utilización de memoria".

Dos cosas,

Si usa un objeto amigable IDisponible (pruebe uno que pueda usar la palabra clave "utilizando"). Esto le permitirá deshacerse del objeto más pronto que tarde, creando menos presión para el recolector de basura a largo plazo.

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

A partir de su pseudocódigo, solo puedo asumir que está usando System.Net.WebHttpRequest, que no es desechable y probablemente se quedará más tiempo del que debería si está realizando llamadas sucesivas.

En segundo lugar, si está realizando llamadas sucesivas a un servidor externo, pondría demoras entre cada llamada. Esto le dará algo de espacio para respirar, ya que su procesador procesará el bucle for mucho más rápido que la red tendrá tiempo para responder (y solo mantendrá en cola las solicitudes y reducirá la velocidad de las que realmente se están procesando).

System.Threading.Thread.Sleep(250);

Obviamente, la mejor solución sería hacer una sola llamada con una lista de los usuarios que desea recuperar y solo tratar con una pregunta / respuesta web.


Una respuesta tardía para otros que sufre el mismo problema:

Este es un problema conocido: KB 2504047

Este problema se produce porque las solicitudes únicas que intentan acceder a los mismos recursos se almacenan en caché como objetos MapPathCacheInfo durante 10 minutos.

Mientras que los objetos se almacenan en caché durante 10 minutos, el consumo de memoria del proceso W3wp.exe aumenta significativamente.

Puedes descargar el hotfix here